From 6d3b4159bd5911c22b33c65ae245f63e06462d85 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Fri, 10 Jan 2025 09:51:59 +0100 Subject: [PATCH] Log warnings to stderr for "bundle validate -o json" (#2109) ## Changes Previously diagnostics were not seen in JSON output mode. This change prints them to stderr. This also fixes acceptance tests to preprocess all output with s/execPath/$CLI/ not just output.txt. ## Tests Existing acceptance tests. In one case I've added non-json command to check that they match in output. --- acceptance/acceptance_test.go | 13 +++++++---- .../job_tasks/out.development.stderr.txt | 6 +++++ .../bundle/override/job_tasks/output.txt | 19 +++++++++++---- acceptance/bundle/override/job_tasks/script | 3 ++- .../override/merge-string-map/output.txt | 4 ++++ cmd/bundle/validate.go | 23 +++++++++++++++---- 6 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 acceptance/bundle/override/job_tasks/out.development.stderr.txt diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 033f26dfb3..b9fb219dcb 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -55,12 +55,15 @@ func TestAccept(t *testing.T) { // Do not read user's ~/.databrickscfg t.Setenv(env.HomeEnvVar(), homeDir) + repls := testdiff.ReplacementsContext{} + repls.Set(execPath, "$CLI") + testDirs := getTests(t) require.NotEmpty(t, testDirs) for _, dir := range testDirs { t.Run(dir, func(t *testing.T) { t.Parallel() - runTest(t, dir) + runTest(t, dir, repls) }) } } @@ -85,7 +88,7 @@ func getTests(t *testing.T) []string { return testDirs } -func runTest(t *testing.T, dir string) { +func runTest(t *testing.T, dir string, repls testdiff.ReplacementsContext) { var tmpDir string var err error if KeepTmp { @@ -112,7 +115,7 @@ func runTest(t *testing.T, dir string) { outB, err := cmd.CombinedOutput() out := formatOutput(string(outB), err) - out = strings.ReplaceAll(out, os.Getenv("CLI"), "$CLI") + out = repls.Replace(out) doComparison(t, filepath.Join(dir, "output.txt"), "script output", out) for key := range outputs { @@ -131,7 +134,8 @@ func runTest(t *testing.T, dir string) { continue } pathExpected := filepath.Join(dir, key) - doComparison(t, pathExpected, pathNew, string(newValBytes)) + newVal := repls.Replace(string(newValBytes)) + doComparison(t, pathExpected, pathNew, newVal) } // Make sure there are not unaccounted for new files @@ -152,6 +156,7 @@ func runTest(t *testing.T, dir string) { // Show the contents & support overwrite mode for it: pathNew := filepath.Join(tmpDir, name) newVal := testutil.ReadFile(t, pathNew) + newVal = repls.Replace(newVal) doComparison(t, filepath.Join(dir, name), filepath.Join(tmpDir, name), newVal) } } diff --git a/acceptance/bundle/override/job_tasks/out.development.stderr.txt b/acceptance/bundle/override/job_tasks/out.development.stderr.txt new file mode 100644 index 0000000000..7b6fef0cc1 --- /dev/null +++ b/acceptance/bundle/override/job_tasks/out.development.stderr.txt @@ -0,0 +1,6 @@ + +>>> errcode $CLI bundle validate -o json -t development +Error: file ./test1.py not found + + +Exit code: 1 diff --git a/acceptance/bundle/override/job_tasks/output.txt b/acceptance/bundle/override/job_tasks/output.txt index 0d561291ed..0bb0b18125 100644 --- a/acceptance/bundle/override/job_tasks/output.txt +++ b/acceptance/bundle/override/job_tasks/output.txt @@ -1,8 +1,3 @@ - ->>> errcode $CLI bundle validate -o json -t development -Error: file ./test1.py not found - -Exit code: 1 { "name": "job", "queue": { @@ -36,6 +31,7 @@ Exit code: 1 >>> errcode $CLI bundle validate -o json -t staging Error: file ./test1.py not found + Exit code: 1 { "name": "job", @@ -66,3 +62,16 @@ Exit code: 1 } ] } + +>>> errcode $CLI bundle validate -t staging +Error: file ./test1.py not found + +Name: override_job_tasks +Target: staging +Workspace: + User: tester@databricks.com + Path: /Workspace/Users/tester@databricks.com/.bundle/override_job_tasks/staging + +Found 1 error + +Exit code: 1 diff --git a/acceptance/bundle/override/job_tasks/script b/acceptance/bundle/override/job_tasks/script index 4e08698575..f41729c1e5 100644 --- a/acceptance/bundle/override/job_tasks/script +++ b/acceptance/bundle/override/job_tasks/script @@ -1,2 +1,3 @@ -trace errcode $CLI bundle validate -o json -t development | jq .resources.jobs.foo +trace errcode $CLI bundle validate -o json -t development 2> out.development.stderr.txt | jq .resources.jobs.foo trace errcode $CLI bundle validate -o json -t staging | jq .resources.jobs.foo +trace errcode $CLI bundle validate -t staging diff --git a/acceptance/bundle/override/merge-string-map/output.txt b/acceptance/bundle/override/merge-string-map/output.txt index e1bd7dfb4f..986da81748 100644 --- a/acceptance/bundle/override/merge-string-map/output.txt +++ b/acceptance/bundle/override/merge-string-map/output.txt @@ -1,5 +1,9 @@ >>> $CLI bundle validate -o json -t dev +Warning: expected map, found string + at resources.clusters.my_cluster + in databricks.yml:6:17 + { "clusters": { "my_cluster": { diff --git a/cmd/bundle/validate.go b/cmd/bundle/validate.go index daeb7426d3..41fa87f30d 100644 --- a/cmd/bundle/validate.go +++ b/cmd/bundle/validate.go @@ -11,18 +11,17 @@ import ( "github.com/databricks/cli/bundle/render" "github.com/databricks/cli/cmd/bundle/utils" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/flags" "github.com/spf13/cobra" ) -func renderJsonOutput(cmd *cobra.Command, b *bundle.Bundle, diags diag.Diagnostics) error { +func renderJsonOutput(cmd *cobra.Command, b *bundle.Bundle) error { buf, err := json.MarshalIndent(b.Config.Value().AsAny(), "", " ") if err != nil { return err } _, _ = cmd.OutOrStdout().Write(buf) - return diags.Error() + return nil } func newValidateCommand() *cobra.Command { @@ -66,7 +65,23 @@ func newValidateCommand() *cobra.Command { return nil case flags.OutputJSON: - return renderJsonOutput(cmd, b, diags) + renderOpts := render.RenderOptions{RenderSummaryTable: false} + err1 := render.RenderDiagnostics(cmd.ErrOrStderr(), b, diags, renderOpts) + err2 := renderJsonOutput(cmd, b) + + if err2 != nil { + return err2 + } + + if err1 != nil { + return err1 + } + + if diags.HasError() { + return root.ErrAlreadyPrinted + } + + return nil default: return fmt.Errorf("unknown output type %s", root.OutputType(cmd)) }