Skip to content

ci: switch from SLSA provenance to actions/attest with subject-path#46

Open
keelerm84 wants to merge 10 commits intomainfrom
devin/1774991555-immutable-releases
Open

ci: switch from SLSA provenance to actions/attest with subject-path#46
keelerm84 wants to merge 10 commits intomainfrom
devin/1774991555-immutable-releases

Conversation

@keelerm84
Copy link
Copy Markdown
Member

@keelerm84 keelerm84 commented Mar 31, 2026

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

N/A — CI-only and documentation changes, no application code or tests modified.

Related issues

Supports the org-wide migration to immutable GitHub releases. Reference implementation: launchdarkly/ld-relay.

Describe the solution you've provided

GitHub's immutable releases feature prevents modifying a release after it is published. The old SLSA provenance generator uploaded .intoto.jsonl files as release assets (via upload-assets: true), which would fail under immutable releases if the release was already published. Since this repo only uses attestation (no binary/artifact uploads to the release), draft releases are not needed — actions/attest@v4 stores attestations via GitHub's attestation API, not as release assets.

This PR makes the following changes:

  1. SLSA → actions/attest@v4 with subject-path (both workflows): Replaced the separate release-provenance job (which used slsa-framework/slsa-github-generator with upload-assets: true) with inline actions/attest@v4 steps in the build job. Attestation uses subject-path: 'dist/*' to reference built distribution files directly on disk, eliminating the previous base64 encode/decode round-trip through subject-checksums.

  2. Removed hash-related outputs and steps: The package-hashes output and "Hash build files for provenance" step are removed from the composite build action (.github/actions/build/action.yml), and the package-hashes job output is removed from workflows. No checksums file generation is needed since subject-path reads artifacts directly.

  3. Removed orphaned job outputs (release-please.yml): The release-created and upload-tag-name outputs were only consumed by the now-removed release-provenance job. They have been removed to avoid dead declarations.

  4. attestations: write permission (both workflows): Added to the build job to support actions/attest@v4.

  5. release-please-config.json: Cosmetic formatting only (array elements moved to separate lines). No draft or force-tag-creation options are needed since this repo does not upload artifacts to the release.

  6. Dry-run guards on attestation (manual-publish.yml): The attest step is gated on format('{0}', inputs.dry_run) == 'false' to safely handle the boolean/string coercion difference between workflow_dispatch (string) and workflow_call (boolean) triggers.

  7. Updated PROVENANCE.md: Rewrote verification instructions to use gh attestation verify ... --owner launchdarkly instead of slsa-verifier with downloaded .intoto.jsonl files. Sample output follows the real gh attestation verify output format including policy criteria and attestation details.

  8. Updated README.md: Changed the provenance section heading and description from "SLSA framework" to "GitHub artifact attestations" with a link to GitHub's attestation docs.

Describe alternatives you've considered

  • An earlier revision used subject-checksums with a checksums file (base64-decoded from the build action output). This was simplified to subject-path since the built artifacts are always on disk in the same job and the base64 round-trip was inherited from the old SLSA generator pattern.
  • An even earlier revision used draft releases with force-tag-creation and a publish-release job. This was removed since this repo only uses attestation (not artifact uploads), so draft releases are unnecessary.
  • An earlier revision added an unused tag input to manual-publish.yml for org-wide consistency. This was removed since it had no consumers and its description referenced "draft release" which doesn't apply here.
  • The verify command in PROVENANCE.md originally used -R launchdarkly/python-server-sdk-otel but was changed to --owner launchdarkly to match real observed output.
  • The dry-run guard originally used inputs.dry_run == false (bare boolean comparison), but this silently fails when workflow_call passes a real boolean vs. workflow_dispatch passing a string. The format('{0}', ...) pattern normalizes both to a string before comparison.

Additional context

Human review checklist — things worth verifying:

  • The subject-path: 'dist/*' glob correctly matches the output of poetry build (typically *.tar.gz and *.whl). If dist/ is empty or missing at attest time, the step will fail.
  • manual-publish.yml has contents: read while release-please.yml has contents: write. Verify actions/attest@v4 does not require contents: write — if it does, the manual publish workflow's attestation step will fail silently.
  • The dry-run guard uses format('{0}', inputs.dry_run) == 'false' to normalize boolean/string. Confirm this works for both workflow_dispatch (string 'false') and workflow_call (boolean false).
  • In release-please.yml, the attest step is gated on steps.release.outputs.releases_created (plural). Verify this is the correct output name — release-please also emits release_created (singular, for the root package). Both should work for a single-package repo, but confirm they are equivalent here.
  • Confirm no downstream consumers depend on the old .intoto.jsonl provenance file that was previously uploaded as a release asset.
  • Confirm no external workflows or monitoring depend on the removed release-created or upload-tag-name job outputs from release-please.yml.
  • The PROVENANCE.md sample output is representative (based on real gh attestation verify output from another repo), not captured from an actual run of this repo. Verify it matches reality after the first attested release.

Link to Devin session: https://app.devin.ai/sessions/7d5bda4d9dbe4ae0b950b30a50485e60
Requested by: @keelerm84


Note

Medium Risk
Release/publish workflows now generate provenance via actions/attest and drop the separate SLSA generator job, so misconfiguration (permissions, dist/* glob, or dry-run gating) could impact publishing or provenance availability.

Overview
Build provenance generation is migrated from the SLSA generator to GitHub Artifact Attestations. Both manual-publish.yml and release-please.yml drop the separate release-provenance job and instead run actions/attest@v4 in the build job against subject-path: 'dist/*', adding attestations: write permissions.

The composite build action removes the package-hashes output and the sha256sum hashing step, and the workflows remove related job outputs and switch dry-run checks to format('{0}', inputs.dry_run) == 'false'.

Docs in PROVENANCE.md are updated to verify provenance via gh attestation verify (no .intoto.jsonl download), and release-please-config.json is reformatted only.

Written by Cursor Bugbot for commit 9f48de6. This will update automatically on new commits. Configure here.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@cursor review

@devin-ai-integration devin-ai-integration bot added the devin-pr Pull request created by Devin AI label Mar 31, 2026
Since actions/attest@v4 stores attestations via GitHub's attestation API
(not as release assets), repos that only use attestation don't need draft
releases. Release-please can publish the release directly.

Changes:
- Remove draft:true from release-please-config.json
- Remove create-tag job/steps (force-tag-creation handles this)
- Remove publish-release job (release is published directly)
- Remove publish_release input from manual workflows
@devin-ai-integration devin-ai-integration bot changed the title ci: use draft releases to support immutable GitHub releases ci: switch to actions/attest and add force-tag-creation Mar 31, 2026
force-tag-creation only operates in conjunction with draft releases.
Since this repo does not use draft releases (attestation-only, no
artifact uploads to the release), force-tag-creation is not needed.
@devin-ai-integration devin-ai-integration bot changed the title ci: switch to actions/attest and add force-tag-creation ci: switch from SLSA provenance to actions/attest Mar 31, 2026
The attest step was already guarded, but the checksums file generation
was not. Now both steps are skipped during dry runs.
@devin-ai-integration devin-ai-integration bot changed the title ci: switch from SLSA provenance to actions/attest ci: switch from SLSA provenance to actions/attest with subject-path Mar 31, 2026
@kinyoklion kinyoklion marked this pull request as ready for review April 1, 2026 17:44
@kinyoklion kinyoklion requested a review from a team as a code owner April 1, 2026 17:44
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

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

Labels

devin-pr Pull request created by Devin AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant