Skip to content

ci: retry transient compliance export failures - #14694

Merged
nv-rinig merged 4 commits into
ai-dynamo:mainfrom
xianlubird:codex/compliance-export-retry
Sep 23, 2026
Merged

nv-rinig merged 4 commits into
ai-dynamo:mainfrom
xianlubird:codex/compliance-export-retry

Conversation

@xianlubird

@xianlubird xianlubird commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Summary

The Planner image build in #14377 succeeded, but the subsequent compliance export failed while BuildKit resolved ghcr.io/astral-sh/uv:0.12.0 with Unavailable: error reading from server: EOF. That made the build job fail and skipped its downstream tests. Failure logs.

Details

Add bounded retries to the shared /compliance/ export: at most three attempts, with 10s and 30s delays, for narrowly matched transient transport errors in the final BuildKit error. Compilation failures, explicit authorization denials, missing manifests, and unrecognized errors fail immediately; exhausted retries retain a failing exit status.

Each attempt exports to a fresh temporary directory. Only a successful export replaces the destination, and every attempt's output remains in the job log. This is an independent CI fix based on main; it does not depend on #14377 or change its Planner code, job dependencies, or compliance requirements.

Where should the reviewer start?

Start with .github/actions/compliance-extract/extract-with-retry.sh for error classification and output isolation, then .pre-commit-config.yaml for the regression-test hook. The existing Pre Merge pre-commit job runs this hook automatically; the tests use only Python's standard library and Bash.

Related Issues

Validation

  • 8 regression tests passed: immediate success, retry recovery, retry exhaustion, deterministic failures, final-error classification, and partial-output isolation. Tests execute the real Bash wrapper with fake BuildKit and sleep commands.
  • Bash syntax and action YAML parsing passed.
  • Black 23.1.0, isort, Flake8 7.3.0, Ruff 0.5.2, and git diff --check passed.
  • Live remote BuildKit/registry recovery has not been exercised locally.

Run the regression suite with python3 .github/actions/compliance-extract/test_extract_with_retry.py.

Summary by CodeRabbit

  • Bug Fixes

    • Improved compliance extraction reliability by retrying recognized temporary build transport failures.
    • Prevented incomplete or partial extraction results from replacing successful output.
    • Preserved immediate failure handling for permanent, authorization, and unrecognized errors.
    • Added clearer per-attempt logging to support troubleshooting.
  • Tests

    • Added coverage for successful extraction, retry behavior, exhausted retries, permanent failures, cleanup, and output validation.

The compliance-extract-selftest pre-commit hook passes all 8 tests. Temporarily disabling retries makes the same hook fail; restoring the wrapper makes it pass again. Verified selection for the action, wrapper, tests, and hook configuration, with unrelated files skipped.

Signed-off-by: xianlubird <xianlubird@gmail.com>
@xianlubird
xianlubird requested a review from a team as a code owner September 11, 2026 03:13
@copy-pr-bot

copy-pr-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@xianlubird
xianlubird deployed to external_collaborator September 11, 2026 03:13 — with GitHub Actions Active
@xianlubird
xianlubird deployed to external_collaborator September 11, 2026 03:13 — with GitHub Actions Active
@github-actions github-actions Bot added external-contribution Pull request is from an external contributor trusted-contributor Org-External user who is trusted to run CI without Org-member approval ci Issues/PRs that reference CI build/test labels Sep 11, 2026
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test d5896ac

@coderabbitai

coderabbitai Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b3d6a218-89cb-4dcc-a55a-e329ab79e21a

📥 Commits

Reviewing files that changed from the base of the PR and between 58f1e01 and d5896ac.

📒 Files selected for processing (3)
  • .github/actions/compliance-extract/action.yml
  • .github/actions/compliance-extract/extract-with-retry.sh
  • .github/actions/compliance-extract/test_extract_with_retry.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


Walkthrough

The compliance extraction action now delegates Docker BuildKit execution to a retry wrapper. The wrapper isolates attempts, classifies failures, publishes successful output, and records logs. A Python test suite covers recovery, exhaustion, terminal failures, cleanup, and retry behavior.

