Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose PEP 740 attestations functionality #236

Open
wants to merge 37 commits into
base: unstable/v1
Choose a base branch
from

Conversation

woodruffw
Copy link
Member

@woodruffw woodruffw commented May 2, 2024

WIP, still experimenting here. Not ready for review 🙂

This adds PEP 740 attestation generation to the workflow: when the Trusted Publishing flow is used, this will generate a publish attestation for each distribution being uploaded. These generated attestations are then fed into twine, which newly supports them via --attestations.

Breakdown:

  • Generate attestations for each dist
  • Actually hook up twine --attestations
  • Documentation

See: pypi/warehouse#15871

@woodruffw
Copy link
Member Author

I've confirmed that the basic version of this works as expected (attestations is currently ignored by PyPI and TestPyPI): https://github.com/woodruffw-experiments/gha-pep740-experiments/actions/runs/9008318313/job/24750093954

@webknjaz
Copy link
Member

@woodruffw I just bumped Twine FYI. And pre-commit is fine now. Rebasing should get the blockers out of the way.

@woodruffw
Copy link
Member Author

@woodruffw I just bumped Twine FYI. And pre-commit is fine now. Rebasing should get the blockers out of the way.

Good timing, so did @facutuesca 😅

Signed-off-by: William Woodruff <william@trailofbits.com>
@facutuesca
Copy link

I'm squashing and rebasing this branch now

Signed-off-by: Facundo Tuesca <facundo.tuesca@trailofbits.com>
@facutuesca
Copy link

@woodruffw @webknjaz The remaining lint failure is due to an error message string (_TOKEN_RETRIEVAL_FAILED_MESSAGE) being duplicated in attestations.py and oidc-exchange.py.
Would you prefer de-duplicating it by moving it into a new common.py file, or another approach?

@woodruffw
Copy link
Member Author

Would you prefer de-duplicating it by moving it into a new common.py file, or another approach?

That's what I was thinking originally, although common.py won't be on sys.path so importing it won't be straightforward 🙁

I think the "clean" thing to do here would be to turn the Python files here into a project structure that gets installed as part of the Docker image's build. But that's a little heavyweight, so @webknjaz may have another idea 🙂

@webknjaz
Copy link
Member

I'm leaning towards just being available on $PYTHONPATH, not pip installed. The way I did it @ https://github.com/re-actors/alls-green/blob/223e4bb/action.yml#L50-L53. However, such a restructuring should be separate and would probably include a src/pypi_publish/__main__.py entrypoint invoking the current main script through subprocess as the first step. I don't want a huge PR since big ones tend to cause long reviews and merge delays. Another thing to do would be to start to use our own managed virtualenv in the container rather than installing the deps globally into the shared userspace.

@webknjaz webknjaz added the enhancement New feature or request label May 29, 2024
@webknjaz
Copy link
Member

@woodruffw
Copy link
Member Author

(Sorry, was on PTO -- catching up on mentions now)

@woodruffw hey, it looks like GitHub rolled out their own attestations in beta: https://github.com/actions/attest-build-provenance / https://github.com/pypa/gh-action-pypi-publish/attestations / https://github.com/orgs/community/discussions/122028. I wonder if we could somehow integrate with that...

Yep, I've been thinking about how to make integrate the two -- the last comment on the PEP discussion thread suggests an approach that would allow GitHub-generated attestations to be compatible with this PEP.

TL;DR: I think our options here are:

  1. Move forward with the current "version 1" specified in the PEP. This would mean no further changes are required on the PEP side. However, it would also mean that "version 1" isn't compatible with the default attestation format generated by GitHub, and that we would have to create a "version 2" that is compatible.
  2. Edit the PEP in its current form and make it compatible with GitHub's attestation format. This would be a small change to the PEP overall, but has knock-on consequences (e.g. conceptually unbounded JSON payloads being signed over).

@woodruffw
Copy link
Member Author

woodruffw commented Jun 10, 2024

Just to update here: I've begun updating PEP 740 to be compatible with what GitHub's artifact attestations feature is doing, and it looks like it'll be pretty smooth.

That then leaves the question of how to approach attestation generation here 🙂:

  1. Go forward with what's currently in this PR (a small script that uses the id-token permission + pypi_attestation_models to manually generate the attestation)
  2. Move to a more discrete/black-boxed approach, e.g. something like this:
steps:
  - name: attest
    # does not exist yet!
    uses: pypa/gh-action-pypi-attest
    with:
      # this would be the default; just for illustration
      packages-dir: dist/

  - name: publish
    uses: pypa/gh-action-pypi-publish@release/v1

The benefit to approach (2) is that it's more devolved and can be built up on top of actions/attest@v1. OTOH it requires users to add another workflow step and get the order right, versus doing it implicitly in this action. Thoughts @webknjaz?

Edit: for context, the reason we can't use actions/attest-build-provenance or actions/attest directly is because (1) we have our own custom predicate, and (2) our attestation format requires a transformation step from the output bundle produced by those actions, so we need to wrap them in a "composite" or similar action to get everything into the shape PyPI expects.

@ofek
Copy link
Sponsor

ofek commented Jun 11, 2024

Awesome stuff! I would much prefer option 1, we should prioritize UX.

@sethmlarson
Copy link

I agree with @ofek that ergonomics is the most important part here, requiring users add another action step means we will see delayed rollout of the feature compared to adding it on to the existing workflow.

@woodruffw
Copy link
Member Author

SG, I'll keep going with the current approach then 🙂

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw woodruffw marked this pull request as ready for review June 11, 2024 21:52
Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw
Copy link
Member Author

This is good for an initial review! To summarize:

  1. By default, nothing changes.
  2. If the user sets attestations: true and is in a Trusted Publishing flow, this workflow will now transparently generate a "PyPI Publish" attestation for them, for each distribution being uploaded.
  3. This attestation will be transparently stabled to each distribution, thanks to twine --attestations.

@sethmlarson
Copy link

@woodruffw Just double checking long-term strategy, is the attestations: true setting only there because this is an experimental feature and the goal is that one day a new version of the workflow will automatically do this without the attestations: true setting?

@woodruffw
Copy link
Member Author

Just double checking long-term strategy, is the attestations: true setting only there because this is an experimental feature and the goal is that one day a new version of the workflow will automatically do this without the attestations: true setting?

Yes, exactly -- my thinking is that it's already non-default due to the tail of people using older versions of this action, so having it be (temporarily) opt-in makes experimenting easier while giving us the ability to flip the switch once everything is stable.

@webknjaz
Copy link
Member

Sorry I didn't react earlier. I also think that UX is important. Not sure how well it plays with uploading attestations to GitHub, though.

Can we attempt relying on the attestations: write privilege instead of the action input? Can we feature-detect that it's enabled and use that as a flag? It feels like better ergonomics + potentially better integration with GH IIUC.

Another thought: why do we need to make attestations opt-in even? If it's supported in the Twine version and PyPI side is a no-op (still?), what's the harm in doing it unconditionally whenever we have OIDC privilege? If you're worried about crashes in our own code, we could just stick overly generic exception handling for the experimentation period.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw woodruffw requested a review from webknjaz July 9, 2024 19:38
Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw
Copy link
Member Author

@webknjaz This could use another round of review, when you have time!

(Please let me know if there's anything I can do to make the changes more tractable here -- I'm happy to try anything that'll make this easier to review 🙂)

@gaborbernat
Copy link

I'm happy to give this a try once I return to Los Angeles on the 22nd.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
README.md Show resolved Hide resolved
twine-upload.sh Outdated Show resolved Hide resolved
twine-upload.sh Outdated Show resolved Hide resolved
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
@gaborbernat
Copy link

I am happy to try this out with some of my projects, should that help 👍

@woodruffw
Copy link
Member Author

I am happy to try this out with some of my projects, should that help 👍

Feel free! It should be as easy as replacing the stable v1 ref with my fork + branch and enabling the attestations setting on the action.

(You may want to start with just TestPyPI, to avoid spamming the admins with alerts on production.)

@gaborbernat
Copy link

None of my projects officially use the test environment.😂

Signed-off-by: William Woodruff <william@trailofbits.com>
@woodruffw
Copy link
Member Author

Gentle ping for review 🙂 -- we're getting close to enabling attestation persistence on PyPI, so being able to roll this out (with the current toggle) will help us smoke test the functionality there.

Copy link
Member

@webknjaz webknjaz left a comment

Choose a reason for hiding this comment

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

I was hoping the refactoring PR would be completed by now, but it looks like that ain't happening... I suppose, this is a good patch to accept. Could you address a few comments below before I merge?

attestations.py Outdated Show resolved Hide resolved
attestations.py Outdated Show resolved Hide resolved
attestations.py Outdated Show resolved Hide resolved
requirements/runtime.in Outdated Show resolved Hide resolved
twine-upload.sh Outdated Show resolved Hide resolved
woodruffw and others added 4 commits August 21, 2024 11:55
Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua>
Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua>
...to make the linters happy.

Signed-off-by: William Woodruff <william@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants