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

Initial implementation of uploading with trusted publishing authentication #1194

Merged
merged 12 commits into from
Dec 11, 2024

Conversation

takluyver
Copy link
Member

Fixes #999

This is quite rough - I suspect it needs some better error messages at least. But I figured I'd open it straight away to get comments on the overall structure. Does it make sense to have this logic in the auth.Resolver class, or should it be somewhere else?

Copy link
Member

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

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

Some early comments but I agree this belongs in the Resolver

twine/auth.py Outdated Show resolved Hide resolved
pyproject.toml Outdated
@@ -62,6 +62,7 @@ register = "twine.commands.register:main"

[project.optional-dependencies]
keyring = ["keyring >= 15.1"]
oidc = ["id"]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
oidc = ["id"]
trusted-publishing = ["id"]

twine/auth.py Outdated Show resolved Hide resolved
twine/auth.py Outdated Show resolved Hide resolved
twine/auth.py Outdated

token_exchange_url = f"https://{repository_domain}/_/oidc/mint-token"

mint_token_resp = requests.post(
Copy link
Member

Choose a reason for hiding this comment

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

Using a session would be ideal. Better yet if we can share it with a Repository to get connection pooling and reuse for performance

Copy link
Member Author

Choose a reason for hiding this comment

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

This is happening before we construct Repository. I could make it so a session is made beforehand and passed in, but the Repository constructor modifies the session object, so that's a bit messy.

Do you think that's worth dealing with, or are you happy to have two sessions? Putting the two requests here in a session should be easy enough.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I know the repository manages the session today. We could move that and the customization somewhere else. The repository manages it today because it was the only place to need a session. I'm not certain we need to share them, but it might be worth the effort.

twine/settings.py Outdated Show resolved Hide resolved
twine/settings.py Outdated Show resolved Hide resolved
pyproject.toml Outdated Show resolved Hide resolved
@takluyver
Copy link
Member Author

I've started reworking it without the explicit flag, as discussed, but I'm aware I still need to do the fallback to prompting for a token.

@takluyver
Copy link
Member Author

I've made the fallback relatively narrow - only if we're not on a supported platform for trusted publishing - but demoted trusted publishing so a token in keyring or in ~/.pypirc has higher priority. So the only case left for the fallback is the interactive prompt, which is unlikely to be used on CI platforms.

@webknjaz
Copy link
Member

webknjaz commented Dec 5, 2024

I've made the fallback relatively narrow - only if we're not on a supported platform for trusted publishing - but demoted trusted publishing so a token in keyring or in ~/.pypirc has higher priority. So the only case left for the fallback is the interactive prompt, which is unlikely to be used on CI platforms.

@takluyver @woodruffw do you know if this would impact pypi-publish if we were to replace the autodetection logic with the new one in twine? Or would it not make sense unless #194 gets solved?

@woodruffw
Copy link
Member

@takluyver @woodruffw do you know if this would impact pypi-publish if we were to replace the autodetection logic with the new one in twine? Or would it not make sense unless #194 gets solved?

I think it would indeed affect pypi-publish; I think ideally what would happen here is pypi-publish could use the behavior here directly.

I have no strong opinions on whether it makes sense to block that on #194 or not 🙂 -- I figure pypi-publish is already using twine as a CLI so it wouldn't hurt to keep doing so.

@sigmavirus24
Copy link
Member

One other note, please be sure to update our --version handling to include id and it's version string. Should be as easy as adding a string to a list

Copy link
Member

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

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

I think I'd be happy approving this as is right now. I'm not sure what other testing you've done for it though

twine/cli.py Show resolved Hide resolved
tox.ini Outdated Show resolved Hide resolved
@takluyver
Copy link
Member Author

So far I've not actually tested this at all. I'll do some manual testing before it's merged by setting up a project to upload to TestPyPI.

How much of this is practical to test in twine's CI? Obviously I could mock out all the responses, but I'm never sure if a test like that is really worth much.

@takluyver
Copy link
Member Author

Manual testing:

In between, I hit a separate error uploading (bad filetype), which #1198 should fix.

@takluyver takluyver marked this pull request as ready for review December 7, 2024 20:59
@takluyver
Copy link
Member Author

One drawback I noticed of putting this below keyring in the priority order is that you get quite a long warning (including a traceback) to say that there's no keyring set up before it uses trusted publishing.

I'm roughly imagining that someone has a CI set up where keyring retrieves a token for PyPI (or another index) from some sort of secret manager. So I want that to still have precedence over trusted publishing, as another way to manually provide a token to twine. But maybe NoKeyringError, when there's no backend set up, should produce a smaller, less error-ish message?

Copy link
Member

@sigmavirus24 sigmavirus24 left a comment

Choose a reason for hiding this comment

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

LGTM. Would like @woodruffw or @di to give it a once over if they have time too as they know the PyPI/action side better than me

@woodruffw
Copy link
Member

LGTM. Would like @woodruffw or @di to give it a once over if they have time too as they know the PyPI/action side better than me

I'll do a full review tomorrow!

@di
Copy link
Member

di commented Dec 7, 2024

Definitely can review on Monday!

@takluyver
Copy link
Member Author

Thanks. 🙂

I've done as I mentioned yesterday and made a much smaller message on NoKeyringError. I realised there's already a brief info-level message if keyring can't be imported, and I think the case where it's installed but has no backend is quite similar to that.

@woodruffw
Copy link
Member

Thanks @takluyver, this approach looks great to me! I'm going to try and do a TestPyPI deployment with this branch in a moment, to confirm that it works as well 🙂

@woodruffw
Copy link
Member

woodruffw commented Dec 9, 2024

Looks like the token exchange worked, although TestPyPI itself errored with a 400 for the actual upload (leaving the release created but empty): https://github.com/woodruffw-experiments/test-twine-tp/actions/runs/12242554831/job/34150133019

From the logs on TestPyPI:

Screenshot 2024-12-09 at 2 10 12 PM

@takluyver
Copy link
Member Author

I think that's the issue same issue I ran into & fixed in #1198. There's a branch in my fork called oidc-debug-filetype which includes that fix on top of this branch; that's what I tested with.

@woodruffw
Copy link
Member

I think that's the issue same issue I ran into & fixed in #1198. There's a branch in my fork called oidc-debug-filetype which includes that fix on top of this branch; that's what I tested with.

🤦 I totally neglected to check whether this tip was behind main:HEAD. That explains it!

@woodruffw
Copy link
Member

Copy link
Member

@woodruffw woodruffw left a comment

Choose a reason for hiding this comment

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

LGTM, thanks a ton @takluyver!

Copy link
Member

@di di left a comment

Choose a reason for hiding this comment

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

LGTM aside from two nits!

twine/auth.py Show resolved Hide resolved
twine/auth.py Outdated Show resolved Hide resolved
@sigmavirus24
Copy link
Member

I think that's three approvals and a successful test run, so I'm meeting this

@sigmavirus24 sigmavirus24 merged commit 28e60bb into pypa:main Dec 11, 2024
26 checks passed
@takluyver takluyver deleted the oidc branch December 11, 2024 09:09
@takluyver
Copy link
Member Author

Thanks all!

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.

Support the "Trusted Publishing" flow directly in twine
5 participants