Changes

Compliance extraction

Layer / File(s) Summary
Retry wrapper and validation
.github/actions/compliance-extract/extract-with-retry.sh, .github/actions/compliance-extract/test_extract_with_retry.py
The wrapper retries recognized transient BuildKit failures, preserves attempt logs, removes failed exports, and publishes output only after success. Tests cover successful recovery, retry exhaustion, terminal failures, cleanup, delays, and false-positive command text.
Action integration
.github/actions/compliance-extract/action.yml
The action invokes the retry wrapper with the output directory and Docker build command. Inline directory creation and the direct local output flag are removed.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to d5896

The compliance export now retries recognized transient BuildKit failures while preserving successful-output publication and terminal failure behavior. No concrete merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 2 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: retrying transient compliance export failures in CI.
Description check ✅ Passed The description includes the required Overview, Details, reviewer guidance, and Related Issues sections. It explains the implementation, scope, validation, and known testing limitation. The reported t…
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 2 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/actions/compliance-extract/test_extract_with_retry.py
Signed-off-by: xianlubird <xianlubird@gmail.com>
@xianlubird
xianlubird deployed to external_collaborator September 11, 2026 03:37 — with GitHub Actions Active
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 78a18d5

Comment thread .github/actions/compliance-extract/test_extract_with_retry.py Outdated
Comment thread .github/actions/compliance-extract/test_extract_with_retry.py Outdated
Signed-off-by: xianlubird <xianlubird@gmail.com>
@xianlubird
xianlubird deployed to external_collaborator September 11, 2026 05:57 — with GitHub Actions Active
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test e8b93c4

@dmitry-tokarev-nv dmitry-tokarev-nv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: bounded retry for the compliance export

I verified the retry predicate, the permanent-failure path, the exhausted-retry path, idempotence and the siblings. One P3, no blockers.

The predicate matches the real error, and three constructed permanent failures still fail on attempt 1

The failing line in the run the description points to is:

ERROR: failed to build: failed to solve: Unavailable: ghcr.io/astral-sh/uv:0.12.0: Unavailable: error reading from server: EOF

The transient pattern Unavailable:.*error reading from server: (unexpected )?EOF matches it, and the permanent pattern does not. So the wrapper retries the case it was written for.

The predicate is an allow list, not "any non-zero exit". An error that matches neither list exits at once. I ran three real docker buildx build failures through the wrapper on macOS with buildx v0.36.1:

constructed failure terminal line attempts exit destination
RUN ... exit 1 process "..." did not complete successfully 1 1 not created
image that does not exist unexpected status from GET ...: 403 Forbidden 1 1 not created
FRUM busybox dockerfile parse error on line 1 1 1 not created

No permanent failure ends in success. The destination directory is never created on failure, so no later step can read a half-written export.

Exhausted retries reach a red job, and pipefail does not swallow the status

The wrapper pipes the build into tee, which is the shape that can hide an exit status. It does not here. Bash pipefail returns the status of the last command that exited non-zero, so the build status wins over tee. Measured with a fake build that exits 42 and prints the transient line:

EXIT=42  wall=41s
::warning::Compliance extraction attempt 1/3 failed with a transient transport error; retrying in 10s
::warning::Compliance extraction attempt 2/3 failed with a transient transport error; retrying in 30s

The step runs under bash --noprofile --norc -e -o pipefail, and the action has no continue-on-error, so a non-zero wrapper status fails the job.

Bounds: 3 attempts at most, delays of 10s then 30s, 40s of added sleep in the worst case. The export itself took 73s on this pull request in CI and about 6 minutes in the run that failed, so a total failure costs roughly 4 to 19 minutes against a job budget of 60 to 120 minutes.

Idempotence, and no path to a misleading baseline diff

Each attempt writes to a fresh output-N directory under mktemp -d. Only a success runs rm -rf destination then mv, so the second attempt neither appends to nor merges with the first. I seeded the destination with stale files and ran a successful export: the destination held only the new file afterward.

The step no longer runs mkdir -p /tmp/compliance. That is safe. Compute OSRB diff runs under if: always() with nullglob, and I confirmed it exits 0 for both a missing and an empty /tmp/compliance. Upload compliance artifact then finds no files and warns, as before.

CI on the head commit proves the whole path: the wrapper ran a real multi-arch export in vllm-runtime / Build multi-arch cuda13.0, and the naming step listed /tmp/compliance/linux_amd64/... and /tmp/compliance/linux_arm64/.... Compliance audit passed. The --output flag now sits after the build context .; buildx accepts flags in that position, which I confirmed locally and which this CI run confirms.

The new tests do pin the behavior

All 8 tests pass, and the compliance-extract-selftest hook passed in the pre-commit job on the head commit. The hook runs under pre-commit/action, which uses --all-files, so the files: pattern does not gate it. I mutated the wrapper to check the tests are not decorative:

mutation result
never retry 6 failures
retry on any error 13 errors
drop the permanent-error list 1 failure
keep a failed attempt's output still passes

The last mutation is not a defect. The destination is still populated only from a successful attempt, and the leftover is a temporary directory.

Unverified: I could not reproduce a live BuildKit transport failure, so the recovery path is proven with a fake build command, not a real flaky registry. The mv between $RUNNER_TEMP and /tmp is proven only on the runner filesystem this pull request already exercised.

Comment thread .github/actions/compliance-extract/action.yml
Signed-off-by: xianlubird <xianlubird@gmail.com>
@xianlubird
xianlubird deployed to external_collaborator September 18, 2026 02:05 — with GitHub Actions Active
@dynamo-ops

Copy link
Copy Markdown
Contributor

/ok to test 1a03cf1

@dmitry-tokarev-nv dmitry-tokarev-nv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approving at 1a03cf1f55. The earlier approval was for e8b93c48c5, so it is stale. The push added one commit on top of that tree and the merge base did not move.

The new commit wraps the /sources/ export in the same retry script, which closes my only open finding. Zero P0, zero P1, zero P2, zero P3 open.

What I re-verified at this head, by execution

Push shape by ancestry: git merge-base --is-ancestor e8b93c48c5 HEAD returns true, so this is an append and not a force-push. The merge base against main is 58f1e01f76 for both the old head and the new head.

Retry bound: 3 attempts, delays of 10s then 30s, so at most 40s of added sleep per wrapped step. The Compliance extract step at this head took 9s in a job with a 60-minute limit.

Failure classification: I ran the real wrapper against 13 different final error lines with a fake build command. The 4 transport patterns retried to the cap. Compile failures, 401, 403, denied:, manifest unknown, an unrecognized error, and an error with no ERROR: line all failed on the first attempt with no sleep.

Artifact: a retried success published a tree that diff -r reports IDENTICAL to a first-attempt success, and the partial file from the failed attempt did not reach the destination. A control that added one file reported DIFFERS.

Visibility after the last attempt: with a build that exits 17, the wrapper exited 17. The original error text appeared 3 times in the step output and was the last line printed.

Argument order: the wrapper appends --output after the build context. A real buildx 0.37.1 export with the flag after the context produced a result identical to the old form. Controls confirmed buildx parses flags in that position and rejects a second context argument.

Expression check: the Compliance extract step reports success at this head, so ${{ github.action_path }} resolves to a real path and the wrapper runs. The /sources/ step uses the same expression in the same file.

Not verified: the /sources/ step is gated on archive_sources == 'true', and every caller passes false today, so no pipeline exercises that path yet. I also did not exercise a live registry failure.

@nv-rinig
nv-rinig merged commit a2e7da3 into ai-dynamo:main Sep 23, 2026
116 checks passed
@xianlubird
xianlubird deleted the codex/compliance-export-retry branch September 24, 2026 00:48

This branch was successfully deployed

1 active deployment
external_collaborator — 1a03cf1f Deployed Sep 18, 2026 by xianlubird via ok-to-test #19895
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

actions ci Issues/PRs that reference CI build/test external-contribution Pull request is from an external contributor size/L trusted-contributor Org-External user who is trusted to run CI without Org-member approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants