Skip to content

only rebuild images if something in tools changes#4

Merged
TomHennen merged 1 commit into
mainfrom
less_image_builds
Dec 21, 2024
Merged

only rebuild images if something in tools changes#4
TomHennen merged 1 commit into
mainfrom
less_image_builds

Conversation

@TomHennen
Copy link
Copy Markdown
Owner

No description provided.

Signed-off-by: Tom Hennen <tom.hennen@gmail.com>
@TomHennen TomHennen merged commit 1bfe133 into main Dec 21, 2024
@TomHennen TomHennen deleted the less_image_builds branch December 22, 2024 14:42
TomHennen added a commit that referenced this pull request Apr 25, 2026
wrangle-test PR #4 (grant-python-id-token-perms) merged to main,
adding id-token: write + actions: read to test-python and
test-python-uv jobs so the new provenance step in wrangle's
reusable workflow can run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TomHennen added a commit that referenced this pull request Apr 26, 2026
The example workflow's `build:` job and the README's quick-start
both granted only `contents: read` when calling wrangle's reusable
workflow. That reusable workflow has a nested provenance job that
calls slsa-github-generator, which declares `contents: write` for
its upload-assets job. GitHub validates a called workflow's
permissions at startup regardless of any `if:` condition that
would skip the job at runtime — so callers granting only
`contents: read` got `startup_failure` immediately.

We hit this same bug in wrangle-test (fixed in tomhennen/wrangle-test
PRs #4 and #5) but the example workflow was never exercised
end-to-end and inherited the same gap. Adopters copying from
either the example or the README would have seen the same failure.

Fix:
- Add contents: write, id-token: write, actions: read to the
  build: job in gh_workflow_examples/build_python.yml.
- Same set in build/actions/python/README.md's quick-start snippet.
- Add bats assertions for both so this regression can't return.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TomHennen added a commit that referenced this pull request Apr 26, 2026
* Implement Python build type

Composite action (build/actions/python/action.yml):
- Input validation with globbing disabled
- uv detection (uv.lock → uv sync/build/run) with PEP 517 fallback
- Python version auto-detected from pyproject.toml via setup-python
- pytest gating (skips gracefully if no tests found)
- SPDX SBOM via syft
- Artifact hash computation for SLSA generator

Reusable workflow (build_and_publish_python.yml):
- Three-job architecture: build (contents:read only) → publish
  (id-token:write, non-PR) → provenance (SLSA L3, non-PR)
- PyPI Trusted Publishing via OIDC (no secrets)
- PEP 740 Sigstore attestations on publish
- SLSA L3 provenance via slsa-github-generator (tag ref per #147)

Also adds:
- 19 structural tests (build/actions/python/test.bats)
- Workflow example (gh_workflow_examples/build_python.yml)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update Python action SHA to include composite action

The previous commit added the action but the workflow referenced
the parent SHA where the action didn't exist yet. Update to the
commit that contains the action.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Trigger fresh CI after companion permission fix

* Split Python build into build-only workflow + adopter publish

PyPI Trusted Publishing does not support reusable workflows — the
OIDC token's workflow_ref must point to the adopter's own workflow.
(pypi/warehouse#11096, open since 2022, no timeline)

Changes:
- Reusable workflow is now build-only: build, test, SBOM, hash
  computation. Exports hashes and version outputs.
- Remove publish and provenance jobs from reusable workflow.
- Workflow example (gh_workflow_examples/build_python.yml) includes
  publish + SLSA provenance jobs directly — adopters copy this and
  configure their Trusted Publisher against their own workflow file.
- Update structural tests to verify build-only architecture.
- Update companion template to build-only with minimal permissions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Update Python spec to reflect build-only reusable workflow

Document the PyPI Trusted Publishing limitation and the split
architecture: wrangle owns build/test/SBOM, adopter owns publish.
Reference #157 for when to revisit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Trigger CI for end-to-end Python publish test

* Trigger CI for zizmor suppression fix

* Address PR review: cosign-verified syft, SBOM upload, hash format

Resolves the three blocking issues from the PR review:

1. Replace `curl | sh` syft install with `tools/syft/install.sh` —
   Cosign keyless verifies checksums.txt (root of trust = Fulcio CA +
   Rekor), then SHA-256 verifies the binary against the trusted
   checksums. Installs to $WRANGLE_BIN_DIR per the install contract.
   Adds sigstore/cosign-installer step before the syft install.
2. Upload `metadata/python/<shortname>/` (SBOM) as a second artifact
   in the reusable workflow. Namespace both dist + metadata artifact
   names by shortname so multiple python builds don't collide; expose
   `dist-artifact-name` output for the adopter's publish job.
3. Refresh SPEC.md to match the build-only reusable workflow:
   drop `repository-url` input, replace stale outputs table, rewrite
   steps 7-10, and rewrite the sample workflow.

Plus the smaller fixes:
- Hash format: `cd dist && sha256sum -- *` (drops `./` prefix that
  would break slsa-verifier subject matching).
- Replace `ls dist/*.whl | head` with `shopt -s nullglob` so the
  pipefail edge case can surface a friendly warning.
- Example workflow: add `upload-assets: ${{ startsWith(github.ref,
  'refs/tags/') }}` to use the `contents: write` permission, document
  the workflow_dispatch publish behavior.
- Tighten the pytest-config grep (`grep -qF '[tool.pytest'`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Bump composite-action pin in reusable workflow to 93cfefa

The reusable workflow still pinned the composite action at the
previous SHA (ae27083), so the integration test was running the
pre-review code — including the curl|sh syft installer that crashed
with EACCES on /boot/efi.

Bumping the pin to 93cfefa picks up the cosign-verified syft install,
the namespaced artifacts, the corrected hash format, and the SBOM
upload. This is the one-commit-lag pattern (see CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix syft cosign identity: anchore releases from main, not tag refs

Anchore's release workflow (anchore/syft/.github/workflows/release.yaml)
is triggered by pushes to main, not by tag pushes — so the OIDC
certificate's workflow_ref claim is @refs/heads/main, not
@refs/tags/v<version>. The previous regex required tag refs and
rejected every signature, aborting installs.

Confirmed by decoding the .pem from
github.com/anchore/syft/releases/download/v1.42.4/syft_1.42.4_checksums.txt.pem
— Subject URI is
"https://github.com/anchore/syft/.github/workflows/release.yaml@refs/heads/main".

Switching to the literal --certificate-identity for that exact value;
this is the strongest claim available given Anchore's release setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Bump composite-action pin to ba6dfa6 (cosign identity fix)

Picks up the syft cosign --certificate-identity fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Move SLSA provenance into reusable workflow; extract helpers

Address remaining PR-156 review comments:

- SLSA provenance generation moves from the example workflow into
  wrangle's reusable workflow (parallel to the container build).
  The example workflow now downloads the provenance artifact and
  runs `slsa-verifier verify-artifact` before publishing — the
  recommended pattern, but adopters can drop the verify step.
- Extract path validation to lib/validate_path.sh (shared) plus
  per-action validate_inputs.sh wrappers for python and container,
  satisfying CLAUDE.md's "extract inline run: blocks >5 lines or
  with logic" rule.
- Extract python install/test logic to install_deps.sh and
  run_tests.sh; document pytest discovery convention.
- Add build/actions/python/README.md with adopter onboarding,
  optional verify-before-publish recipe, and consumer-side
  verification commands. Update build/README.md and
  gh_workflow_examples/README.md to mention python.
- Update SPEC.md to reflect provenance-in-reusable-workflow,
  the new provenance-artifact-name output, and the pytest
  convention.
- Add behavioral tests for lib/validate_path.sh and the
  container validate_inputs.sh; refresh python test.bats for
  the helper extraction.

The reusable workflow's composite-action SHA pin still references
the pre-refactor commit and must be bumped to this commit's SHA
in a follow-up commit before integration tests will pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Bump composite-action pin to c65a939 (helper-script extraction)

c65a939 extracted validate_inputs.sh, install_deps.sh, run_tests.sh,
and lib/validate_path.sh. The reusable workflow's `uses:` ref needs
to point at that commit so the composite action it pulls includes
those scripts; the previous pin (ba6dfa6) does not.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Trigger CI to exercise merged python-uv fixture

The companion repo's add-python-uv-fixture branch landed on main,
adding python-uv/ and the test-python-uv job. This empty commit
re-runs the integration test so the new uv path coverage exercises
the post-refactor composite (helper scripts + provenance-in-reusable-workflow).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix SLSA generator output name (provenance-name, not -download-name)

The previous commit referenced ${{ jobs.provenance.outputs.provenance-download-name }}
but slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
declares its output as `provenance-name`, not `provenance-download-name`.
GitHub couldn't resolve the workflow_call output binding, so the integration
test failed at startup before any job ran.

Source: https://github.com/slsa-framework/slsa-github-generator/blob/v2.1.0/.github/workflows/generator_generic_slsa3.yml

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Trigger CI after companion permission fix

wrangle-test PR #4 (grant-python-id-token-perms) merged to main,
adding id-token: write + actions: read to test-python and
test-python-uv jobs so the new provenance step in wrangle's
reusable workflow can run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Diagnostic: drop provenance-artifact-name workflow output

Suspect: workflow_call.outputs.<name>.value referencing a job that
is itself a `uses:` reusable-workflow call (the SLSA generator)
may be what's tripping startup_failure on the integration run.
Container's reusable workflow has zero workflow_call outputs and
works; python's only structural difference is this one output
binding. Removing it temporarily to confirm.

If the integration test passes after this, replace this commit
with a permanent fix: keep the output gone and have the example
download provenance via `pattern: '*.intoto.jsonl'` instead of by name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Drop contents: write from provenance job (caller permission cascade)

Root cause of the integration startup_failure: the provenance job
declared `contents: write` for upload-assets on tag pushes, but
GitHub's permission model requires every caller of a reusable
workflow to grant every scope the workflow declares. wrangle-test's
test-python jobs grant `contents: read`, so GitHub rejected the
call before any job ran.

Container's reusable workflow doesn't hit this because its provenance
job uses `packages: write` (registry attestations) — and wrangle-test
already grants packages: write on test-container.

Fix: drop `contents: write` AND `upload-assets:` from the provenance
job. Provenance is uploaded as a workflow artifact (default behavior,
90-day retention). Adopters who want release-asset upload re-upload
from their own publish job where they already hold `contents: write`
for the release.

This also restores the `provenance-artifact-name` workflow output —
the diagnostic in the previous commit confirmed it wasn't the cause.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Grant contents: read to provenance job

generator_generic_slsa3.yml declares `permissions: contents: read`
at its workflow_call top level. When a job uses scoped permissions,
GitHub denies all unlisted scopes — so without explicitly granting
contents: read in the provenance job, the SLSA generator's call
fails at startup.

container_slsa3.yml uses `permissions: {}` instead, which is why
the container reusable workflow's provenance job didn't need this
and worked end-to-end.

This is the third and final permission fix in the cascade. Read
GitHub's permission resolution model:
https://docs.github.com/en/actions/using-workflows/reusing-workflows#access-and-permissions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Restore contents: write + upload-assets on provenance job

Confirmed by reading slsa-github-generator v2.1.0's reference example:
GitHub validates every called-job's permissions at workflow startup,
regardless of `if:` conditions that would skip the job at runtime.
The generic generator's upload-assets job declares contents: write,
so the caller must grant it — even with upload-assets disabled.

Since the write permission is unavoidable, restore upload-assets:
${{ startsWith(github.ref, 'refs/tags/') }} and use the permission
for the real feature it enables (release-asset upload on tags).

Wrangle-test PR #5 (companion) lifts test-python and test-python-uv
to contents: write so the call from wrangle-test passes startup.

Reference:
https://github.com/slsa-framework/slsa-github-generator/blob/v2.1.0/internal/builders/generic/README.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Trigger CI after wrangle-test contents:write merge

wrangle-test PR #5 (grant-python-contents-write) merged to main,
upgrading test-python and test-python-uv to grant contents: write
so the SLSA generator's upload-assets job permission validates at
startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* README: split PEP 740 vs SLSA verification, clarify availability

Address PR-156 review comments on build/actions/python/README.md:

- The "Verifying after install" section conflated PEP 740 attestations
  (verified against PyPI, prove who published) and SLSA L3 provenance
  (verified against GitHub release assets, prove how it was built).
  Split into two sub-sections that name each system, where the
  artifact lives, and what `slsa-verifier`/`pip`/`sigstore-python`
  each verify.
- Note that SLSA provenance is only attached to GitHub releases on
  tag pushes (because the reusable workflow's upload-assets is
  gated on `startsWith(github.ref, 'refs/tags/')`). Non-tag publishes
  leave provenance only as a 90-day workflow artifact, which
  external consumers can't retrieve.
- Establish a wrangle convention: tag pushes publish provenance to
  the release; non-tag publishes don't have consumer-retrievable
  provenance. Adopters whose workflows don't push tags should
  publicize this.
- Note that the verify call in the recipe is exercised by the
  integration test; consumer-side verification against a real
  release is tracked in #163.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix permission bug in example workflow + README quick-start

The example workflow's `build:` job and the README's quick-start
both granted only `contents: read` when calling wrangle's reusable
workflow. That reusable workflow has a nested provenance job that
calls slsa-github-generator, which declares `contents: write` for
its upload-assets job. GitHub validates a called workflow's
permissions at startup regardless of any `if:` condition that
would skip the job at runtime — so callers granting only
`contents: read` got `startup_failure` immediately.

We hit this same bug in wrangle-test (fixed in tomhennen/wrangle-test
PRs #4 and #5) but the example workflow was never exercised
end-to-end and inherited the same gap. Adopters copying from
either the example or the README would have seen the same failure.

Fix:
- Add contents: write, id-token: write, actions: read to the
  build: job in gh_workflow_examples/build_python.yml.
- Same set in build/actions/python/README.md's quick-start snippet.
- Add bats assertions for both so this regression can't return.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Remove setup.py-only fallback (it never worked)

validate_inputs.sh accepted projects with only setup.py (no
pyproject.toml) by emitting a warning and continuing. But
actions/setup-python in the next step uses
`python-version-file: pyproject.toml` which fails when the file
doesn't exist. The fallback was therefore broken in practice —
the action would green-light the input then crash a step later.

Tighten the validator: pyproject.toml is required. Surface a
helpful error pointing at the packaging docs so adopters know
how to migrate. Update SPEC.md to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <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