Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions acceptance/experimental/air/get/output.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

=== get (text)
>>> [CLI] experimental air get run 123
>>> [CLI] experimental air get 123

╭─ Configuration ────────────────────────────────────────────────╮
│ │
Expand Down Expand Up @@ -31,11 +31,9 @@
│ │
╰────────────────────────────────────────────────────────────────╯

Run URL: [DATABRICKS_URL]/jobs/runs/123?o=[NUMID]
MLflow URL: [DATABRICKS_URL]/ml/experiments/exp1/runs/run1

=== get (json)
>>> [CLI] experimental air get run 123 -o json
>>> [CLI] experimental air get 123 -o json
{
"v": 1,
"ts": "[TIMESTAMP]",
Expand All @@ -52,13 +50,13 @@ MLflow URL: [DATABRICKS_URL]/ml/experiments/exp1/runs/run1
}

=== invalid run id
>>> [CLI] experimental air get run notanumber
>>> [CLI] experimental air get notanumber
Error: invalid JOB_RUN_ID "notanumber": must be a positive integer

Exit code: 1

=== invalid run id (json)
>>> [CLI] experimental air get run notanumber -o json
>>> [CLI] experimental air get notanumber -o json
{
"v": 1,
"ts": "[TIMESTAMP]",
Expand Down
8 changes: 4 additions & 4 deletions acceptance/experimental/air/get/script
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
title "get (text)"
trace $CLI experimental air get run 123
trace $CLI experimental air get 123

title "get (json)"
trace $CLI experimental air get run 123 -o json
trace $CLI experimental air get 123 -o json

title "invalid run id"
errcode trace $CLI experimental air get run notanumber
errcode trace $CLI experimental air get notanumber

title "invalid run id (json)"
errcode trace $CLI experimental air get run notanumber -o json
errcode trace $CLI experimental air get notanumber -o json
3 changes: 2 additions & 1 deletion acceptance/experimental/air/help/output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Usage:

Available Commands:
cancel Cancel one or more runs
get Show details for a specific resource
get Show status, configuration, and timing details for a specific run
list List your recent runs (active and completed) for the current profile

logs Stream or fetch logs for a run
register-image Mirror a Docker image into the workspace registry
run Submit a training workload from a YAML config
Expand Down
19 changes: 5 additions & 14 deletions experimental/air/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/spf13/cobra"
)

// getData is the payload printed by `air get run`. The json-tagged fields form
// getData is the payload printed by `air get`. The json-tagged fields form
// the machine-readable output; fields tagged `json:"-"` are shown only in the
// human-readable text view.
type getData struct {
Expand All @@ -27,7 +27,7 @@ type getData struct {
MLflowURL *string `json:"mlflow_url"`

// The fields below are pre-rendered text-view cells, excluded from JSON
// (matching `air get run --json`). Each shows "N/A" when its value is
// (matching `air get --json`). Each shows "N/A" when its value is
// missing. The styled single-run renderer (render.go) consumes them; the
// Run ID, Status, and MLflow Run cells it draws are styled and hyperlinked
// there rather than stored here.
Expand Down Expand Up @@ -63,20 +63,11 @@ Sweep Tasks:
{{- end}}
`

// newGetCommand is the `get` parent group. Subcommands name the resource to
// describe, e.g. `air get run JOB_RUN_ID`, mirroring the Python CLI.
// newGetCommand returns the `air get JOB_RUN_ID` command, which shows status,
// configuration, and timing details for a specific run.
func newGetCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: "Show details for a specific resource",
}
cmd.AddCommand(newGetRunCommand())
return cmd
}

func newGetRunCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "run JOB_RUN_ID",
Use: "get JOB_RUN_ID",
Args: root.ExactArgs(1),
Short: "Show status, configuration, and timing details for a specific run",
Annotations: map[string]string{
Expand Down
19 changes: 16 additions & 3 deletions experimental/air/cmd/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,23 @@ func renderGet(t *testing.T, data getData) string {
return buf.String()
}

// TestGetCommandShape locks in that `get` takes the run id directly as
// `air get JOB_RUN_ID` and has no `run` subcommand (it was collapsed back into
// `get`). The acceptance test exercises the happy path end to end.
func TestGetCommandShape(t *testing.T) {
cmd := newGetCommand()
assert.Equal(t, "get JOB_RUN_ID", cmd.Use)
assert.Empty(t, cmd.Commands(), "get must not register subcommands")
// ExactArgs(1): exactly one run id is required.
assert.NoError(t, cmd.Args(cmd, []string{"123"}))
assert.Error(t, cmd.Args(cmd, []string{}))
assert.Error(t, cmd.Args(cmd, []string{"1", "2"}))
}

func TestGetRunInvalidID(t *testing.T) {
m := mocks.NewMockWorkspaceClient(t)
ctx := cmdctx.SetWorkspaceClient(cmdio.MockDiscard(t.Context()), m.WorkspaceClient)
cmd := withOutput(newGetRunCommand(), flags.OutputText)
cmd := withOutput(newGetCommand(), flags.OutputText)
cmd.SetContext(ctx)

err := cmd.RunE(cmd, []string{"abc"})
Expand All @@ -45,7 +58,7 @@ func TestGetRunNotFound(t *testing.T) {
m.GetMockJobsAPI().EXPECT().GetRun(mock.Anything, jobs.GetRunRequest{RunId: 5}).Return(
nil, apierr.ErrResourceDoesNotExist)
ctx := cmdctx.SetWorkspaceClient(cmdio.MockDiscard(t.Context()), m.WorkspaceClient)
cmd := withOutput(newGetRunCommand(), flags.OutputText)
cmd := withOutput(newGetCommand(), flags.OutputText)
cmd.SetContext(ctx)

err := cmd.RunE(cmd, []string{"5"})
Expand All @@ -60,7 +73,7 @@ func TestGetRunNotFoundJSON(t *testing.T) {
nil, apierr.ErrResourceDoesNotExist)
ctx := cmdctx.SetWorkspaceClient(t.Context(), m.WorkspaceClient)
ctx = cmdio.InContext(ctx, cmdio.NewIO(ctx, flags.OutputJSON, nil, &buf, &buf, "", ""))
cmd := withOutput(newGetRunCommand(), flags.OutputJSON)
cmd := withOutput(newGetCommand(), flags.OutputJSON)
cmd.SetContext(ctx)

// In JSON mode the not-found error is a structured envelope, not a bare error.
Expand Down
13 changes: 0 additions & 13 deletions experimental/air/cmd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,6 @@ func renderRunText(ctx context.Context, out io.Writer, w *databricks.WorkspaceCl
// A single write: a blank line before the first box and after the last, and
// one between each box.
fmt.Fprintf(out, "\n%s\n\n", strings.Join(sections, "\n\n"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change intentional?


// Bare-URL footer so the job run / MLflow links remain reachable when
// stdout is not a hyperlink-capable terminal (piped, redirected, NO_COLOR).
// In that case the OSC 8 hyperlinks on the Run ID / MLflow Run cells
// degrade to plain labels and the URLs would otherwise disappear from text
// output, breaking workflows like `air get run X > out.txt` or
// `NO_COLOR=1 air get run X` that the previous `Job Link:` line supported.
if view.dashboardURL != "" {
fmt.Fprintf(out, "Run URL: %s\n", view.dashboardURL)
}
if view.mlflowURL != "" {
fmt.Fprintf(out, "MLflow URL: %s\n", view.mlflowURL)
}
}

// genAIComputeTask returns the run's first GenAI-compute task, or nil.
Expand Down
Loading