-
-
Notifications
You must be signed in to change notification settings - Fork 104
Use twine's built-in Trusted Publishing support #360
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
base: unstable/v1
Are you sure you want to change the base?
Use twine's built-in Trusted Publishing support #360
Conversation
Nice, thanks @facutuesca! I'll do a full review in a bit. |
dfb8951
to
f2d4b3e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks @facutuesca!
I'm going to wait until pypa/twine#1249 gets released (twine 6.1.1?) cc @woodruffw perhaps you could make that happen? |
Yeah, I'm OK with a release -- I'll prep one tomorrow and see if Ian's okay with one 🙂 |
I've opened pypa/twine#1264 for the twine release. |
# The top-level error message that gets rendered. | ||
# This message wraps one of the other templates/messages defined below. | ||
_ERROR_SUMMARY_MESSAGE = """ | ||
Trusted publishing exchange failure: | ||
{message} | ||
You're seeing this because the action wasn't given the inputs needed to | ||
perform password-based or token-based authentication. If you intended to | ||
perform one of those authentication methods instead of trusted | ||
publishing, then you should double-check your secret configuration and variable | ||
names. | ||
Read more about trusted publishers at https://docs.pypi.org/trusted-publishers/ | ||
Read more about how this action uses trusted publishers at | ||
https://github.com/marketplace/actions/pypi-publish#trusted-publishing | ||
""" | ||
|
||
# Rendered if OIDC identity token retrieval fails for any reason. | ||
_TOKEN_RETRIEVAL_FAILED_MESSAGE = """ | ||
OpenID Connect token retrieval failed: {identity_error} | ||
This generally indicates a workflow configuration error, such as insufficient | ||
permissions. Make sure that your workflow has `id-token: write` configured | ||
at the job level, e.g.: | ||
```yaml | ||
permissions: | ||
id-token: write | ||
``` | ||
Learn more at https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings. | ||
""" # noqa: S105; not a password | ||
|
||
# Specialization of the token retrieval failure case, when we know that | ||
# the failure cause is use within a third-party PR. | ||
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE = """ | ||
OpenID Connect token retrieval failed: {identity_error} | ||
The workflow context indicates that this action was called from a | ||
pull request on a fork. GitHub doesn't give these workflows OIDC permissions, | ||
even if `id-token: write` is explicitly configured. | ||
To fix this, change your publishing workflow to use an event that | ||
forks of your repository cannot trigger (such as tag or release | ||
creation, or a manually triggered workflow dispatch). | ||
""" # noqa: S105; not a password | ||
|
||
# Rendered if the package index refuses the given OIDC token. | ||
_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE = """ | ||
Token request failed: the server refused the request for the following reasons: | ||
{reasons} | ||
This generally indicates a trusted publisher configuration error, but could | ||
also indicate an internal error on GitHub or PyPI's part. | ||
{rendered_claims} | ||
""" # noqa: S105; not a password | ||
|
||
_RENDERED_CLAIMS = """ | ||
The claims rendered below are **for debugging purposes only**. You should **not** | ||
use them to configure a trusted publisher unless they already match your expectations. | ||
If a claim is not present in the claim set, then it is rendered as `MISSING`. | ||
* `sub`: `{sub}` | ||
* `repository`: `{repository}` | ||
* `repository_owner`: `{repository_owner}` | ||
* `repository_owner_id`: `{repository_owner_id}` | ||
* `workflow_ref`: `{workflow_ref}` | ||
* `job_workflow_ref`: `{job_workflow_ref}` | ||
* `ref`: `{ref}` | ||
* `environment`: `{environment}` | ||
See https://docs.pypi.org/trusted-publishers/troubleshooting/ for more help. | ||
""" | ||
|
||
# Rendered if the package index's token response isn't valid JSON. | ||
_SERVER_TOKEN_RESPONSE_MALFORMED_JSON = """ | ||
Token request failed: the index produced an unexpected | ||
{status_code} response. | ||
This strongly suggests a server configuration or downtime issue; wait | ||
a few minutes and try again. | ||
You can monitor PyPI's status here: https://status.python.org/ | ||
""" # noqa: S105; not a password | ||
|
||
# Rendered if the package index's token response isn't a valid API token payload. | ||
_SERVER_TOKEN_RESPONSE_MALFORMED_MESSAGE = """ | ||
Token response error: the index gave us an invalid response. | ||
This strongly suggests a server configuration or downtime issue; wait | ||
a few minutes and try again. | ||
""" # noqa: S105; not a password |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@facutuesca could you confirm we're not loosing all this troubleshooting detail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The troubleshooting details are lost. I checked, and here's what each error looks like now when failing from twine:
_TOKEN_RETRIEVAL_FAILED_MESSAGE
-> "Unable to retrieve an OIDC token from the CI platform for trusted publishing {e}" (src)_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE
-> Same as above_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE
-> "The token request failed; the index server gave the following reasons:\n\n{reasons}" (src)_SERVER_TOKEN_RESPONSE_MALFORMED_JSON
-> "The token-minting request returned invalid JSON" (src)_SERVER_TOKEN_RESPONSE_MALFORMED_MESSAGE
-> "Expected a trusted publishing token but got None" (src)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to find a way to keep this in. Twine might be providing generic info but we need to be more precise, specific to GHA.
# and also uploading via Trusted Publishing | ||
twine >= 6.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@facutuesca time to bump to 6.2
# NOTE: Used to detect an ambient OIDC credential for OIDC publishing, | ||
# NOTE: as well as PEP 740 attestations. | ||
id ~= 1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@facutuesca upon rebase, you'll probably need to remove the type stub that #381 added and drop this dep from the pre-commit config.
@woodruffw any chance you could resurrect the Twine API effort? |
As an intermediate solution, would having Twine exit with different status codes depending on the error be possible/desirable? We could disambiguate the error type from the exit code and print the corresponding troubleshooting message (including Twine's stderr output) |
This PR removes the OIDC token exchange logic, replacing it with
twine
's built-in support for Trusted Publishing present in version6.1.0
.In practice, this just means running
twine
without setting theTWINE_PASSWORD
env variable whenever we detect the Trusted Publishing flow.cc @woodruffw