From 4bc6a143cc557413e5487fe204bbba4dd34e1965 Mon Sep 17 00:00:00 2001 From: riddhibhagwat-db Date: Wed, 12 Aug 2026 17:54:29 +0000 Subject: [PATCH] AIR CLI: drop requirements.yaml file path for air run dependencies environment.dependencies now accepts only an inline list of packages. The string form (a path to a requirements.yaml file) is rejected at config load, now that inline deps are fully supported. Removes the file-reading paths (readRequirementsDependencies, requirementsDoc, requirementsFile) and the file-vs-inline branching in run submit; the dependencies union collapses to a plain list. Co-authored-by: Isaac --- .../air/run-submit-deps/.gitattributes | 7 +- .../air/run-submit-deps/output.txt | 57 +------------- .../air/run-submit-deps/reqs.yaml | 4 - .../experimental/air/run-submit-deps/script | 10 +-- .../air/run-submit-deps/test.toml | 4 - experimental/air/cmd/runconfig.go | 37 ++++----- experimental/air/cmd/runconfig_launch.go | 18 +---- experimental/air/cmd/runconfig_launch_test.go | 19 +---- experimental/air/cmd/runconfig_test.go | 29 +++---- experimental/air/cmd/runsubmit.go | 35 +-------- experimental/air/cmd/runsubmit_test.go | 78 ------------------- experimental/air/cmd/runupload.go | 28 ------- experimental/air/cmd/runupload_test.go | 17 +--- 13 files changed, 38 insertions(+), 305 deletions(-) delete mode 100644 acceptance/experimental/air/run-submit-deps/reqs.yaml diff --git a/acceptance/experimental/air/run-submit-deps/.gitattributes b/acceptance/experimental/air/run-submit-deps/.gitattributes index 0d9562f4bfe..3c92a7ca7ab 100644 --- a/acceptance/experimental/air/run-submit-deps/.gitattributes +++ b/acceptance/experimental/air/run-submit-deps/.gitattributes @@ -1,6 +1,3 @@ -# These YAML files' contents are uploaded verbatim (as training_config.yaml and -# requirements.yaml), so their line endings must stay \n on every OS — a Windows -# \r would change the recorded payload. +# run.yaml's contents are uploaded verbatim (as training_config.yaml), so its line +# endings must stay \n on every OS — a Windows \r would change the recorded payload. run.yaml text eol=lf -run-file.yaml text eol=lf -reqs.yaml text eol=lf diff --git a/acceptance/experimental/air/run-submit-deps/output.txt b/acceptance/experimental/air/run-submit-deps/output.txt index 7a0d971b9f5..3c7280bc5ba 100644 --- a/acceptance/experimental/air/run-submit-deps/output.txt +++ b/acceptance/experimental/air/run-submit-deps/output.txt @@ -56,59 +56,6 @@ Tip: use --watch to stream logs until the run completes. } } -=== file-form deps: version comes from the requirements file +=== a requirements.yaml file path is rejected; deps must be inline >>> [CLI] experimental air run -f run-file.yaml -Submitting experiment: deps-file-smoke -Submitted workload with Job Run ID: 555 -View job run at: [DATABRICKS_URL]/jobs/runs/555 - -Tip: use --watch to stream logs until the run completes. - -=== file-form deps: the requirements file is not uploaded either ->>> print_requests.py //api/2.0/workspace-files/import-file --oneline --sort --unique --keep -{"method": "POST", "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.air/cli_launch/deps-file-smoke/deps-file-smoke_[RUN_ID]/command.sh", "q": {"overwrite": "true"}, "raw_body": "python train.py"} -{"method": "POST", "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.air/cli_launch/deps-file-smoke/deps-file-smoke_[RUN_ID]/training_config.yaml", "q": {"overwrite": "true"}, "raw_body": "experiment_name: deps-file-smoke\ncommand: python train.py\ncompute:\n accelerator_type: GPU_1xH100\n num_accelerators: 1\nenvironment:\n dependencies: ./reqs.yaml\n"} - -=== file-form deps ride on environments[].spec.dependencies ->>> print_requests.py //api/2.2/jobs/runs/submit -{ - "method": "POST", - "path": "/api/2.2/jobs/runs/submit", - "body": { - "environments": [ - { - "environment_key": "default", - "spec": { - "dependencies": [ - "numpy", - "torch==2.3.0" - ], - "environment_version": "5" - } - } - ], - "idempotency_token": "[UUID]", - "run_name": "deps-file-smoke", - "tasks": [ - { - "ai_runtime_task": { - "deployments": [ - { - "command_path": "/Workspace/Users/[USERNAME]/.air/cli_launch/deps-file-smoke/deps-file-smoke_[RUN_ID]/command.sh", - "compute": { - "accelerator_count": 1, - "accelerator_type": "GPU_1xH100" - } - } - ], - "experiment": "deps-file-smoke" - }, - "environment_key": "default", - "max_retries": 3, - "retry_on_timeout": true, - "run_if": "ALL_SUCCESS", - "task_key": "deps-file-smoke" - } - ] - } -} +Error: invalid config run-file.yaml: environment.dependencies must be a list of packages or reference a requirements.txt (see https://docs.databricks.com/aws/en/machine-learning/ai-runtime/cli/yaml-config#reference). A direct file reference is not supported diff --git a/acceptance/experimental/air/run-submit-deps/reqs.yaml b/acceptance/experimental/air/run-submit-deps/reqs.yaml deleted file mode 100644 index da4b89534c2..00000000000 --- a/acceptance/experimental/air/run-submit-deps/reqs.yaml +++ /dev/null @@ -1,4 +0,0 @@ -version: 5 -dependencies: - - numpy - - torch==2.3.0 diff --git a/acceptance/experimental/air/run-submit-deps/script b/acceptance/experimental/air/run-submit-deps/script index 52035bbae65..12dc884740d 100644 --- a/acceptance/experimental/air/run-submit-deps/script +++ b/acceptance/experimental/air/run-submit-deps/script @@ -7,11 +7,5 @@ trace print_requests.py //api/2.0/workspace-files/import-file --oneline --sort - title "declared deps ride on environments[].spec.dependencies" trace print_requests.py //api/2.2/jobs/runs/submit -title "file-form deps: version comes from the requirements file" -trace $CLI experimental air run -f run-file.yaml - -title "file-form deps: the requirements file is not uploaded either" -trace print_requests.py //api/2.0/workspace-files/import-file --oneline --sort --unique --keep - -title "file-form deps ride on environments[].spec.dependencies" -trace print_requests.py //api/2.2/jobs/runs/submit +title "a requirements.yaml file path is rejected; deps must be inline" +musterr trace $CLI experimental air run -f run-file.yaml diff --git a/acceptance/experimental/air/run-submit-deps/test.toml b/acceptance/experimental/air/run-submit-deps/test.toml index 590c2918a25..7ebac7a298a 100644 --- a/acceptance/experimental/air/run-submit-deps/test.toml +++ b/acceptance/experimental/air/run-submit-deps/test.toml @@ -17,7 +17,3 @@ Response.Body = ''' [[Repls]] Old = 'deps-smoke_[0-9a-f]{16}' New = 'deps-smoke_[RUN_ID]' - -[[Repls]] -Old = 'deps-file-smoke_[0-9a-f]{16}' -New = 'deps-file-smoke_[RUN_ID]' diff --git a/experimental/air/cmd/runconfig.go b/experimental/air/cmd/runconfig.go index 5a1d082abb3..10295275d25 100644 --- a/experimental/air/cmd/runconfig.go +++ b/experimental/air/cmd/runconfig.go @@ -251,40 +251,29 @@ func (e *environmentConfig) validate() error { return e.DockerImage.validate() } - // version pins the client image version, which is only meaningful for an - // inline (list) dependency set — a requirements.yaml file carries its own. - if e.Version.set { - if e.Dependencies.set && !e.Dependencies.isList { - return errors.New("'environment.version' is only valid with inline dependencies (a list); when 'dependencies' points to a requirements.yaml file, set the version inside that file") - } - if !e.Dependencies.set { - return errors.New("'environment.version' requires inline 'dependencies' (a list of packages)") - } + // version pins the client image version, which is only meaningful alongside an + // inline dependency set. + if e.Version.set && !e.Dependencies.set { + return errors.New("'environment.version' requires inline 'dependencies' (a list of packages)") } return nil } -// dependencies is environment.dependencies, which is polymorphic: a string is a -// path to a requirements.yaml file; a list is an inline package list. +// dependencies is environment.dependencies: an inline list of packages. A scalar +// (e.g. a path to a requirements file) is rejected — the list may itself reference +// a requirements.txt, but dependencies must be given as a list. type dependencies struct { - set bool - isList bool - path string - list []string + set bool + list []string } func (d *dependencies) UnmarshalYAML(node *yaml.Node) error { - switch node.Kind { - case yaml.ScalarNode: - d.set, d.isList = true, false - return node.Decode(&d.path) - case yaml.SequenceNode: - d.set, d.isList = true, true - return node.Decode(&d.list) - default: - return errors.New("environment.dependencies must be a string path or a list of packages") + if node.Kind != yaml.SequenceNode { + return errors.New("environment.dependencies must be a list of packages or reference a requirements.txt (see https://docs.databricks.com/aws/en/machine-learning/ai-runtime/cli/yaml-config#reference). A direct file reference is not supported") } + d.set = true + return node.Decode(&d.list) } // stringOrInt holds a scalar that may be a string or an integer in YAML diff --git a/experimental/air/cmd/runconfig_launch.go b/experimental/air/cmd/runconfig_launch.go index 1408b600736..75de05ec1be 100644 --- a/experimental/air/cmd/runconfig_launch.go +++ b/experimental/air/cmd/runconfig_launch.go @@ -36,27 +36,17 @@ func (c *runConfig) dockerImageURL() string { return "" } -// requirementsFile returns the path to a requirements file when -// environment.dependencies is a string, and whether it was set. -func (c *runConfig) requirementsFile() (string, bool) { - if c.Environment == nil || !c.Environment.Dependencies.set || c.Environment.Dependencies.isList { - return "", false - } - return c.Environment.Dependencies.path, true -} - -// inlineDependencies returns the inline package list when -// environment.dependencies is a list, and whether it was set. +// inlineDependencies returns the inline package list from +// environment.dependencies, and whether it was set. func (c *runConfig) inlineDependencies() ([]string, bool) { - if c.Environment == nil || !c.Environment.Dependencies.set || !c.Environment.Dependencies.isList { + if c.Environment == nil || !c.Environment.Dependencies.set { return nil, false } return c.Environment.Dependencies.list, true } // runtimeVersion returns the client image version from environment.version when -// set. For a requirements-file dependency set, the version lives in that file and -// is resolved at launch, not here. +// set. func (c *runConfig) runtimeVersion() (string, bool) { if c.Environment == nil || !c.Environment.Version.set { return "", false diff --git a/experimental/air/cmd/runconfig_launch_test.go b/experimental/air/cmd/runconfig_launch_test.go index 289db91c7de..4b250a83fc2 100644 --- a/experimental/air/cmd/runconfig_launch_test.go +++ b/experimental/air/cmd/runconfig_launch_test.go @@ -39,32 +39,17 @@ func TestRunConfigDockerImageURL(t *testing.T) { func TestRunConfigDependencies(t *testing.T) { t.Run("unset", func(t *testing.T) { c := &runConfig{} - _, ok := c.requirementsFile() - assert.False(t, ok) - _, ok = c.inlineDependencies() - assert.False(t, ok) - }) - - t.Run("file path", func(t *testing.T) { - c := &runConfig{Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: false, path: "req.yaml"}, - }} - path, ok := c.requirementsFile() - assert.True(t, ok) - assert.Equal(t, "req.yaml", path) - _, ok = c.inlineDependencies() + _, ok := c.inlineDependencies() assert.False(t, ok) }) t.Run("inline list", func(t *testing.T) { c := &runConfig{Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: true, list: []string{"torch", "numpy"}}, + Dependencies: dependencies{set: true, list: []string{"torch", "numpy"}}, }} list, ok := c.inlineDependencies() assert.True(t, ok) assert.Equal(t, []string{"torch", "numpy"}, list) - _, ok = c.requirementsFile() - assert.False(t, ok) }) } diff --git a/experimental/air/cmd/runconfig_test.go b/experimental/air/cmd/runconfig_test.go index fce77aacf03..f99bb344e79 100644 --- a/experimental/air/cmd/runconfig_test.go +++ b/experimental/air/cmd/runconfig_test.go @@ -82,7 +82,7 @@ permissions: require.NoError(t, err) assert.Equal(t, gpuType8xH100, gpuType(cfg.Compute.AcceleratorType)) require.NotNil(t, cfg.Environment) - assert.True(t, cfg.Environment.Dependencies.isList) + assert.True(t, cfg.Environment.Dependencies.set) assert.Equal(t, []string{"torch==2.3.0", "numpy"}, cfg.Environment.Dependencies.list) assert.True(t, cfg.Environment.Version.set) assert.Equal(t, "5", cfg.Environment.Version.raw) @@ -94,18 +94,17 @@ permissions: assert.Len(t, cfg.Permissions, 2) } -// TestLoadRunConfig_PolymorphicFields exercises the str|list, str|int, and -// bool|str unions decoded by custom UnmarshalYAML. +// TestLoadRunConfig_PolymorphicFields exercises the str|int and bool|str unions +// decoded by custom UnmarshalYAML, plus the rejection of the removed +// dependencies string form. func TestLoadRunConfig_PolymorphicFields(t *testing.T) { - t.Run("dependencies as string path", func(t *testing.T) { - cfg, err := loadRunConfig(writeConfig(t, minimalConfig+` + t.Run("dependencies as string path is rejected", func(t *testing.T) { + _, err := loadRunConfig(writeConfig(t, minimalConfig+` environment: dependencies: requirements.yaml `)) - require.NoError(t, err) - assert.True(t, cfg.Environment.Dependencies.set) - assert.False(t, cfg.Environment.Dependencies.isList) - assert.Equal(t, "requirements.yaml", cfg.Environment.Dependencies.path) + require.Error(t, err) + assert.Contains(t, err.Error(), "must be a list of packages") }) t.Run("git remote as bool true is rejected", func(t *testing.T) { @@ -278,7 +277,7 @@ func TestEnvironmentConfigValidate(t *testing.T) { "docker image with deps conflicts", environmentConfig{ DockerImage: &dockerImageConfig{URL: "org/repo:tag"}, - Dependencies: dependencies{set: true, isList: true, list: []string{"torch"}}, + Dependencies: dependencies{set: true, list: []string{"torch"}}, }, "not allowed: dependencies", }, @@ -287,14 +286,6 @@ func TestEnvironmentConfigValidate(t *testing.T) { environmentConfig{DockerImage: &dockerImageConfig{URL: " "}}, "docker_image.url cannot be empty", }, - { - "version with file deps", - environmentConfig{ - Version: stringOrInt{set: true, raw: "5"}, - Dependencies: dependencies{set: true, isList: false, path: "req.yaml"}, - }, - "only valid with inline dependencies", - }, { "version without deps", environmentConfig{Version: stringOrInt{set: true, raw: "5"}}, @@ -304,7 +295,7 @@ func TestEnvironmentConfigValidate(t *testing.T) { "version with inline deps ok", environmentConfig{ Version: stringOrInt{set: true, raw: "5"}, - Dependencies: dependencies{set: true, isList: true, list: []string{"torch"}}, + Dependencies: dependencies{set: true, list: []string{"torch"}}, }, "", }, diff --git a/experimental/air/cmd/runsubmit.go b/experimental/air/cmd/runsubmit.go index 9518a0e1a2e..d68ae6753ba 100644 --- a/experimental/air/cmd/runsubmit.go +++ b/experimental/air/cmd/runsubmit.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "path" - "path/filepath" "strconv" "strings" @@ -39,26 +38,6 @@ func dlRuntimeImage(ctx context.Context, runtimeVersion string) string { return strings.TrimPrefix(img, "CLIENT-GPU-") } -// environmentDependencies resolves the user's declared dependencies as a flat -// list to carry inline on the serverless environment's spec.dependencies: the -// inline list directly, or the dependencies read from a requirements file -// (resolved against the config's directory). For file-form deps it also returns -// the version declared inside that file, which selects the runtime image since -// top-level environment.version is not allowed there. Returns nil when none are -// declared. -func environmentDependencies(cfg *runConfig, configPath string) (deps []string, fileVersion string, err error) { - if deps, ok := cfg.inlineDependencies(); ok { - return deps, "", nil - } - if reqPath, ok := cfg.requirementsFile(); ok { - if !filepath.IsAbs(reqPath) { - reqPath = filepath.Join(filepath.Dir(configPath), reqPath) - } - return readRequirementsDependencies(reqPath) - } - return nil, "", nil -} - // buildSubmitPayload assembles the runs/submit payload. commandPath is the // workspace path of the uploaded command.sh; dlImage is the runtime channel; // usagePolicyID is the already-resolved policy id ("" when the run has none); @@ -182,12 +161,7 @@ func submitWorkload(ctx context.Context, w *databricks.WorkspaceClient, cfg *run } } - // Resolve dependencies before any upload too, so a bad requirements file fails - // fast without leaving orphaned artifacts in the workspace. - deps, fileVersion, err := environmentDependencies(cfg, configPath) - if err != nil { - return 0, "", err - } + deps, _ := cfg.inlineDependencies() experimentDir := "" if cfg.MLflowExperimentDirectory != nil { @@ -237,12 +211,7 @@ func submitWorkload(ctx context.Context, w *databricks.WorkspaceClient, cfg *run } } - // Top-level environment.version wins; for file-form deps it is disallowed, so - // fall back to the version declared inside the requirements file. - runtimeVersion, ok := cfg.runtimeVersion() - if !ok { - runtimeVersion = fileVersion - } + runtimeVersion, _ := cfg.runtimeVersion() payload := buildSubmitPayload(cfg, path.Join(funcDir, commandScriptName), dlRuntimeImage(ctx, runtimeVersion), usagePolicyID, snap, deps) payload.IdempotencyToken = token diff --git a/experimental/air/cmd/runsubmit_test.go b/experimental/air/cmd/runsubmit_test.go index 559c3e8d188..9677fc0cf53 100644 --- a/experimental/air/cmd/runsubmit_test.go +++ b/experimental/air/cmd/runsubmit_test.go @@ -3,7 +3,6 @@ package aircmd import ( "encoding/json" "io" - "os" "path" "path/filepath" "strings" @@ -128,64 +127,6 @@ func TestBuildSubmitPayloadInlineDependencies(t *testing.T) { } } -// TestEnvironmentDependencies covers how declared deps are resolved to a flat list: -// an inline list (no file version), a requirements file (path resolved against the -// config dir, version read from the file), none, and a missing file. -func TestEnvironmentDependencies(t *testing.T) { - inline := &runConfig{Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: true, list: []string{"torch", "numpy"}}, - }} - deps, version, err := environmentDependencies(inline, "run.yaml") - require.NoError(t, err) - assert.Equal(t, []string{"torch", "numpy"}, deps) - assert.Empty(t, version) - - dir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(dir, "reqs.yaml"), []byte("version: \"5\"\ndependencies:\n - pandas\n"), 0o600)) - fromFile := &runConfig{Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: false, path: "reqs.yaml"}, - }} - deps, version, err = environmentDependencies(fromFile, filepath.Join(dir, "run.yaml")) - require.NoError(t, err) - assert.Equal(t, []string{"pandas"}, deps) - assert.Equal(t, "5", version) - - deps, _, err = environmentDependencies(&runConfig{}, "run.yaml") - require.NoError(t, err) - assert.Nil(t, deps) - - missing := &runConfig{Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: false, path: "nope.yaml"}, - }} - _, _, err = environmentDependencies(missing, filepath.Join(dir, "run.yaml")) - require.ErrorContains(t, err, "failed to read requirements file") -} - -// TestReadRequirementsDependencies covers reading a requirements file's dependency -// list and version, with a missing key yielding an empty list and a -r include -// rejected. -func TestReadRequirementsDependencies(t *testing.T) { - dir := t.TempDir() - - reqPath := filepath.Join(dir, "requirements.yaml") - require.NoError(t, os.WriteFile(reqPath, []byte("version: \"5\"\ndependencies:\n - torch==2.3.0\n - numpy\n"), 0o600)) - deps, version, err := readRequirementsDependencies(reqPath) - require.NoError(t, err) - assert.Equal(t, []string{"torch==2.3.0", "numpy"}, deps) - assert.Equal(t, "5", version) - - emptyPath := filepath.Join(dir, "empty.yaml") - require.NoError(t, os.WriteFile(emptyPath, []byte("version: \"5\"\n"), 0o600)) - deps, _, err = readRequirementsDependencies(emptyPath) - require.NoError(t, err) - assert.Empty(t, deps) - - includePath := filepath.Join(dir, "include.yaml") - require.NoError(t, os.WriteFile(includePath, []byte("dependencies:\n - -r other.txt\n"), 0o600)) - _, _, err = readRequirementsDependencies(includePath) - require.ErrorContains(t, err, "requirements-file include") -} - func TestSubmitToken(t *testing.T) { cfg := &runConfig{IdempotencyToken: new("from-config")} @@ -590,25 +531,6 @@ func TestSubmitWorkloadGuards(t *testing.T) { assert.NotContains(t, p, "/workspace/", "no workspace write may precede policy resolution") } }) - - t.Run("bad requirements file fails before any upload", func(t *testing.T) { - server := testserver.New(t) - t.Cleanup(server.Close) - var uploaded bool - server.Handle("POST", "/api/2.0/workspace-files/import-file/{path...}", func(testserver.Request) any { - uploaded = true - return nil - }) - testserver.AddDefaultHandlers(server) - tw, err := databricks.NewWorkspaceClient(&databricks.Config{Host: server.URL, Token: "token"}) - require.NoError(t, err) - - cfg := *base - cfg.Environment = &environmentConfig{Dependencies: dependencies{set: true, isList: false, path: "missing.yaml"}} - _, _, err = submitWorkload(t.Context(), tw, &cfg, cfgPath, "", false) - require.ErrorContains(t, err, "failed to read requirements file") - assert.False(t, uploaded, "no artifacts should be uploaded when dependency resolution fails") - }) } // The resolved policy id must reach the submit payload, by literal id and by name. diff --git a/experimental/air/cmd/runupload.go b/experimental/air/cmd/runupload.go index a6e68655d5d..ca0ffd9c480 100644 --- a/experimental/air/cmd/runupload.go +++ b/experimental/air/cmd/runupload.go @@ -43,34 +43,6 @@ type fileWriter interface { Write(ctx context.Context, name string, reader io.Reader, mode ...filer.WriteMode) error } -// requirementsDoc mirrors the on-disk requirements.yaml format, used to read a -// user-provided requirements file's dependencies for the inline spec.dependencies. -type requirementsDoc struct { - Version string `yaml:"version,omitempty"` - Dependencies []string `yaml:"dependencies"` -} - -// readRequirementsDependencies reads the dependencies and version out of a -// requirements.yaml file so file-form deps can be carried on the serverless -// environment's inline spec.dependencies and its version can select the runtime -// image. Returns an empty list when the file declares no dependencies. -func readRequirementsDependencies(reqPath string) ([]string, string, error) { - data, err := os.ReadFile(reqPath) - if err != nil { - return nil, "", fmt.Errorf("failed to read requirements file %s: %w", reqPath, err) - } - var doc requirementsDoc - if err := yaml.Unmarshal(data, &doc); err != nil { - return nil, "", fmt.Errorf("failed to parse requirements file %s: %w", reqPath, err) - } - for _, dep := range doc.Dependencies { - if fields := strings.Fields(dep); len(fields) > 0 && (fields[0] == "-r" || fields[0] == "--requirement") { - return nil, "", fmt.Errorf("requirements file dependency %q uses a requirements-file include (-r/--requirement), which is not supported; list the dependencies directly instead", dep) - } - } - return doc.Dependencies, doc.Version, nil -} - // buildArtifacts assembles the files to upload for a run: the merged config, the // inline command as a script, and hyperparameters. configPath is the local YAML // path. diff --git a/experimental/air/cmd/runupload_test.go b/experimental/air/cmd/runupload_test.go index e57a8aa2c41..1b3e04ee00d 100644 --- a/experimental/air/cmd/runupload_test.go +++ b/experimental/air/cmd/runupload_test.go @@ -62,7 +62,7 @@ func TestBuildArtifacts_ParametersButNoRequirements(t *testing.T) { cfg := &runConfig{ Command: new("echo hi"), Environment: &environmentConfig{ - Dependencies: dependencies{set: true, isList: true, list: []string{"torch", "numpy"}}, + Dependencies: dependencies{set: true, list: []string{"torch", "numpy"}}, Version: stringOrInt{set: true, raw: "5"}, }, Parameters: map[string]any{"lr": 0.1}, @@ -94,21 +94,6 @@ func TestBuildArtifacts_EnvVarsAndSecrets(t *testing.T) { assert.JSONEq(t, `[{"name":"HF_TOKEN","secret_scope":"myscope","secret_key":"hf"}]`, string(byName[secretEnvVarsName])) } -func TestBuildArtifacts_RequirementsFileNotUploaded(t *testing.T) { - dir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(dir, "run.yaml"), []byte("x: y\n"), 0o600)) - require.NoError(t, os.WriteFile(filepath.Join(dir, "reqs.yaml"), []byte("version: 4\n"), 0o600)) - cfg := &runConfig{ - Command: new("echo hi"), - Environment: &environmentConfig{Dependencies: dependencies{set: true, isList: false, path: "reqs.yaml"}}, - } - - // A declared requirements file is not uploaded; its deps travel on spec.dependencies. - items, err := buildArtifacts(cfg, filepath.Join(dir, "run.yaml")) - require.NoError(t, err) - assert.Equal(t, []string{trainingConfigName, commandScriptName}, itemNames(items)) -} - func TestBuildArtifacts_OversizeConfigRejected(t *testing.T) { path := writeConfigFile(t, "run.yaml", strings.Repeat("a", maxConfigYAMLBytes+1)) _, err := buildArtifacts(&runConfig{Command: new("x")}, path)