Skip to content

feat: auto-attest docker images pushed in later steps#33

Draft
tooky wants to merge 1 commit into
mainfrom
20260715_attest_docker_artifact
Draft

feat: auto-attest docker images pushed in later steps#33
tooky wants to merge 1 commit into
mainfrom
20260715_attest_docker_artifact

Conversation

@tooky

@tooky tooky commented Jul 15, 2026

Copy link
Copy Markdown

What

Adds an opt-in attest-docker-artifact: true input. When enabled (Linux runners only), the action installs bash shims for docker, buildx, and docker-buildx that shadow the real binaries on the PATH of subsequent steps. Whenever a later step pushes an image — docker push, docker build --push, docker buildx build --push, standalone buildx build --push, or the --output type=registry / push=true long forms — the shim runs

kosli attest artifact "<ref>" --artifact-type=oci --name "<name>"

for each pushed tag, after the push succeeds. No explicit attest step needed:

- uses: kosli-dev/setup-cli-action@v5   # must run BEFORE the build/push step
  with:
    attest-docker-artifact: true
- uses: docker/build-push-action@v5
  with: { push: true, tags: my-registry/my-image:latest }
# the push is attested automatically

--name is derived per image ref (strip digest → last /-segment → strip tag), so monorepos pushing several images each get their own artifact name; artifact-name overrides it for single-artifact repos. attest-flags appends extra flags; fail-on-attest-error (default false) controls whether a failed attestation fails the push step. A real docker/buildx failure is never masked, and the shim writes only to stderr so $(docker push -q ...) captures stay clean.

How

  • src/shim.js — the bash shim embedded as a JS string constant, so ncc bundles it into dist/ on release (a runtime-read .sh asset would only break in released tags). Bash-3.2 compatible.
  • src/wrapDocker.js — resolves the real binaries, writes the shim per program name into a fresh dir under $RUNNER_TEMP, passes config via KOSLI_SHIM_* env vars (can't collide with the CLI's KOSLI_<flag> binding), prepends the dir to PATH. Warns and no-ops rather than ever failing the CLI install.

Testing

  • test/shim.test.js executes the real shim against stub docker/kosli binaries that record their argv — 26 cases covering all three invocation names, ref collection, dedupe, name derivation, and failure handling.
  • test/wrapDocker.test.js unit-tests the installer with injected deps.
  • New attest-docker integration job in test.yaml: pushes to a local registry:2 with KOSLI_DRY_RUN=true and asserts the shim's marker lines for docker push, docker buildx build --push, and a no-push build.

Learnings from the first CI run (all green)

  • The design's known unknown is resolved: kosli attest artifact --artifact-type=oci under KOSLI_DRY_RUN fingerprints an image from a plain-HTTP localhost:5000 registry with no extra flags or config. The integration job needed zero debug cycles.
  • The shim really is bash-3.2 clean: the shim unit tests execute the actual script on the macOS matrix leg (bash 3.2), so the no-associative-arrays / guarded-double-shift constraints are enforced by CI, not just convention.
  • Asserting on the shim's own kosli-shim: marker lines (not kosli's dry-run output) worked as intended — the integration test stays decoupled from CLI output format changes.
  • Two local-dev findings that shaped the tests: AVA's 10s inactivity timeout trips if many spawnSync calls block the worker thread, so the harness spawns the shim asynchronously; and npm run build's convert-action step rewrites action.yml to main: dist/index.js, which must not be committed on main (dist/ only lands on release tags).

v1 limitations (documented in README)

--all-tags push (warned + skipped), absolute-path invocations of docker (PATH shims can't intercept), buildx bake / docker compose push, attest-flags values containing spaces.

🤖 Generated with Claude Code

Opt-in via attest-docker-artifact: true. On Linux runners the action
installs bash shims for docker/buildx/docker-buildx that shadow the real
binaries on the PATH of subsequent steps. After a successful push
(docker push, build --push, or --output type=registry/push=true) the
shim runs `kosli attest artifact <ref> --artifact-type=oci` for each
pushed tag, deriving --name from the image ref unless artifact-name is
set. Attest failures warn by default (fail-on-attest-error to opt out);
a real docker failure is never masked.

The shim source is embedded as a JS string constant so ncc bundles it
into dist/ on release. Unit tests execute the shim against stub
docker/kosli binaries; a new integration job pushes to a local
registry:2 with KOSLI_DRY_RUN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant