Skip to content

AIR CLI Integration: Port code_source Snapshotting#5897

Open
riddhibhagwat-db wants to merge 4 commits into
air-clifrom
air-integration-m4
Open

AIR CLI Integration: Port code_source Snapshotting#5897
riddhibhagwat-db wants to merge 4 commits into
air-clifrom
air-integration-m4

Conversation

@riddhibhagwat-db

@riddhibhagwat-db riddhibhagwat-db commented Jul 11, 2026

Copy link
Copy Markdown

Changes

Ports code_source snapshotting from the Python air CLI to Go and wires it into air run. When a config sets code_source.type: snapshot, the CLI now packages the local repo into a tarball, uploads it to the workspace (or a Volume), records git provenance sidecars, and attaches the paths (code_source_path, git_state_path, git_diff_path) to the ai_runtime_task payload.

Git-archive tarballs are cached by (commit, include_paths), so re-submitting the same commit skips packaging and upload.

Why

air run needs to ship the user's code to the training workload. This is a local-only port: we archive the local checkout rather than cloning from a remote, since that remote-fetch path is being deprecated. Consequences of that decision, which aren't obvious from the code alone:

  • git.remote is rejected with an actionable error (pointing users to git.commit) instead of silently fetching.
  • A ref must resolve to a commit already present locally.
  • A dirty working tree combined with git.branch is a hard error and git.branch deploys the committed HEAD, so uncommitted changes would silently be dropped.

Tests

  • Unit: cache-key parity with Python golden fixtures, git queries, the resolve decision tree, tar builders, and provenance sidecars
  • Acceptance: air run --dry-run validation & a full recorded-request submit asserting the snapshot paths land on the payload
  • End-to-end manual verification on a live workspace: exercised git-archive, plain-tar (dirty, with git_diff.patch), cache-hit reuse, and the dirty+git.branch guard; inspected the uploaded tarball and git_state.json.

First slice of the code_source snapshot port (Go equivalent of the Python
CLI's cli/utils/snapshot.py). Adds the snapshot subpackage with the pure
cache-key function and a golden-fixture harness:

- cachekey.go: ComputeCacheKey + PackagingVersion="v1", ported verbatim from
  compute_snapshot_cache_key so both CLIs agree on the key algorithm.
- testdata/cache_keys.json: golden keys captured from the exact Python
  algorithm over the local-only matrix (commit + include_paths permutations).
- cachekey_test.go: byte-for-byte golden table test plus a properties test
  pinning the normalization (trim + sort, no dedup, nil == empty).

Co-authored-by: Isaac
…only)

Continues the code_source snapshot port. Flattens the snapshot files into the
aircmd package (matching the rest of experimental/air/cmd; an organizational
layer can land later) and adds Phase 2/3:

- snapshot_git.go: local, no-network git introspection ported from
  cli/utils/git_state.py — is-repo, HEAD/branch SHA, dirty check (whole-tree
  and path-scoped), commit-exists, include-path validation. Concrete gitRepo
  shelling out with arg slices (no shell string); tested against real temp
  repos, matching the Python tests.
- snapshot_resolve.go: the mode/ref decision tree (cli_entrypoint ~1541-1722),
  local-only. git.commit (must exist locally) / git.branch (local HEAD) ->
  git_archive; no ref or non-git dir -> plain_tar. Dirty check runs once and is
  threaded into the plan; dirty + git.branch is a hard error.
- snapshot_package.go: git archive / plain tar builders + .gitignore->exclude
  parsing ported from cli/utils/snapshot.py. Shells out for parity; preserves
  the top-level dir name (--prefix / -C parent) the remote extraction depends
  on, and the .git / macOS AppleDouble exclusions.
- runconfig.go: reject a truthy git.remote (remote-fetch path deprecated) with
  an actionable error pointing to git.commit; remote: false stays valid.

The remote-fetch flavor (fetch_branch_sha, partial clone, remote HEAD) is
intentionally not ported: the snapshot archives the local copy only.

Co-authored-by: Isaac
Completes the code_source snapshot port (Phases 4-5): git provenance sidecars,
the top-level orchestrator, upload via libs/filer, and the submit wiring.

- snapshot_gitstate.go: git_state.json (schema_version 1, Python field names,
  null-for-absent encoding) + git_diff.patch capture (dirty-only, 1MB cap, 5s
  timeout). Best-effort — a failure warns and continues, never fails submit.
  Merge-base/repo-url read local refs only (no fetch), matching the local-only
  design.
- snapshot.go: runSnapshot orchestrator (resolve -> package+upload ->
  sidecars). git_archive is cache-keyed and a workspace/Volume existence check
  skips packaging+upload on a hit; plain_tar is timestamp-named. Uploads through
  libs/filer (WorkspaceFilesClient, or FilesClient for a remote_volume). Also
  ports root_path resolution (project_root/, ~, env vars).
- snapshot_git.go: add currentBranch / remoteURL / mergeBaseWithUpstream and a
  runBytes variant for the raw diff bytes.
- runsubmit.go: drop the code_source rejection; package the snapshot and attach
  code_source_path / git_state_path / git_diff_path to the ai_runtime_task.

Tested end to end against the in-process fake workspace, including the cache-hit
skip and dirty-diff sidecar paths.

Co-authored-by: Isaac
Phase 6 of the snapshot port — acceptance tests over the user-visible surface.

- experimental/air/run: add dry-run cases for the new code_source config
  surface — a valid snapshot config passes validation, and a git.remote config
  is rejected with the actionable error.
- experimental/air/run-submit: a full (non-dry-run) submit that packages a git
  code_source, uploads the tarball + git_state sidecar, and asserts
  code_source_path / git_state_path land on the ai_runtime_task payload. The
  commit is date-pinned so the cache-key-derived tarball name is stable, and
  RecordRequests captures the submitted payload.

Co-authored-by: Isaac
@github-actions

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/experimental/air/ - needs approval

10 files changed
Eligible: @apeforest, @bfontain, @lu-wang-dl, @panchalhp-db, @vinchenzo-db, @maggiewang-db, @ben-hansen-db, @pardis-beikzadeh-db

/experimental/air/ - needs approval

15 files changed
Eligible: @apeforest, @bfontain, @lu-wang-dl, @panchalhp-db, @vinchenzo-db, @maggiewang-db, @ben-hansen-db, @pardis-beikzadeh-db

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose) can approve all areas.
See OWNERS for ownership rules.

@riddhibhagwat-db riddhibhagwat-db deployed to test-trigger-is July 11, 2026 05:45 — with GitHub Actions Active
@riddhibhagwat-db riddhibhagwat-db changed the title AIR CLI Integration: port code_source snapshotting (local-only) AIR CLI Integration: Port code_source Snapshotting Jul 11, 2026
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f0d74ce

Run: 29141712788

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 4 4 230 1085 6:05
🔄​ aws windows 2 4 4 230 1083 10:46
💚​ aws-ucws linux 4 4 314 1003 5:15
💚​ aws-ucws windows 4 4 316 1001 7:09
💚​ azure linux 4 4 230 1084 6:02
💚​ azure windows 4 4 232 1082 7:09
💚​ azure-ucws linux 4 4 316 1000 6:30
💚​ azure-ucws windows 4 4 318 998 7:14
💚​ gcp linux 4 4 229 1086 6:32
💚​ gcp windows 4 4 231 1084 7:05
10 interesting tests: 4 RECOVERED, 4 SKIP, 2 flaky
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestSecretsPutSecretBytesValue ✅​p 🔄​f 🙈​s 🙈​s ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestSecretsPutSecretStringValue ✅​p 🔄​f 🙈​s 🙈​s ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 15 slowest tests (at least 2 minutes):
duration env testname
6:14 aws windows TestAccept
6:10 aws-ucws windows TestAccept
6:10 azure-ucws windows TestAccept
6:08 gcp windows TestAccept
6:08 azure windows TestAccept
2:59 aws linux TestAccept
2:58 azure linux TestAccept
2:58 gcp linux TestSecretsPutSecretStringValue
2:53 azure-ucws linux TestAccept
2:50 aws linux TestSecretsPutSecretStringValue
2:48 aws-ucws linux TestAccept
2:48 gcp linux TestAccept
2:45 aws windows TestSecretsPutSecretStringValue
2:22 azure linux TestSecretsPutSecretStringValue
2:13 azure-ucws linux TestSecretsPutSecretStringValue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants