Skip to content

Test uv+PyPI Trusted Publishing on Gitlab#17443

Merged
woodruffw merged 2 commits intomainfrom
ww/pypi-tp-gl-test
Jan 15, 2026
Merged

Test uv+PyPI Trusted Publishing on Gitlab#17443
woodruffw merged 2 commits intomainfrom
ww/pypi-tp-gl-test

Conversation

@woodruffw
Copy link
Member

@woodruffw woodruffw commented Jan 13, 2026

Summary

WIP.

The basic idea here is to invoke a Gitlab Pipeline via GitHub. That pipeline generates an OIDC token which it saves as an artifact, which the GitHub workflow can then read. We then use that OIDC token to impersonate the identity of the Gitlab Pipeline for Trusted Publishing purposes.

Guinea pig project: https://test.pypi.org/project/astral-test-pypi-trusted-publishing-gitlab/

See #17438 for motivating context.

Test Plan

Implement the above within test_publish.py and ci.yml.

@woodruffw woodruffw self-assigned this Jan 13, 2026
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 19:19 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 19:27 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 19:33 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 19:46 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 19:57 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 20:07 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 20:19 — with GitHub Actions Inactive
@woodruffw woodruffw temporarily deployed to uv-test-registries January 13, 2026 20:32 — with GitHub Actions Inactive
Signed-off-by: William Woodruff <william@astral.sh>

disable attestations for GitLab TP test

Signed-off-by: William Woodruff <william@astral.sh>
Signed-off-by: William Woodruff <william@astral.sh>
Copy link
Member Author

Choose a reason for hiding this comment

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

Apologies for the churn in this file -- I realized that I was having trouble tracking the state through the different sub-tests, so what I've done is add a "plan" abstraction that pre-plans some test state. We can then pass that state around consistently via a Plan object rather than sharing it piecemeal.

Comment on lines +235 to +244
"pypi-trusted-publishing-gitlab": TargetConfiguration(
"astral-test-pypi-trusted-publishing-gitlab",
publish_url=TEST_PYPI_PUBLISH_URL,
index_url="https://test.pypi.org/simple/",
index=None,
# We're impersonating GitLab, so we can't easily test attestations here.
# TODO: In principle we could test this by having GitLab issue us an `aud:sigstore`
# OIDC token in addition to the `aud:testpypi` one.
attestations=False,
),
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 the main operative change 🙂

Comment on lines +561 to +562
if plan.target in ("pypi-trusted-publishing-gitlab",):
return
Copy link
Member Author

Choose a reason for hiding this comment

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

Flagging: the nuance here is that GitLab, unlike GitHub and other TP providers, doesn't have an "active" OIDC source: the runner gets a single OIDC credential at startup. As a result of that PyPI (and pyx) consider the OIDC credential "spent" after its first use, meaning that our duplicate/conflict/etc. tests all fail for an unrelated reason (the index is rejecting our credential as reused).

There's no great way around this, it's seemingly an architectural limitation of GitLab. But the good news is that it won't affect typical user workflows, it only dings us here because we're intentionally re-using an OIDC credential across separate uv publish invocations 🙂

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 true for the other tests below as well.)

Copy link
Member

@zsol zsol Jan 14, 2026

Choose a reason for hiding this comment

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

Would this mean that two subsequent uv publish commands in the same job in a user's workflow fail the same way? If so, that's quite a severe limitation

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, exactly. There's ways we could potentially work around that within uv itself (e.g. we could stash the minted credential for reuse between invocations), but the limitation on OIDC token reuse is architectural/inside PyPI.

In practice this doesn't generally bite users because all they do is a single uv publish invocation, plus must CI providers don't have this limitation (GitHub and all the others can acquire additional OIDC creds at runtime.)

packages-dir: "astral-test-pypa-gh-action/dist"

- name: "Request GitLab OIDC token for impersonation"
uses: digital-blueprint/gitlab-pipeline-trigger-action@20e77989b24af658ba138a0aa5291bdc657f1505 # v1.3.0
Copy link
Member Author

Choose a reason for hiding this comment

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

Flagging: this adds ~30s of sync runtime to the publishing tests, since this action needs to block while the Gitlab pipeline completes. That's not ideal, but the silver lining is that it'll be amortized as we add more Gitlab publishing tests (e.g. for pyx too), since we can use the same pipeline invocation to generate multiple ID tokens.

Copy link
Member Author

Choose a reason for hiding this comment

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

(Also noting that I reviewed this action's source. I considered trying to open-code this with GitLab's glab CLI instead, but this seemed simpler. We could always switch to that instead, though.)

@woodruffw woodruffw marked this pull request as ready for review January 13, 2026 22:19
@woodruffw woodruffw requested review from konstin, zanieb and zsol January 13, 2026 22:19
@woodruffw woodruffw added the internal A refactor or improvement that is not user-facing label Jan 13, 2026
Copy link
Member

@zsol zsol left a comment

Choose a reason for hiding this comment

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

This looks ok to me, but maybe it would be slightly better if we would have a separate, parallel job for the non-github trusted publishing tests, so if they do fail at the point of getting the oidc token the rest of the tests would still run

@woodruffw
Copy link
Member Author

so if they do fail at the point of getting the oidc token the rest of the tests would still run

Yeah, I was thinking about either sharding this across jobs with a matrix or changing it to use a more "traditional" pytest setup, so that we could run the entire suite without failing fast. I can look at that with a follow up.

@woodruffw woodruffw merged commit b3b1556 into main Jan 15, 2026
100 checks passed
@woodruffw woodruffw deleted the ww/pypi-tp-gl-test branch January 15, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal A refactor or improvement that is not user-facing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants