Use twine's built-in Trusted Publishing support#360
Use twine's built-in Trusted Publishing support#360facutuesca wants to merge 1 commit intopypa:unstable/v1from
Conversation
|
Nice, thanks @facutuesca! I'll do a full review in a bit. |
dfb8951 to
f2d4b3e
Compare
|
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.
@facutuesca could you confirm we're not loosing all this troubleshooting detail?
There was a problem hiding this comment.
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.
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 |
| # 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.
@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
twinewithout setting theTWINE_PASSWORDenv variable whenever we detect the Trusted Publishing flow.cc @woodruffw