Compare commits

...

175 Commits

Author SHA1 Message Date
Sviatoslav Sydorenko e0449d218c 🧪 Integrate a unified alls-green GHA status 2025-01-24 03:30:02 +01:00
Sviatoslav Sydorenko cebc64f283 🧪 Bump setuptools in smoke test to v75.8.0
Previously GitHub updated their `ubuntu-latest` images to use Ubuntu
24.04 which has Python 3.12 as the default interpreter. Before that,
it was Ubuntu 22.04 with Python 3.9. This caused an uncontrolled
runtime bump which led to an incompatibility discovery — older
versions of `setuptools` are incompatible with Python 3.12.

This bumps the `setuptools` version following the previous commit
da900af963 that pins the distro version.
Going forward, these two must be bumped in tandem to avoid situations
when one gets upgraded suddenly but the other doesn't.
2025-01-24 03:23:47 +01:00
Sviatoslav Sydorenko da900af963 🧪 Run smoke tests against Ubuntu 24 and 22
They are pinned instead of using `-latest` in the interest of better
reproducibility in the CI.
2025-01-24 03:23:24 +01:00
Sviatoslav Sydorenko 8cafb5c2bf 💰 Sync the funding config 2024-12-23 02:29:15 +01:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) 916e57631f Merge pull request #315 from webknjaz/refactoring/attestations-exist-bundle
💅 Bundle attestation existence check together
2024-12-10 19:25:21 +01:00
Sviatoslav Sydorenko daa899706d 📝 Add a GH Sponsors badge 2024-12-10 02:15:24 +01:00
Sviatoslav Sydorenko 72d1032bb0 💅 Bundle attestation existence check together
This patch moves said check out of the signing loop and performs the
check early in the process. It is then able to report multiple
problems in a single error.
2024-12-10 01:52:29 +01:00
Sviatoslav Sydorenko 88a4d039d1 📝💅 Add a PyPA badge to README 2024-12-10 01:47:47 +01:00
Sviatoslav Sydorenko 674c7c85f0 📝 Fix s/PyPA/PyPUG/ typo on the badge 2024-12-10 01:47:15 +01:00
Sviatoslav Sydorenko 03e1883a77 💅📝 Add a tutorial badge to README 2024-12-10 01:36:55 +01:00
Sviatoslav Sydorenko 97583d9694 🧪 Allow 8 module members @ flake8 rule 2024-12-10 01:36:36 +01:00
Sviatoslav Sydorenko fe7e9df44b 🧪 Disable WPS318 @ flake8 2024-12-10 01:36:15 +01:00
Sviatoslav Sydorenko f14df0bb20 💅 Add a return type to die() @ attestations 2024-12-10 01:35:33 +01:00
Sviatoslav Sydorenko 67339c736f 📦 Only keep lower bounds @ input requirements
This concerns both direct (`twine`) and indirect (`pkginfo`) deps,
provided there's no broken versions to exclude.
2024-12-09 15:07:39 +01:00
Sviatoslav Sydorenko cbd6d01d85 📝Fix a typo in "privileges" @ README 2024-12-07 05:17:14 +01:00
Sviatoslav Sydorenko 7252a9a09c 📝 Outline unsupported scenarios in README 2024-12-07 05:13:12 +01:00
Sviatoslav Sydorenko a536fa9505 📌📦 Include jeepney & secretstorage pins
It appears these have been missed when updating `cryptography`. This
is probably dependabot's fault.
2024-12-07 02:25:27 +01:00
Sviatoslav Sydorenko 43caae4bb1 💅📦 Split transitive dep constraints
This is a structural change allowing for better placement of direct
dependencies and limiting the transitive ones.
2024-12-07 02:24:42 +01:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) f371c3d566 Merge pull request #313 from webknjaz/maintenance/metadata-2.4
This patch adds support for uploading dists with metadata v2.4 through bumping the transitive dependency `pkgutil` to v1.12 to enable support for validating metadata v2.4 in Twine. It also integrates a Maturin-based package into the smoke test in CI as a regression check.

Closes #312
Resolves #311
Resolves #310
2024-12-06 19:53:07 +01:00
William Woodruff 138a1215a3 📌📦 Pin pkginfo to v1.12 @ runtime deps
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-12-06 19:35:56 +01:00
Sviatoslav Sydorenko ff2b051b0a 🧪 Add a Maturin-based package to CI 2024-12-06 19:35:46 +01:00
Sviatoslav Sydorenko 0a0a6ae824 🧪 Allow CI to register multiple distributions
This is necessary to allow the smoke test check uploading multiple
packages.
2024-12-06 19:35:41 +01:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) e7723a410e Merge pull request #309 from trail-of-forks/ww/bumptwine
requirements: bump twine to ~= 6.0
2024-12-04 13:01:05 +01:00
William Woodruff 0e10725395 requirements: bump twine to ~= 6.0
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-12-01 12:05:46 -05:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) 218af422c0 Merge pull request #305 from trail-of-forks/ww/debug-workflow-ref 2024-11-24 03:01:28 +01:00
William Woodruff 7c5c585c36 oidc-exchange: add workflow_ref to debug msg
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-11-22 12:58:46 -05:00
🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) 93e87954aa Merge pull request #301 from br3ndonland/ghcr-sha 2024-11-15 04:22:10 +01:00
Brendon Smith f81cd95ad9 Tag Docker images with Git SHA
PR https://github.com/pypa/gh-action-pypi-publish/pull/230 updated the
action to pull Docker images from GHCR instead of building Docker images
each time the workflow runs. As part of this PR, a new GitHub Actions
workflow was added that builds Docker images and pushes them to GitHub
Container Registry (GHCR).

Actions can be referenced in various ways. The Docker build workflow
covers most of the action references, but does not push Docker images
tagged with the Git commit ID (Git SHA).

This commit will add Docker tags for referencing the action with a Git
SHA. GitHub Actions only supports references by the full 40 character
SHA. If users try to reference the action by a short SHA like `1234567`,
they will get an error like, "Unable to resolve action
`pypa/gh-action-pypi-publish@1234567`, the provided ref `1234567` is the
shortened version of a commit SHA, which is not supported. Please use
the full commit SHA `1234567890123456789012345678901234567890` instead."

https://github.com/pypa/gh-action-pypi-publish/pull/230
https://github.com/pypa/gh-action-pypi-publish/issues/290
https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow#using-shas
2024-11-11 18:58:36 -05:00
Sviatoslav Sydorenko (Святослав Сидоренко) 15c56dba36 Merge pull request #297 from trail-of-forks/ww/bump-pypi-attestations
requirements: bump pypi-attestations to 0.0.15
2024-11-07 00:00:24 +01:00
William Woodruff fe8d1484ba requirements: bump pypi-attestations to 0.0.15
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-11-06 17:53:10 -05:00
Sviatoslav Sydorenko (Святослав Сидоренко) 1f5d4ec244 Merge pull request #295 from trail-of-forks/ww/fix-sdist-collection 2024-11-06 20:01:10 +01:00
William Woodruff fec2f0c0ce attestations: collect *.zip sdists as well
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-11-06 13:43:44 -05:00
Sviatoslav Sydorenko (Святослав Сидоренко) a8b73a6d88 Merge pull request #294 from webknjaz/bugfixes/optional-python 2024-11-06 16:24:24 +01:00
Sviatoslav Sydorenko 9b4dfb0c84 Pre-install Python if there's none
This is not usually the case for GitHub-hosted Runners but it might
happen with self-hosted runners.

Fixes #289.
2024-11-06 16:20:12 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) 0a87186d5f Merge pull request #293 from webknjaz/bugfixes/uncheckout-intermediate-action 2024-11-06 15:50:37 +01:00
Sviatoslav Sydorenko dfcfeca43e 🧪 Use prefetched action to make trampoline
Previously, the action repository was being cloned from the remote
twice, unnecessarily. This patch eliminates this step and
uses the copy that was checked out on job start.

The generated trampoline action is still copied into the allowlisted
working directory so it can be referenced by the relative path
starting with `./`.

It is now output under
`./.github/.tmp/.generated-actions/run-pypi-publish-in-docker-container`
which mutates the end-user's workspace slightly but uses a path that
is unlikely to clash with somebody else's use.

Unfortunately, we cannot use randomized paths because the composite
action syntax does not allow accessing variables in `uses:`.

Fixes #292.
2024-11-06 15:47:43 +01:00
Sviatoslav Sydorenko 0d02f372c3 📝💅 Update the CI/CD badge in README
This is a follow-up for #230, which renamed the workflow filename.
2024-11-05 22:29:18 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) 61da13deb5 Merge pull request #230 from br3ndonland/ghcr
Build Docker image and push to GHCR
2024-11-05 20:58:36 +01:00
Brendon Smith 36965cb24a Run smoke tests before Docker builds
https://github.com/pypa/gh-action-pypi-publish/pull/230#discussion_r1787027821
2024-11-04 16:35:15 -05:00
Brendon Smith da554410b0 Move smoke test to reusable workflow 2024-11-04 16:35:14 -05:00
Brendon Smith 80b1d50e0d Make workflow_dispatch Docker tag input required
https://github.com/pypa/gh-action-pypi-publish/pull/230#discussion_r1759496153
2024-11-04 16:35:14 -05:00
pre-commit-ci[bot] 1b9f21a741 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-11-04 16:35:14 -05:00
Brendon Smith cfb9d93a26 Add Docker tags for major and minor versions 2024-11-04 16:35:14 -05:00
Brendon Smith 153ccde9bc Verify fail-fast in unsupported environments 2024-11-04 16:35:14 -05:00
Brendon Smith d03addb8e6 Drop args from create-docker-action.py
Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua>
2024-11-04 16:35:14 -05:00
Brendon Smith bacb62682c Fail-fast in unsupported environments
https://github.com/pypa/gh-action-pypi-publish/pull/230#discussion_r1632406604

Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua>
2024-11-04 16:35:14 -05:00
Brendon Smith 7ea8313fc2 Check repo ID instead of repo owner ID 2024-11-04 16:35:14 -05:00
Brendon Smith f51682fb52 Check repo owner ID instead of repo name 2024-11-04 16:35:14 -05:00
Brendon Smith a360fcb184 Dump action as JSON 2024-11-04 16:35:14 -05:00
Brendon Smith a869dd36b2 Checkout github.head_ref and repo for PRs
https://github.com/actions/checkout/issues/27#issuecomment-535897113
https://github.com/actions/checkout/issues/1108
2024-11-04 16:35:14 -05:00
Brendon Smith 5ded5310e7 Add workflow_dispatch trigger for Docker builds 2024-11-04 16:35:13 -05:00
Brendon Smith cf5ce177da Use YAML block strip syntax (>-) where possible 2024-11-04 16:35:13 -05:00
Brendon Smith f1f014b445 Reset pre-commit files: regex 2024-11-04 16:35:13 -05:00
Brendon Smith aed6c4b1b0 Generate Docker container action with Python 2024-11-04 16:35:13 -05:00
Brendon Smith 0d8d5059c8 Separate docker login and docker push
https://github.com/pypa/gh-action-pypi-publish/pull/230#discussion_r1578694138
2024-11-04 16:35:13 -05:00
Brendon Smith e453f8c630 Fix pre-commit errors 2024-11-04 16:35:13 -05:00
Brendon Smith 783267be69 Build Docker image and push to GHCR
Up to this point, the project has been set up as a Docker action
referencing the Dockerfile. The downside to using the Dockerfile for the
action is that the Docker image must be built every time the action is
used.

This commit will set up the project to build the Docker image and push
it to GitHub Container Registry (GHCR). This change will speed up user
workflows every time the action is used because the workflows will
simply pull the Docker image from GHCR instead of building again.

Changes:

- Add required metadata to Dockerfile
- Build container image with GitHub Actions
- Push container image to GHCR

Docker actions support pulling in pre-built Docker images. The downside
is that there's no way to specify the correct Docker tag because the
GitHub Actions `image` and `uses:` keys don't accept any context.
For example, if a user's workflow has
`uses: pypa/gh-action-pypi-publish@release/v1.8`, then the action should
pull in a Docker image built from the `release/v1.8` branch, something
like `ghcr.io/pypa/gh-action-pypi-publish:release-v1.8` (Docker tags
can't have `/`). The workaround is to switch the top-level `action.yml`
to a composite action that then calls the Docker action, substituting
the correct image name and tag.
2024-11-04 16:35:13 -05:00
Sviatoslav Sydorenko fb13cb3069 📝 Reflect the PR #277 changes in README
This makes minimum modifications to indicate that `attestations` is
not on by default.
2024-10-30 02:20:55 +01:00
Sviatoslav Sydorenko 72ead1a85a Merge PRs #276 and #277 into release/v1 2024-10-30 02:04:39 +01:00
William Woodruff 0126dcac8e action: enable attestations by default
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-10-28 14:31:58 -04:00
William Woodruff 335e8b00ae bump sigstore==3.5.1
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-10-28 14:29:41 -04:00
William Woodruff 1545e96dcb requirements: bump sigstore, pypi-attestations
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-10-22 12:40:04 -04:00
Sviatoslav Sydorenko (Святослав Сидоренко) f7600683ef Merge pull request #271 from mosfet80/patch-3
Update `actions/checkout` to v3 in self-tests
2024-09-29 11:06:37 +02:00
mosfet80 6edc294485 Fix node.js v16 deprecation self-smoke-test-action.yml
actions/checkout@v3 use node.js versio 16. But version 16 is deprecated.
version 4 fixes the problem.
2024-09-29 09:04:41 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 85a5a80b22 Merge pull request #270 from trail-of-forks/fix-magic-link-summary 2024-09-29 01:45:28 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 954318b48e Merge pull request #267 from mosfet80/patch-2 2024-09-29 01:38:05 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 24791c7774 Merge pull request #266 from mosfet80/patch-1 2024-09-29 01:37:58 +02:00
Facundo Tuesca d8c894824b Fix magic link nudge formatting in job summary 2024-09-27 20:47:50 +02:00
Facundo Tuesca a1ce3844ac Check for Trusted Publishing in magic link logic 2024-09-27 20:47:02 +02:00
mosfet80 00b87c80e8 Update check-jsonschema and pre-commit libs
https://github.com/python-jsonschema/check-jsonschema/releases

https://github.com/pre-commit/pre-commit-hooks/releases/tag/v4.6.0
2024-09-23 11:56:13 +02:00
mosfet80 a571f1e128 Update pylint lib
https://github.com/pylint-dev/pylint/releases/tag/v3.3.0
2024-09-23 11:52:50 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 897895f1e1 Merge pull request #262 from trail-of-forks/ww/bump-attestations-req
Resolves #263
2024-09-20 23:35:44 +02:00
William Woodruff ce32325c61 requirements: bump pypi-attestations to 0.0.12
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-09-19 18:14:50 +02:00
Facundo Tuesca 36978192ca Add nudge message with magic link to create new Trusted Publisher
PR #250

Co-authored-by: Sviatoslav Sydorenko <sviat@redhat.com>
2024-09-05 17:25:58 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 4f8925cefa Merge pull request #258 from facutuesca/patch-1 2024-09-05 17:06:25 +02:00
Facundo Tuesca a58e550ac2 Remove redundant Path.absolute() call 2024-09-03 16:21:03 +02:00
Sviatoslav Sydorenko 0ab0b79471 🚑 Invert the dists-to-attest validity check
This bug sneaked into #236 but should not affect many people as the
attestations generation feature is experimental and opt-in.

Fixes #256
2024-09-03 10:25:06 +02:00
William Woodruff 8a08d61689 Expose PEP 740 attestations functionality
PR #236

This patch 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`.

Ref: https://github.com/pypi/warehouse/issues/15871
2024-09-01 02:50:29 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) fb9fc6a4e6 Merge pull request #245 from trail-of-forks/ww/bump-twine 2024-06-27 19:55:19 +02:00
William Woodruff 4d020ff0a9 requirements: re-compile requirements with latest twine
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-06-24 16:49:50 -04:00
Sviatoslav Sydorenko ec4db0b4dd Merge PR #243 into unstable/v1 2024-06-16 20:09:43 +02:00
William Woodruff e7908444c6 oidc-exchange: link to status dashboard
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-06-11 17:49:43 -04:00
Sviatoslav Sydorenko 87b624f871 💅Update homepage @ Dockerfile to GH Marketplace 2024-05-29 22:25:10 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) da2f9bb91e Merge pull request #241 from br3ndonland/ghcr-label
Add Docker label for GHCR
2024-05-29 22:20:17 +02:00
Brendon Smith abbea2dd5c Add Docker label for GHCR
This commit will add the label `org.opencontainers.image.source` to the
Dockerfile. This label helps link GitHub Container Registry (GHCR) with
the associated repo.

https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
https://github.com/pypa/gh-action-pypi-publish/pull/230/files#r1603926630
2024-05-29 22:18:35 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 2734d07314 build(deps): bump requests from 2.31.0 to 2.32.0 in /requirements (#240)
build(deps): bump requests from 2.31.0 to 2.32.0 in /requirements
2024-05-29 16:37:07 +02:00
dependabot[bot] a54b9b8952 ---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 05:26:31 +00:00
Sviatoslav Sydorenko 699cd6103f ⇪📦 Bump the runtime dep lockfile 2024-05-16 17:50:20 +02:00
pre-commit-ci[bot] 8414fc2457 [pre-commit.ci] pre-commit autoupdate (#225)
* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/Lucas-C/pre-commit-hooks.git: v1.5.4 → v1.5.5](https://github.com/Lucas-C/pre-commit-hooks.git/compare/v1.5.4...v1.5.5)
- [github.com/python-jsonschema/check-jsonschema.git: 0.27.3 → 0.28.1](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.27.3...0.28.1)
- [github.com/adrienverge/yamllint.git: v1.33.0 → v1.35.1](https://github.com/adrienverge/yamllint.git/compare/v1.33.0...v1.35.1)
- [github.com/PyCQA/flake8.git: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8.git/compare/6.1.0...7.0.0)
- [github.com/PyCQA/flake8.git: 4.0.1 → 7.0.0](https://github.com/PyCQA/flake8.git/compare/4.0.1...7.0.0)
- [github.com/PyCQA/pylint.git: v3.0.3 → v3.1.0](https://github.com/PyCQA/pylint.git/compare/v3.0.3...v3.1.0)

* Bump WPS to v0.19.x series

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Merge separate flake8 runs back into one

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sviatoslav Sydorenko <sviat@redhat.com>
2024-05-16 15:39:26 +00:00
Peter Shen 67a07ebbed Disable the progress bar when running twine upload
PR #231
Resolves #229

Co-authored-by: Sviatoslav Sydorenko <webknjaz@redhat.com>
2024-05-16 17:14:58 +02:00
William Woodruff 771d60f44b Eliminate future tense in the password nudge in twine-upload
Additionally, this turns the corresponding code branch into a hard error in case of the regular PyPI.

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

PR #234
Fixes #233
2024-05-16 17:07:28 +02:00
Sviatoslav Sydorenko 04f4e64de3 Set Python 3.11 for the flake8-commas linter
It doesn't yet support 3.12 and is an unconditional dependency of WPS.
2024-05-16 16:29:54 +02:00
Sviatoslav Sydorenko (Святослав Сидоренко) 3fbcf7ccf4 Merge pull request #228 from pypa/dependabot/pip/requirements/idna-3.7
build(deps): bump idna from 3.6 to 3.7 in /requirements
2024-04-12 15:30:45 +02:00
dependabot[bot] 576aae3934 build(deps): bump idna from 3.6 to 3.7 in /requirements
Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-12 04:51:56 +00:00
Sviatoslav Sydorenko 81e9d935c8 Bump pip to v24.0 in runtime prerequisites lock 2024-03-08 00:20:54 +01:00
Sviatoslav Sydorenko 91527c4583 Regenerate lockfiles with pip-tools v7.4.1 2024-03-08 00:19:54 +01:00
Sviatoslav Sydorenko 3a817c6dce Bump action runtime to CPython 3.12 2024-03-08 00:15:38 +01:00
Sviatoslav Sydorenko 741947b9ca Add a config file for pip-tools 2024-03-07 23:43:48 +01:00
Sviatoslav Sydorenko d7af439579 Mass-bump transitive dependencies of runtime 2024-03-07 23:08:31 +01:00
Sviatoslav Sydorenko e90ddca975 Bump readme-renderer to v43.0 2024-03-07 23:07:33 +01:00
Sviatoslav Sydorenko dae7fa3e8d Bump Twine to v5.0.0 2024-03-07 23:05:40 +01:00
Sviatoslav Sydorenko 0fe04ae7d9 Bump id to v1.3.0 2024-03-07 23:04:40 +01:00
Sviatoslav Sydorenko 444e17980b Bump cryptography to v42.0.5 2024-03-07 23:02:36 +01:00
Sviatoslav Sydorenko 820be4e5e3 Normalize pip-tools' header comment @ runtime.txt
It's currently not prefixed with `requirements/` in most places and
that what Dependabot keeps using.
2024-03-07 23:00:46 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) aec4e82833 Merge pull request #219 from SigureMo/re-generate-requirements
build(deps): bump `pkginfo` version to support `Metadata-version=2.3`
2024-03-06 19:16:52 +01:00
SigureMo b065889f7f revert other bumps 2024-03-06 19:20:47 +08:00
SigureMo 00a7cd17a2 re-gen on Linux and run command in requirements/ 2024-03-06 01:59:27 +00:00
SigureMo 2972d54cda bump pkginfo only 2024-03-05 18:16:00 +08:00
SigureMo f6a1bcf881 Revert "build(deps): re-generate requirements to support Metadata-version=2.3"
This reverts commit e6ed2a4dfb.
2024-03-05 18:07:49 +08:00
SigureMo e6ed2a4dfb build(deps): re-generate requirements to support Metadata-version=2.3 2024-03-05 12:56:14 +08:00
William Woodruff e53eb8b103 Clarify the error during OIDC exchange on PRs from forks
This specializes the token retrieval error handling, providing an
alternative error message when the error cause is something
that we know can't possibly work due to GitHub's own restrictions
on PRs from forks.

PR #203
Closes #202
Ref https://github.com/python-pillow/Pillow/pull/7616

Co-authored-by: Sviatoslav Sydorenko <webknjaz@redhat.com>
2024-02-27 05:09:52 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) edfa8f355b Merge pull request #216 from xuanzhi33/unstable/v1
Correct the trusted publishing note admonition markdown syntax in the README
2024-02-24 20:27:48 +01:00
xuanzhi33 aeff019ac8 docs(fix): Fix a markdown alert 2024-02-24 18:46:07 +08:00
Sviatoslav Sydorenko (Святослав Сидоренко) 24c5d5ca4a Merge pull request #214 from pypa/dependabot/pip/requirements/cryptography-42.0.4
build(deps): bump cryptography from 42.0.2 to 42.0.4 in /requirements
2024-02-22 02:26:27 +01:00
dependabot[bot] c13b4aa8c5 build(deps): bump cryptography from 42.0.2 to 42.0.4 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.2 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.2...42.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 20:44:40 +00:00
Sviatoslav Sydorenko (Святослав Сидоренко) 72a79c870c Merge pull request #213 from pypa/dependabot/pip/requirements/cryptography-42.0.2
build(deps): bump cryptography from 42.0.0 to 42.0.2 in /requirements
2024-02-17 03:24:59 +01:00
dependabot[bot] 751e5b80a4 build(deps): bump cryptography from 42.0.0 to 42.0.2 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.0 to 42.0.2.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.0...42.0.2)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-17 00:58:14 +00:00
Sviatoslav Sydorenko (Святослав Сидоренко) 0580fcbb84 Merge pull request #210 from pypa/dependabot/pip/requirements/cryptography-42.0.0
build(deps): bump cryptography from 41.0.6 to 42.0.0 in /requirements
2024-02-08 05:04:39 +01:00
dependabot[bot] a524841e7b build(deps): bump cryptography from 41.0.6 to 42.0.0 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.6 to 42.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/41.0.6...42.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-06 03:03:07 +00:00
Sviatoslav Sydorenko (Святослав Сидоренко) 3f824c73d9 Merge pull request #204 from pypa/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2024-02-05 18:14:39 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) 013c017b41 Revert flake8 to v4.0.1 for WPS 2024-02-05 18:13:32 +01:00
pre-commit-ci[bot] a0620a4177 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/isort.git: 5.12.0 → 5.13.2](https://github.com/PyCQA/isort.git/compare/5.12.0...5.13.2)
- [github.com/python-jsonschema/check-jsonschema.git: 0.27.0 → 0.27.3](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.27.0...0.27.3)
- [github.com/pre-commit/pre-commit-hooks.git: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks.git/compare/v4.4.0...v4.5.0)
- [github.com/adrienverge/yamllint.git: v1.32.0 → v1.33.0](https://github.com/adrienverge/yamllint.git/compare/v1.32.0...v1.33.0)
- [github.com/PyCQA/flake8.git: 4.0.1 → 6.1.0](https://github.com/PyCQA/flake8.git/compare/4.0.1...6.1.0)
- [github.com/PyCQA/pylint.git: v3.0.0 → v3.0.3](https://github.com/PyCQA/pylint.git/compare/v3.0.0...v3.0.3)
2024-02-05 18:12:44 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) e82f99a47c Merge pull request #186 from virtuald/virtuald-patch-1
Mention in the docs that reusable workflows aren't supported right now
2024-02-05 18:12:13 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) e080e0073c Merge pull request #206 from trail-of-forks/ww/update-oidc-endpoint
This patch updates the PyPI API minting endpoint used uding the OIDC exchange process.
2024-02-05 17:59:15 +01:00
William Woodruff cd96453c9d oidc-exchange: update OIDC minting endpoint
Signed-off-by: William Woodruff <william@trailofbits.com>
2024-01-10 16:05:30 -05:00
Dustin Spicuzza 415d7a6bec Update README.md
Add suggested changes.
2023-12-20 15:11:12 +01:00
Dustin Spicuzza dea1d707f3 Update oidc-exchange.py
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2023-12-20 15:11:12 +01:00
Dustin Spicuzza a1a49954d3 Give more information to users
Reusable workflows don't work, and it's challenging to know that. Help the user out.
2023-12-20 15:11:12 +01:00
Sviatoslav Sydorenko (Святослав Сидоренко) c12cc61414 Merge pull request #196 from woodruffw-forks/ww/notice-to-debug
This replaces the use of `::notice` in each authentication case with `::debug`, reducing the user confusion caused by the these messages. It also simplifies the message in the Trusted Publishing case to make it less ambiguous.

Closes #192.
2023-12-20 12:12:06 +01:00
William Woodruff 674fb78567 twine-upload: replace notice with debug, simplify msgs 2023-12-04 20:27:16 -05:00
Sviatoslav Sydorenko 2f6f737ca5 Merge commit PR #184 into unstable/v1 2023-11-29 03:25:52 +01:00
Sviatoslav Sydorenko 2fa448ab0c Merge PRs #190, #184, #185, #189 and #194 into unstable/v1 2023-11-29 03:23:56 +01:00
Sviatoslav Sydorenko 824ad31786 Revert flake8 to v4.0.1 for WPS 2023-11-29 03:23:18 +01:00
dependabot[bot] 41f3f53c75 Bump cryptography from 41.0.3 to 41.0.6 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.3 to 41.0.6.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/41.0.3...41.0.6)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 23:56:20 +00:00
William Woodruff 2319287e0a twine-upload: ::error, switch nudge order
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-11-22 17:28:02 -05:00
William Woodruff 254a0d4ec4 twine-upload: add a nudge for password auth
Closes #187.
2023-11-05 23:53:52 -05:00
dependabot[bot] 70a33caeb9 Bump pip from 22.3.1 to 23.3 in /requirements
Bumps [pip](https://github.com/pypa/pip) from 22.3.1 to 23.3.
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/22.3.1...23.3)

---
updated-dependencies:
- dependency-name: pip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-02 21:42:46 +00:00
dependabot[bot] 102f507b75 Bump urllib3 from 2.0.6 to 2.0.7 in /requirements
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.6...2.0.7)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-17 21:02:57 +00:00
Sviatoslav Sydorenko 79739dc2f2 Merge pull request #183 from pypa/dependabot/pip/requirements/urllib3-2.0.6
Bump urllib3 from 2.0.3 to 2.0.6 in /requirements
2023-10-02 23:46:28 -04:00
pre-commit-ci[bot] 9a3f9ad5bc [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma.git: v3.0.0 → v3.1.0](https://github.com/asottile/add-trailing-comma.git/compare/v3.0.0...v3.1.0)
- [github.com/Lucas-C/pre-commit-hooks.git: v1.5.1 → v1.5.4](https://github.com/Lucas-C/pre-commit-hooks.git/compare/v1.5.1...v1.5.4)
- [github.com/python-jsonschema/check-jsonschema.git: 0.23.2 → 0.27.0](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.23.2...0.27.0)
- [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.2.5...v2.2.6)
- [github.com/PyCQA/flake8.git: 6.0.0 → 6.1.0](https://github.com/PyCQA/flake8.git/compare/6.0.0...6.1.0)
- [github.com/PyCQA/flake8.git: 4.0.1 → 6.1.0](https://github.com/PyCQA/flake8.git/compare/4.0.1...6.1.0)
- [github.com/PyCQA/pylint.git: v3.0.0a6 → v3.0.0](https://github.com/PyCQA/pylint.git/compare/v3.0.0a6...v3.0.0)
2023-10-03 00:40:18 +00:00
dependabot[bot] 75ca4c1f12 Bump urllib3 from 2.0.3 to 2.0.6 in /requirements
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.3 to 2.0.6.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.3...2.0.6)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 23:58:34 +00:00
Sviatoslav Sydorenko a712d989cc Make the vulnerability report URL direct 2023-09-11 16:40:56 +02:00
Sviatoslav Sydorenko bbf06d8ae3 Migrate security doc from RST to Markdown
RST files are no longer correctly recognized by GitHub.
2023-09-11 16:38:50 +02:00
Sviatoslav Sydorenko 8cdc2ab67c Merge pull request #179 from pypa/di-patch-1 2023-08-11 17:31:18 +02:00
Dustin Ingram 41c10ee223 Add link to configuration docs for Trusted Publishing 2023-08-11 11:23:40 -04:00
Sviatoslav Sydorenko b7f401de30 Merge PR #177 into unstable/v1 2023-08-10 22:58:37 +02:00
William Woodruff ba3ecc9355 oidc-exchange: fix padding
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-10 16:08:35 -04:00
Sviatoslav Sydorenko ade57f54dc Merge PRs #174 #175 and #172 into unstable/v1 2023-08-10 18:57:00 +02:00
William Woodruff 637917e5f2 README: re-add "pro tip" language
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 18:01:51 -04:00
William Woodruff 4864f13c38 README: use semantic callouts
See: https://github.com/orgs/community/discussions/16925

Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 17:58:56 -04:00
William Woodruff 326f9ad1e1 oidc-exchange: add-trailing-comma
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 15:17:18 -04:00
William Woodruff e5f0690e91 oidc-exchange: ignore a nested function
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 15:12:44 -04:00
William Woodruff 8bdd0cc2a0 oidc-exchange: lintage
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 15:10:56 -04:00
William Woodruff 71a0032909 oidc-exchange: render claims if exchange fails
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-08-09 15:08:47 -04:00
dependabot[bot] adef75a5a6 Bump cryptography from 41.0.2 to 41.0.3 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.2 to 41.0.3.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/41.0.2...41.0.3)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-02 02:15:59 +00:00
Sviatoslav Sydorenko 413a8d5d62 Merge pull request #171 from pypa/dependabot/pip/requirements/certifi-2023.7.22
Bump certifi from 2023.5.7 to 2023.7.22 in /requirements
2023-07-26 11:43:53 +02:00
dependabot[bot] c185b8ee4e Bump certifi from 2023.5.7 to 2023.7.22 in /requirements
Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.5.7 to 2023.7.22.
- [Commits](https://github.com/certifi/python-certifi/compare/2023.05.07...2023.07.22)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 23:36:57 +00:00
Sviatoslav Sydorenko 2a939dd49b 🎨📝 Link SHA pinning encouragement @ README
This article [[1]] describes security flows of using branches and
tags as an end-user. The commit is intended to educate them but not
force doing so if they don't want to.

[1]: https://julienrenaux.fr/2019/12/20/github-actions-security-risk/
2023-07-13 16:44:47 +02:00
Sviatoslav Sydorenko f8c70e705f Merge pull request #168 from pquentin/bump-dependencies 2023-07-12 02:46:40 +02:00
Sviatoslav Sydorenko 68276eb3e4 Merge pull request #167 from trail-of-forks/tob-nudge 2023-07-12 02:43:50 +02:00
Quentin Pradet a5d57af63c Bump runtime dependencies 2023-07-11 09:31:13 +04:00
William Woodruff e90e853e89 twine-upload: only nudge on PyPI-looking domains
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-07-10 12:11:56 -04:00
William Woodruff be695966b0 twine-upload: add a nudge for trusted publishing
Closes #164.

Signed-off-by: William Woodruff <william@trailofbits.com>
2023-07-10 11:44:56 -04:00
Sviatoslav Sydorenko 54d67ed3c5 Merge pull request #165 from pypa/pre-commit-ci-update-config 2023-07-09 14:55:23 +02:00
Sviatoslav Sydorenko d32e2fab32 Revert flake8 to v4.0.1 2023-07-09 14:53:38 +02:00
pre-commit-ci[bot] a8d92e9876 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma.git: v2.4.0 → v3.0.0](https://github.com/asottile/add-trailing-comma.git/compare/v2.4.0...v3.0.0)
- [github.com/python-jsonschema/check-jsonschema.git: 0.22.0 → 0.23.2](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.22.0...0.23.2)
- [github.com/codespell-project/codespell: v2.2.4 → v2.2.5](https://github.com/codespell-project/codespell/compare/v2.2.4...v2.2.5)
- [github.com/adrienverge/yamllint.git: v1.30.0 → v1.32.0](https://github.com/adrienverge/yamllint.git/compare/v1.30.0...v1.32.0)
- [github.com/PyCQA/flake8.git: 4.0.1 → 6.0.0](https://github.com/PyCQA/flake8.git/compare/4.0.1...6.0.0)
2023-07-03 22:49:42 +00:00
Sviatoslav Sydorenko f5622bde02 Merge PRs #159 and #160 into unstable/v1 2023-06-26 18:18:24 +02:00
Sviatoslav Sydorenko 3be882c473 Merge pull request #161 from jaap3/jaap3-patch-1
This patch remove extraneous trailing `}` from the annotation note.
2023-06-08 16:22:18 +02:00
Jaap Roes 775be49481 Remove extraneous } 2023-06-08 14:56:32 +02:00
dependabot[bot] 5684530096 Bump cryptography from 39.0.1 to 41.0.0 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 39.0.1 to 41.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/39.0.1...41.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-02 20:16:33 +00:00
Hugo van Kemenade 135d0d5353 Ignore pip's root user warning 2023-05-29 13:42:14 +03:00
Sviatoslav Sydorenko 110f54a387 Merge pull request #157 from pypa/dependabot/pip/requirements/requests-2.31.0
Bump requests from 2.28.1 to 2.31.0 in /requirements
2023-05-23 07:41:59 +02:00
dependabot[bot] c803c91ef0 Bump requests from 2.28.1 to 2.31.0 in /requirements
Bumps [requests](https://github.com/psf/requests) from 2.28.1 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.28.1...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-23 05:16:54 +00:00
Sviatoslav Sydorenko f9ed8ba9ad Merge pull request #156 from trail-of-forks/tob-fix-annotation 2023-05-17 02:02:16 +02:00
William Woodruff 30639668ca oidc-exchange: "fix" multiline annotations
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-05-12 11:04:38 -04:00
20 changed files with 1115 additions and 218 deletions
+6
View File
@@ -13,4 +13,10 @@ ko_fi: webknjaz
liberapay: webknjaz
open_collective: webknjaz
# patreon: webknjaz # not in use because of the ties with ruscism
thanks_dev: u/gh/webknjaz
...
+32
View File
@@ -0,0 +1,32 @@
# Security Policy
**⚠️ Please do not file public GitHub issues for security
vulnerabilities as they are open for everyone to see! ⚠️**
We encourage responsible disclosure practices for security
vulnerabilities.
## Supported Versions
Always update to the latest version of
this Action to keep up with security patches.
## Reporting a Vulnerability
If you believe you've found a security-related bug, we
prefer that you fill out a [vulnerability report on GitHub]
directly.
[vulnerability report on GitHub]:
//github.com/pypa/gh-action-pypi-publish/security/advisories/new
## Don't have a GitHub account?
Alternatively, drop an email to
``wk+gh-action-pypi-publish-security`` at ``sydorenko`` dot
``org`` dot ``ua`` instead of filing a ticket or posting to
_any_ public groups. We will try to assess the problem in
timely manner and disclose it in a responsible way.
-14
View File
@@ -1,14 +0,0 @@
Security Policy
---------------
Supported Versions
==================
Always update to the latest version of
this Action to keep up with security patches.
Reporting a Vulnerability
=========================
Email to ``wk+gh-action-pypi-publish-security``
at ``sydorenko`` dot ``org`` dot ``ua``.
@@ -0,0 +1,75 @@
---
name: 🏗️
on: # yamllint disable-line rule:truthy
pull_request:
push:
branches: ["release/*", "unstable/*"]
workflow_dispatch:
inputs:
tag:
description: Docker image tag
required: true
type: string
jobs:
smoke-test:
uses: ./.github/workflows/reusable-smoke-test.yml
check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- smoke-test
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
build-and-push:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
needs:
- check
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
DOCKER_TAG="${DOCKER_TAG/'/'/'-'}"
DOCKER_TAG_MAJOR=$(echo "$DOCKER_TAG" | cut -d '.' -f 1)
DOCKER_TAG_MAJOR_MINOR=$(echo "$DOCKER_TAG" | cut -d '.' -f 1-2)
IMAGE="ghcr.io/$GITHUB_REPOSITORY:${DOCKER_TAG}"
IMAGE_MAJOR="ghcr.io/$GITHUB_REPOSITORY:${DOCKER_TAG_MAJOR}"
IMAGE_MAJOR_MINOR="ghcr.io/$GITHUB_REPOSITORY:${DOCKER_TAG_MAJOR_MINOR}"
IMAGE_SHA="ghcr.io/$GITHUB_REPOSITORY:${GITHUB_SHA}"
echo "IMAGE=$IMAGE" >>"$GITHUB_ENV"
echo "IMAGE_MAJOR=$IMAGE_MAJOR" >>"$GITHUB_ENV"
echo "IMAGE_MAJOR_MINOR=$IMAGE_MAJOR_MINOR" >>"$GITHUB_ENV"
echo "IMAGE_SHA=$IMAGE_SHA" >>"$GITHUB_ENV"
docker build . \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--cache-from $IMAGE \
--tag $IMAGE
docker tag $IMAGE $IMAGE_MAJOR
docker tag $IMAGE $IMAGE_MAJOR_MINOR
docker tag $IMAGE $IMAGE_SHA
env:
DOCKER_TAG: ${{ inputs.tag || github.ref_name }}
- name: Log in to GHCR
run: >-
echo ${{ secrets.GITHUB_TOKEN }} |
docker login ghcr.io -u $GITHUB_ACTOR --password-stdin
- name: Push Docker image to GHCR
run: |
docker push $IMAGE
docker push $IMAGE_MAJOR
docker push $IMAGE_MAJOR_MINOR
docker push $IMAGE_SHA
@@ -1,10 +1,9 @@
---
name: 🧪
name: ♻️ 🧪
on: # yamllint disable-line rule:truthy
push:
pull_request:
workflow_call:
env:
devpi-password: abcd1234
@@ -27,8 +26,40 @@ env:
PYTEST_THEME_MODE
jobs:
fail-fast:
strategy:
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 2
steps:
- name: Check out the action locally
uses: actions/checkout@v4
with:
path: test
- name: Fail-fast in unsupported environments
continue-on-error: true
id: fail-fast
uses: ./test
- name: Error if action did not fail-fast in unsupported environments
if: steps.fail-fast.outcome == 'success'
run: |
>&2 echo This action should fail-fast in unsupported environments.
exit 1
smoke-test:
runs-on: ubuntu-latest
strategy:
matrix:
os:
- ubuntu-24.04
- ubuntu-22.04
runs-on: ${{ matrix.os }}
services:
devpi:
@@ -42,7 +73,7 @@ jobs:
steps:
- name: Check out the action locally
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: test
- name: Install the packaging-related tools
@@ -61,7 +92,7 @@ jobs:
CONTENTS: |
[build-system]
requires = [
"setuptools == 65.6.3",
"setuptools == 75.8.0",
]
build-backend = "setuptools.build_meta"
@@ -71,8 +102,31 @@ jobs:
readme = "README.md"
- name: Build the stub package sdist and wheel distributions
run: python3 -m build
- name: Create the Rust package directory
run: mkdir -pv rust-example
- name: Initialize a Rust project
run: cargo init
working-directory: rust-example
- name: Populate the Rust package `pyproject.toml`
run: echo "$CONTENTS" > pyproject.toml
env:
CONTENTS: |
[build-system]
requires = [
"maturin ~=1.0",
]
build-backend = "maturin"
working-directory: rust-example
- name: Build the stub package sdist and wheel distributions
run: python3 -m build -o ../dist/
working-directory: rust-example
- name: Register the stub package in devpi
run: twine register dist/*.tar.gz
run: |
for dist in dist/*.tar.gz
do
echo "Registering ${dist}..."
twine register "${dist}"
done
env:
TWINE_USERNAME: ${{ env.devpi-username }}
TWINE_PASSWORD: ${{ env.devpi-password }}
+4
View File
@@ -0,0 +1,4 @@
[tool.pip-tools]
allow-unsafe = true
resolver = "backtracking"
strip-extras = true
+17 -62
View File
@@ -5,24 +5,24 @@ ci:
repos:
- repo: https://github.com/asottile/add-trailing-comma.git
rev: v2.4.0
rev: v3.1.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/PyCQA/isort.git
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
args:
- --honor-noqa
- repo: https://github.com/Lucas-C/pre-commit-hooks.git
rev: v1.5.1
rev: v1.5.5
hooks:
- id: remove-tabs
- repo: https://github.com/python-jsonschema/check-jsonschema.git
rev: 0.22.0
rev: 0.29.2
hooks:
- id: check-github-actions
- id: check-github-workflows
@@ -37,7 +37,7 @@ repos:
- id: check-readthedocs
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.4.0
rev: v4.6.0
hooks:
# Side-effects:
- id: end-of-file-fixer
@@ -62,12 +62,12 @@ repos:
language_version: python3
- repo: https://github.com/codespell-project/codespell
rev: v2.2.4
rev: v2.2.6
hooks:
- id: codespell
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.30.0
rev: v1.35.1
hooks:
- id: yamllint
files: \.(yaml|yml)$
@@ -78,13 +78,13 @@ repos:
- --strict
- repo: https://github.com/PyCQA/flake8.git
rev: 6.0.0
rev: 7.0.0
hooks:
- id: flake8
alias: flake8-no-wps
name: flake8 WPS-excluded
args:
- --ignore
# NOTE: WPS326: Found implicit string concatenation
# NOTE: WPS332: Found walrus operator
- >-
D100,
D101,
@@ -92,26 +92,11 @@ repos:
D107,
E402,
E501,
additional_dependencies:
- flake8-2020 ~= 1.7.0
- flake8-pytest-style ~= 1.6.0
- repo: https://github.com/PyCQA/flake8.git
# NOTE: This is kept at v4 for until WPS starts supporting flake v5.
rev: 4.0.1 # enforce-version: 4.0.1
hooks:
- id: flake8
alias: flake8-only-wps
name: flake8 WPS-only
args:
- --ignore
# NOTE: WPS326: Found implicit string concatenation
# NOTE: WPS332: Found walrus operator
- >-
WPS102,
WPS110,
WPS111,
WPS305,
WPS318,
WPS326,
WPS332,
WPS347,
@@ -124,13 +109,15 @@ repos:
WPS440,
WPS441,
WPS453,
- --select
- WPS
- --max-module-members=8 # WPS202
additional_dependencies:
- wemake-python-styleguide ~= 0.17.0
- flake8-2020 ~= 1.7.0
- flake8-pytest-style ~= 1.6.0
- wemake-python-styleguide ~= 0.19.0
language_version: python3.11 # flake8-commas doesn't work w/ Python 3.12
- repo: https://github.com/PyCQA/pylint.git
rev: v3.0.0a6
rev: v3.3.0
hooks:
- id: pylint
args:
@@ -149,36 +136,4 @@ repos:
- --output-format
- colorized
- repo: local
hooks:
- id: enforced-flake8-version
name: Verify that enforced flake8 version stays unchanged
description: >-
This is a sanity check and fixer that makes sure that
the `flake8` version in this file remains matching the
corresponding request in the `# enforce-version` comment.
# Using Python here because using
# shell test does not always work in CIs:
entry: >-
python -c 'import pathlib, re, sys;
pre_commit_config = pathlib.Path(sys.argv[1]);
cfg_txt = pre_commit_config.read_text();
new_cfg_txt = re.sub(
r"(?P<spaces>\s+)rev:\s(?:\d+\.\d+\.\d+)\s{0,2}"
r"#\senforce-version:\s(?P<enforced_version>\d+\.\d+\.\d+)"
r"[ \t\f\v]*",
r"\g<spaces>rev: \g<enforced_version> "
r"# enforce-version: \g<enforced_version>",
cfg_txt,
);
cfg_txt != new_cfg_txt and
pre_commit_config.write_text(new_cfg_txt)
'
pass_filenames: true
language: system
files: >-
^\.pre-commit-config\.ya?ml$
types:
- yaml
...
+7 -3
View File
@@ -1,16 +1,18 @@
FROM python:3.11-slim
FROM python:3.12-slim
LABEL "maintainer" "Sviatoslav Sydorenko <wk+pypa@sydorenko.org.ua>"
LABEL "repository" "https://github.com/pypa/gh-action-pypi-publish"
LABEL "homepage" "https://github.com/pypa/gh-action-pypi-publish"
LABEL "homepage" "https://github.com/marketplace/actions/pypi-publish"
LABEL "org.opencontainers.image.source" "https://github.com/pypa/gh-action-pypi-publish"
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV PIP_NO_CACHE_DIR 1
ENV PIP_ROOT_USER_ACTION ignore
ENV PATH "/root/.local/bin:${PATH}"
ENV PYTHONPATH "/root/.local/lib/python3.11/site-packages"
ENV PYTHONPATH "/root/.local/lib/python3.12/site-packages"
COPY requirements requirements
RUN \
@@ -25,7 +27,9 @@ WORKDIR /app
COPY LICENSE.md .
COPY twine-upload.sh .
COPY print-hash.py .
COPY print-pkg-names.py .
COPY oidc-exchange.py .
COPY attestations.py .
RUN chmod +x twine-upload.sh
ENTRYPOINT ["/app/twine-upload.sh"]
+148 -13
View File
@@ -1,7 +1,9 @@
[![SWUbanner]][SWUdocs]
![PyPA badge]
[![🧪 GitHub Actions CI/CD workflow tests badge]][GHA workflow runs list]
[![pre-commit.ci status badge]][pre-commit.ci results page]
[![GH Sponsors badge]][GH Sponsors URL]
# PyPI publish GitHub Action
@@ -13,19 +15,31 @@ walkthrough check out the [PyPA guide].
If you have any feedback regarding specific action versions, please leave
comments in the corresponding [per-release announcement discussions].
> [!TIP]
> A limited number of usage scenarios is supported, including the
> [PyPA guide] example. See the [non-goals] for more detail.
## 🌇 `master` branch sunset ❗
The `master` branch version has been sunset. Please, change the GitHub
Action version you use from `master` to `release/v1` or use an exact
tag, or a full Git commit SHA.
tag, or opt-in to [use a full Git commit SHA] and Dependabot.
## Usage
### Trusted publishing
> **NOTE**: Trusted publishing is sometimes referred to by its
> [!NOTE]
> Trusted publishing cannot be used from within a reusable workflow at this
> time. It is recommended to instead create a non-reusable workflow that contains a
> job calling your reusable workflow, and then do the trusted publishing step from
> a separate job within that non-reusable workflow. Alternatively, you can still
> use a username/token inside the reusable workflow.
> [!NOTE]
> Trusted publishing is sometimes referred to by its
> underlying technology -- OpenID Connect, or OIDC for short.
> If you see references to "OIDC publishing" in the context of PyPI,
> this is what they're referring to.
@@ -38,7 +52,7 @@ This action supports PyPI's [trusted publishing]
implementation, which allows authentication to PyPI without a manually
configured API token or username/password combination. To perform
[trusted publishing] with this action, your project's
publisher must already be configured on PyPI.
publisher must already be [configured on PyPI].
To enter the trusted publishing flow, configure this action's job with the
`id-token: write` permission and **without** an explicit username or password:
@@ -61,10 +75,11 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
```
> **Pro tip**: instead of using branch pointers, like `unstable/v1`, pin
versions of Actions that you use to tagged versions or sha1 commit identifiers.
This will make your workflows more secure and better reproducible, saving you
from sudden and unpleasant surprises.
> [!NOTE]
> Pro tip: instead of using branch pointers, like `unstable/v1`, pin versions of
> Actions that you use to tagged versions or sha1 commit identifiers.
> This will make your workflows more secure and better reproducible, saving you
> from sudden and unpleasant surprises.
Other indices that support trusted publishing can also be used, like TestPyPI:
@@ -76,7 +91,8 @@ Other indices that support trusted publishing can also be used, like TestPyPI:
```
_(don't forget to update the environment name to `testpypi` or similar!)_
> **Pro tip**: only set the `id-token: write` permission in the job that does
> [!NOTE]
> Pro tip: only set the `id-token: write` permission in the job that does
> publishing, not globally. Also, try to separate building from publishing
> — this makes sure that any scripts maliciously injected into the build
> or test environment won't be able to elevate privileges while flying under
@@ -89,14 +105,44 @@ filter to the job:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
```
### Generating and uploading attestations
> [!IMPORTANT]
> Support for generating and uploading [digital attestations] is currently
> experimental and limited only to Trusted Publishing flows using PyPI or TestPyPI.
> Support for this feature is not yet stable; the settings and behavior described
> below may change without prior notice.
> [!NOTE]
> Generating and uploading digital attestations currently requires
> authentication with a [trusted publisher].
Generating signed [digital attestations] for all the distribution files
and uploading them all together is now on by default for all projects
using Trusted Publishing. To disable it, set `attestations` as follows:
```yml
with:
attestations: false
```
The attestation objects are created using [Sigstore] for each
distribution package, signing them with the identity provided
by the GitHub's OIDC token associated with the current workflow. This means
both the trusted publishing authentication and the attestations are tied to the
same identity.
## Non-goals
This GitHub Action [has nothing to do with _building package
distributions_]. Users are responsible for preparing dists for upload
by putting them into the `dist/` folder prior to running this Action.
They are typically expected to do this in a _separate GitHub Actions
CI/CD job_ running before the one where they call this action and having
restricted privileges.
> **IMPORTANT**: Since this GitHub Action is docker-based, it can only
> [!IMPORTANT]
> Since this GitHub Action is docker-based, it can only
> be used from within GNU/Linux based jobs in GitHub Actions CI/CD
> workflows. This is by design and is unlikely to change due to a number
> of considerations we rely on.
@@ -118,6 +164,72 @@ by putting them into the `dist/` folder prior to running this Action.
> sharing the built dists across stages and jobs. Then, use the `needs`
> setting to order the build, test and publish stages.
The expected environment for running `pypi-publish` is the
GitHub-provided Ubuntu VM. We are running a smoke-test against
`ubuntu-latest` in CI but any currently available numbered versions
should do. We'll consider them supported for as long as GitHub itself
supports them.
Running the action in a job that has a `container:` set is not
supported. It might work for you but you're on your own when it breaks.
If you feel the need to use it, it's likely that you're not following
the recommendation of invoking the build automation in a separate job,
which is considered a security issue (especially, when using [Trusted
Publishing][trusted publisher] that may cause privilege escalation and
would enable the attackers to impersonate the GitHub-backed identity of
the repository through transitive build dependency poisoning). The
solution is to have one job (or multiple, in case of projects with
C-extensions) for building the distribution packages, followed by
another that publishes them.
Self-hosted runners are best effort, provided no other unsupported
things influence them. We are unable to test this in CI and they may
break. This is often the case when using custom runtimes and not the
official GitHub-provided VMs. In general, if you follow the
recommendation of building in a separate job, you shouldn't need to run
this action within a self-hosted runner — it should be possible to
build your dists in a self-hosted runner, save them as a GitHub Actions
artifact in that job, and then invoke the publishing job that would run
within GitHub-provided runners, downloading the artifact with the dists
and publishing them. Such separation is the _recommended_/**supported**
way of handling this scenario.
Our understandng is that Trusted publishing is expected to work on
self-hosted runners. It is backed by OIDC. If it doesn't work, you
should probably ask GitHub if you missed something. We wouldn't be able
to assist here.
Trusted Publishing cannot be tested in CI at the moment, sadly. It is
supported and bugs should be reported but it may take time to sort out
as it often requires cross-project collaboration to debug (sometimes,
problems occur due to changes in PyPI and not in the action).
The only case that is explicitly unsupported at the moment is [Trusted
Publishing][trusted publisher] in reusable workflows. This requires
support on the PyPI side and is being worked on. Please, do not report
bugs related to this case. The current recommendation is to put
everything else you want into a reusable workflow but keep the job
calling `pypi-publish` in a top-level one.
Invoking `pypi-publish` from composite actions is unsupported. It is not
tested. GitHub Runners have limitations and bugs in this case. But more
importantly, this is usually an indication of using it insecurely. When
using [Trusted Publishing][trusted publisher], it is imperative to keep
build machinery invocation in a separate job with restrictive privileges
as [Trusted Publishing][trusted publisher] itself requires elevated
permissions to make use of OIDC. Our observation is that the users
sometimes create in-project composite actions that invoke building and
publishing in the same job. As such, we don't seek to support such a
dangerous configuration in the first place. The solution is pretty much
the same as with the previous problem — use a separate job with
dedicated and scoped privileges just for publishing; and invoke that
in-project composite action from a different job.
And finally, invoking `pypi-publish` more than once in the same job is
not considered supported. It may work in a limited number of scenarios
but please, don't do this. If you want to publish to several indexes,
build the dists in one job and add several publishing jobs, one per
upload.
## Advanced release management
@@ -187,9 +299,10 @@ default) setting as follows:
skip-existing: true
```
> **Pro tip**: try to avoid enabling this setting where possible. If you
have steps for publishing to both PyPI and TestPyPI, consider only using
it for the latter, having the former fail loudly on duplicates.
> [!NOTE]
> Pro tip: try to avoid enabling this setting where possible. If you
> have steps for publishing to both PyPI and TestPyPI, consider only using
> it for the latter, having the former fail loudly on duplicates.
### For Debugging
@@ -239,9 +352,11 @@ on supported platforms (like GitHub).
The Dockerfile and associated scripts and documentation in this project
are released under the [BSD 3-clause license](LICENSE.md).
[PyPA badge]:
https://img.shields.io/badge/project-yellow?label=PyPA&labelColor=ffd242&color=3775a9
[🧪 GitHub Actions CI/CD workflow tests badge]:
https://github.com/pypa/gh-action-pypi-publish/actions/workflows/self-smoke-test-action.yml/badge.svg?branch=unstable%2Fv1&event=push
https://github.com/pypa/gh-action-pypi-publish/actions/workflows/build-and-push-docker-image.yml/badge.svg?branch=unstable%2Fv1&event=push
[GHA workflow runs list]:
https://github.com/pypa/gh-action-pypi-publish/actions/workflows/self-smoke-test-action.yml?query=branch%3Aunstable%2Fv1
@@ -250,9 +365,24 @@ https://results.pre-commit.ci/latest/github/pypa/gh-action-pypi-publish/unstable
[pre-commit.ci status badge]:
https://results.pre-commit.ci/badge/github/pypa/gh-action-pypi-publish/unstable/v1.svg
[docs badge]:
https://img.shields.io/badge/guide-gray?logo=readthedocs&label=PyPUG&color=white
[PyPUG guide]:
https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
[GH Sponsors badge]:
https://img.shields.io/badge/%40webknjaz-transparent?logo=githubsponsors&logoColor=%23EA4AAA&label=Sponsor&color=2a313c
[GH Sponsors URL]:
https://github.com/sponsors/webknjaz
[use a full Git commit SHA]:
https://julienrenaux.fr/2019/12/20/github-actions-security-risk/
[per-release announcement discussions]:
https://github.com/pypa/gh-action-pypi-publish/discussions/categories/announcements
[non-goals]: #Non-goals
[Creating & using secrets]:
https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets
[has nothing to do with _building package distributions_]:
@@ -269,5 +399,10 @@ https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md
[warehouse#12965]: https://github.com/pypi/warehouse/issues/12965
[trusted publishing]: https://docs.pypi.org/trusted-publishers/
[configured on PyPI]: https://docs.pypi.org/trusted-publishers/adding-a-publisher/
[how to specify username and password]: #specifying-a-different-username
[digital attestations]: https://peps.python.org/pep-0740/
[Sigstore]: https://www.sigstore.dev/
[trusted publisher]: #trusted-publishing
+94 -11
View File
@@ -80,18 +80,101 @@ inputs:
Use `print-hash` instead.
required: false
default: 'false'
attestations:
description: >-
[EXPERIMENTAL]
Enable experimental support for PEP 740 attestations.
Only works with PyPI and TestPyPI via Trusted Publishing.
required: false
default: 'true'
branding:
color: yellow
icon: upload-cloud
runs:
using: docker
image: Dockerfile
args:
- ${{ inputs.user }}
- ${{ inputs.password }}
- ${{ inputs.repository-url }}
- ${{ inputs.packages-dir }}
- ${{ inputs.verify-metadata }}
- ${{ inputs.skip-existing }}
- ${{ inputs.verbose }}
- ${{ inputs.print-hash }}
using: composite
steps:
- name: Fail-fast in unsupported environments
if: runner.os != 'Linux'
run: |
>&2 echo This action is only able to run under GNU/Linux environments
exit 1
shell: bash -eEuo pipefail {0}
- name: Reset path if needed
run: |
# Reset path if needed
# https://github.com/pypa/gh-action-pypi-publish/issues/112
if [[ $PATH != *"/usr/bin"* ]]; then
echo "\$PATH=$PATH. Resetting \$PATH for GitHub Actions."
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
echo "PATH=$PATH" >>"$GITHUB_ENV"
echo "$PATH" >>"$GITHUB_PATH"
echo "\$PATH reset. \$PATH=$PATH"
fi
shell: bash
- name: Set repo and ref from which to run Docker container action
id: set-repo-and-ref
run: |
# Set repo and ref from which to run Docker container action
# to handle cases in which `github.action_` context is not set
# https://github.com/actions/runner/issues/2473
REF=${{ env.ACTION_REF || env.PR_REF || github.ref_name }}
REPO=${{ env.ACTION_REPO || env.PR_REPO || github.repository }}
REPO_ID=${{ env.PR_REPO_ID || github.repository_id }}
echo "ref=$REF" >>"$GITHUB_OUTPUT"
echo "repo=$REPO" >>"$GITHUB_OUTPUT"
echo "repo-id=$REPO_ID" >>"$GITHUB_OUTPUT"
shell: bash
env:
ACTION_REF: ${{ github.action_ref }}
ACTION_REPO: ${{ github.action_repository }}
PR_REF: ${{ github.event.pull_request.head.ref }}
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
PR_REPO_ID: ${{ github.event.pull_request.base.repo.id }}
- name: Discover pre-installed Python
id: pre-installed-python
run: |
# 🔎 Discover pre-installed Python
echo "python-path=$(command -v python3 || :)" | tee -a "${GITHUB_OUTPUT}"
shell: bash
- name: Install Python 3
if: steps.pre-installed-python.outputs.python-path == ''
id: new-python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Create Docker container action
run: |
# Create Docker container action
${{
steps.pre-installed-python.outputs.python-path == ''
&& steps.new-python.outputs.python-path
|| steps.pre-installed-python.outputs.python-path
}} '${{ github.action_path }}/create-docker-action.py'
env:
REF: ${{ steps.set-repo-and-ref.outputs.ref }}
REPO: ${{ steps.set-repo-and-ref.outputs.repo }}
REPO_ID: ${{ steps.set-repo-and-ref.outputs.repo-id }}
shell: bash
- name: Run Docker container
# The generated trampoline action must exist in the allowlisted
# runner-defined working directory so it can be referenced by the
# relative path starting with `./`.
#
# This mutates the end-user's workspace slightly but uses a path
# that is unlikely to clash with somebody else's use.
#
# We cannot use randomized paths because the composite action
# syntax does not allow accessing variables in `uses:`. This
# means that we end up having to hardcode this path both here and
# in `create-docker-action.py`.
uses: ./.github/.tmp/.generated-actions/run-pypi-publish-in-docker-container
with:
user: ${{ inputs.user }}
password: ${{ inputs.password }}
repository-url: ${{ inputs.repository-url || inputs.repository_url }}
packages-dir: ${{ inputs.packages-dir || inputs.packages_dir }}
verify-metadata: ${{ inputs.verify-metadata || inputs.verify_metadata }}
skip-existing: ${{ inputs.skip-existing || inputs.skip_existing }}
verbose: ${{ inputs.verbose }}
print-hash: ${{ inputs.print-hash || inputs.print_hash }}
attestations: ${{ inputs.attestations }}
+149
View File
@@ -0,0 +1,149 @@
import logging
import os
import sys
from pathlib import Path
from typing import NoReturn
from pypi_attestations import Attestation, Distribution
from sigstore.oidc import IdentityError, IdentityToken, detect_credential
from sigstore.sign import Signer, SigningContext
# Be very verbose.
sigstore_logger = logging.getLogger('sigstore')
sigstore_logger.setLevel(logging.DEBUG)
sigstore_logger.addHandler(logging.StreamHandler())
_GITHUB_STEP_SUMMARY = Path(os.getenv('GITHUB_STEP_SUMMARY'))
# The top-level error message that gets rendered.
# This message wraps one of the other templates/messages defined below.
_ERROR_SUMMARY_MESSAGE = """
Attestation generation failure:
{message}
You're seeing this because the action attempted to generated PEP 740
attestations for its inputs, but failed to do so.
"""
# Rendered if OIDC identity token retrieval fails for any reason.
_TOKEN_RETRIEVAL_FAILED_MESSAGE = """
OpenID Connect token retrieval failed: {identity_error}
This failure occurred after a successful Trusted Publishing Flow,
suggesting a transient error.
""" # noqa: S105; not a password
def die(msg: str) -> NoReturn:
with _GITHUB_STEP_SUMMARY.open('a', encoding='utf-8') as io:
print(_ERROR_SUMMARY_MESSAGE.format(message=msg), file=io)
# HACK: GitHub Actions' annotations don't work across multiple lines naively;
# translating `\n` into `%0A` (i.e., HTML percent-encoding) is known to work.
# See: https://github.com/actions/toolkit/issues/193
msg = msg.replace('\n', '%0A')
print(f'::error::Attestation generation failure: {msg}', file=sys.stderr)
sys.exit(1)
def debug(msg: str) -> None:
print(f'::debug::{msg}', file=sys.stderr)
def collect_dists(packages_dir: Path) -> list[Path]:
# Collect all sdists and wheels.
dist_paths = [sdist.resolve() for sdist in packages_dir.glob('*.tar.gz')]
dist_paths.extend(sdist.resolve() for sdist in packages_dir.glob('*.zip'))
dist_paths.extend(whl.resolve() for whl in packages_dir.glob('*.whl'))
# Make sure everything that looks like a dist actually is one.
# We do this up-front to prevent partial signing.
if (invalid_dists := [path for path in dist_paths if not path.is_file()]):
invalid_dist_list = ', '.join(map(str, invalid_dists))
die(
'The following paths look like distributions but '
f'are not actually files: {invalid_dist_list}',
)
return dist_paths
def assert_attestations_do_not_pre_exist(
dist_to_attestation_map: dict[Path, Path],
) -> None:
existing_attestations = {
f'* {dist !s} -> {dist_attestation !s}'
for dist, dist_attestation in dist_to_attestation_map.items()
if dist_attestation.exists()
}
if not existing_attestations:
return
existing_attestations_list = '\n'.join(map(str, existing_attestations))
error_message = (
'The following distributions already have publish attestations:'
f'{existing_attestations_list}',
)
die(error_message)
def compose_attestation_mapping(dist_paths: list[Path]) -> dict[Path, Path]:
dist_to_attestation_map = {
dist_path: dist_path.with_suffix(
f'{dist_path.suffix}.publish.attestation',
)
for dist_path in dist_paths
}
# We are the publishing step, so there should be no pre-existing publish
# attestation. The presence of one indicates user confusion.
# Make sure there's no publish attestations on disk.
# We do this up-front to prevent partial signing.
assert_attestations_do_not_pre_exist(dist_to_attestation_map)
return dist_to_attestation_map
def attest_dist(
dist_path: Path,
attestation_path: Path,
signer: Signer,
) -> None:
dist = Distribution.from_file(dist_path)
attestation = Attestation.sign(signer, dist)
attestation_path.write_text(attestation.model_dump_json(), encoding='utf-8')
debug(f'saved publish attestation: {dist_path=} {attestation_path=}')
def get_identity_token() -> IdentityToken:
# Will raise `sigstore.oidc.IdentityError` if it fails to get the token
# from the environment or if the token is malformed.
# NOTE: audience is always sigstore.
oidc_token = detect_credential()
return IdentityToken(oidc_token)
def main() -> None:
dist_to_attestation_map = compose_attestation_mapping(
collect_dists(Path(sys.argv[1])),
)
try:
identity = get_identity_token()
except IdentityError as identity_error:
# NOTE: We only perform attestations in trusted publishing flows, so we
# don't need to re-check for the "PR from fork" error mode, only
# generic token retrieval errors. We also render a simpler error,
# since permissions can't be to blame at this stage.
die(_TOKEN_RETRIEVAL_FAILED_MESSAGE.format(identity_error=identity_error))
with SigningContext.production().signer(identity, cache=True) as signer:
debug(f'attesting to dists: {dist_to_attestation_map.keys()}')
for dist_path, attestation_path in dist_to_attestation_map.items():
attest_dist(dist_path, attestation_path, signer)
if __name__ == '__main__':
main()
+91
View File
@@ -0,0 +1,91 @@
import json
import os
import pathlib
DESCRIPTION = 'description'
REQUIRED = 'required'
REF = os.environ['REF']
REPO = os.environ['REPO']
REPO_ID = os.environ['REPO_ID']
REPO_ID_GH_ACTION = '178055147'
ACTION_SHELL_CHECKOUT_PATH = pathlib.Path(__file__).parent.resolve()
def set_image(ref: str, repo: str, repo_id: str) -> str:
if repo_id == REPO_ID_GH_ACTION:
return str(ACTION_SHELL_CHECKOUT_PATH / 'Dockerfile')
docker_ref = ref.replace('/', '-')
return f'docker://ghcr.io/{repo}:{docker_ref}'
image = set_image(REF, REPO, REPO_ID)
action = {
'name': '🏃',
DESCRIPTION: (
'Run Docker container to upload Python distribution packages to PyPI'
),
'inputs': {
'user': {DESCRIPTION: 'PyPI user', REQUIRED: False},
'password': {
DESCRIPTION: 'Password for your PyPI user or an access token',
REQUIRED: False,
},
'repository-url': {
DESCRIPTION: 'The repository URL to use',
REQUIRED: False,
},
'packages-dir': {
DESCRIPTION: 'The target directory for distribution',
REQUIRED: False,
},
'verify-metadata': {
DESCRIPTION: 'Check metadata before uploading',
REQUIRED: False,
},
'skip-existing': {
DESCRIPTION: (
'Do not fail if a Python package distribution'
' exists in the target package index'
),
REQUIRED: False,
},
'verbose': {DESCRIPTION: 'Show verbose output.', REQUIRED: False},
'print-hash': {
DESCRIPTION: 'Show hash values of files to be uploaded',
REQUIRED: False,
},
'attestations': {
DESCRIPTION: (
'[EXPERIMENTAL]'
' Enable experimental support for PEP 740 attestations.'
' Only works with PyPI and TestPyPI via Trusted Publishing.'
),
REQUIRED: False,
},
},
'runs': {
'using': 'docker',
'image': image,
},
}
# The generated trampoline action must exist in the allowlisted
# runner-defined working directory so it can be referenced by the
# relative path starting with `./`.
#
# This mutates the end-user's workspace slightly but uses a path
# that is unlikely to clash with somebody else's use.
#
# We cannot use randomized paths because the composite action
# syntax does not allow accessing variables in `uses:`. This
# means that we end up having to hardcode this path both here and
# in `action.yml`.
action_path = pathlib.Path(
'.github/.tmp/.generated-actions/'
'run-pypi-publish-in-docker-container/action.yml',
)
action_path.parent.mkdir(parents=True, exist_ok=True)
action_path.write_text(json.dumps(action, ensure_ascii=False), encoding='utf-8')
+129 -28
View File
@@ -1,3 +1,5 @@
import base64
import json
import os
import sys
from http import HTTPStatus
@@ -8,7 +10,7 @@ from urllib.parse import urlparse
import id # pylint: disable=redefined-builtin
import requests
_GITHUB_STEP_SUMMARY = Path(os.getenv("GITHUB_STEP_SUMMARY"))
_GITHUB_STEP_SUMMARY = Path(os.getenv('GITHUB_STEP_SUMMARY'))
# The top-level error message that gets rendered.
# This message wraps one of the other templates/messages defined below.
@@ -43,13 +45,49 @@ permissions:
```
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}`
See https://docs.pypi.org/trusted-publishers/troubleshooting/ for more help.
"""
# Rendered if the package index's token response isn't valid JSON.
@@ -59,7 +97,9 @@ Token request failed: the index produced an unexpected
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 = """
@@ -67,26 +107,30 @@ 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
def die(msg: str) -> NoReturn:
with _GITHUB_STEP_SUMMARY.open("a", encoding="utf-8") as io:
with _GITHUB_STEP_SUMMARY.open('a', encoding='utf-8') as io:
print(_ERROR_SUMMARY_MESSAGE.format(message=msg), file=io)
print(f"::error::Trusted publishing exchange failure: {msg}", file=sys.stderr)
# HACK: GitHub Actions' annotations don't work across multiple lines naively;
# translating `\n` into `%0A` (i.e., HTML percent-encoding) is known to work.
# See: https://github.com/actions/toolkit/issues/193
msg = msg.replace('\n', '%0A')
print(f'::error::Trusted publishing exchange failure: {msg}', file=sys.stderr)
sys.exit(1)
def debug(msg: str):
print(f"::debug::{msg.title()}", file=sys.stderr)
print(f'::debug::{msg.title()}', file=sys.stderr)
def get_normalized_input(name: str) -> str | None:
name = f"INPUT_{name.upper()}"
name = f'INPUT_{name.upper()}'
if val := os.getenv(name):
return val
return os.getenv(name.replace("-", "_"))
return os.getenv(name.replace('-', '_'))
def assert_successful_audience_call(resp: requests.Response, domain: str):
@@ -98,13 +142,13 @@ def assert_successful_audience_call(resp: requests.Response, domain: str):
# This index supports OIDC, but forbids the client from using
# it (either because it's disabled, ratelimited, etc.)
die(
f"audience retrieval failed: repository at {domain} has trusted publishing disabled",
f'audience retrieval failed: repository at {domain} has trusted publishing disabled',
)
case HTTPStatus.NOT_FOUND:
# This index does not support OIDC.
die(
"audience retrieval failed: repository at "
f"{domain} does not indicate trusted publishing support",
'audience retrieval failed: repository at '
f'{domain} does not indicate trusted publishing support',
)
case other:
status = HTTPStatus(other)
@@ -112,34 +156,84 @@ def assert_successful_audience_call(resp: requests.Response, domain: str):
# something we expect. This can happen if the index is broken, in maintenance mode,
# misconfigured, etc.
die(
"audience retrieval failed: repository at "
f"{domain} responded with unexpected {other}: {status.phrase}",
'audience retrieval failed: repository at '
f'{domain} responded with unexpected {other}: {status.phrase}',
)
repository_url = get_normalized_input("repository-url")
def render_claims(token: str) -> str:
_, payload, _ = token.split('.', 2)
# urlsafe_b64decode needs padding; JWT payloads don't contain any.
payload += '=' * (4 - (len(payload) % 4))
claims = json.loads(base64.urlsafe_b64decode(payload))
def _get(name: str) -> str: # noqa: WPS430
return claims.get(name, 'MISSING')
return _RENDERED_CLAIMS.format(
sub=_get('sub'),
repository=_get('repository'),
repository_owner=_get('repository_owner'),
repository_owner_id=_get('repository_owner_id'),
workflow_ref=_get('workflow_ref'),
job_workflow_ref=_get('job_workflow_ref'),
ref=_get('ref'),
)
def event_is_third_party_pr() -> bool:
# Non-`pull_request` events cannot be from third-party PRs.
if os.getenv('GITHUB_EVENT_NAME') != 'pull_request':
return False
event_path = os.getenv('GITHUB_EVENT_PATH')
if not event_path:
# No GITHUB_EVENT_PATH indicates a weird GitHub or runner bug.
debug('unexpected: no GITHUB_EVENT_PATH to check')
return False
try:
event = json.loads(Path(event_path).read_bytes())
except json.JSONDecodeError:
debug('unexpected: GITHUB_EVENT_PATH does not contain valid JSON')
return False
try:
return event['pull_request']['head']['repo']['fork']
except KeyError:
return False
repository_url = get_normalized_input('repository-url')
repository_domain = urlparse(repository_url).netloc
token_exchange_url = f"https://{repository_domain}/_/oidc/github/mint-token"
token_exchange_url = f'https://{repository_domain}/_/oidc/mint-token'
# Indices are expected to support `https://{domain}/_/oidc/audience`,
# which tells OIDC exchange clients which audience to use.
audience_url = f"https://{repository_domain}/_/oidc/audience"
audience_resp = requests.get(audience_url)
audience_url = f'https://{repository_domain}/_/oidc/audience'
audience_resp = requests.get(audience_url, timeout=5) # S113 wants a timeout
assert_successful_audience_call(audience_resp, repository_domain)
oidc_audience = audience_resp.json()["audience"]
oidc_audience = audience_resp.json()['audience']
debug(f"selected trusted publishing exchange endpoint: {token_exchange_url}")
debug(f'selected trusted publishing exchange endpoint: {token_exchange_url}')
try:
oidc_token = id.detect_credential(audience=oidc_audience)
except id.IdentityError as identity_error:
die(_TOKEN_RETRIEVAL_FAILED_MESSAGE.format(identity_error=identity_error))
cause_msg_tmpl = (
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE if event_is_third_party_pr()
else _TOKEN_RETRIEVAL_FAILED_MESSAGE
)
for_cause_msg = cause_msg_tmpl.format(identity_error=identity_error)
die(for_cause_msg)
# Now we can do the actual token exchange.
mint_token_resp = requests.post(
token_exchange_url,
json={"token": oidc_token},
json={'token': oidc_token},
timeout=5, # S113 wants a timeout
)
try:
@@ -156,19 +250,26 @@ except requests.JSONDecodeError:
# On failure, the JSON response includes the list of errors that
# occurred during minting.
if not mint_token_resp.ok:
reasons = "\n".join(
f"* `{error['code']}`: {error['description']}"
for error in mint_token_payload["errors"]
reasons = '\n'.join(
f'* `{error["code"]}`: {error["description"]}'
for error in mint_token_payload['errors']
)
die(_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE.format(reasons=reasons))
rendered_claims = render_claims(oidc_token)
pypi_token = mint_token_payload.get("token")
die(
_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE.format(
reasons=reasons,
rendered_claims=rendered_claims,
),
)
pypi_token = mint_token_payload.get('token')
if pypi_token is None:
die(_SERVER_TOKEN_RESPONSE_MALFORMED_MESSAGE)
# Mask the newly minted PyPI token, so that we don't accidentally leak it in logs.
print(f"::add-mask::{pypi_token}", file=sys.stderr)
print(f'::add-mask::{pypi_token}', file=sys.stderr)
# This final print will be captured by the subshell in `twine-upload.sh`.
print(pypi_token)
+8 -8
View File
@@ -2,17 +2,17 @@ import hashlib
import pathlib
import sys
packages_dir = pathlib.Path(sys.argv[1]).resolve().absolute()
packages_dir = pathlib.Path(sys.argv[1]).resolve()
print("Showing hash values of files to be uploaded:")
print('Showing hash values of files to be uploaded:')
for file_object in packages_dir.iterdir():
sha256 = hashlib.sha256()
md5 = hashlib.md5()
md5 = hashlib.md5() # noqa: S324; only use for reference
blake2_256 = hashlib.blake2b(digest_size=256 // 8)
print(file_object)
print("")
print('')
content = file_object.read_bytes()
@@ -20,7 +20,7 @@ for file_object in packages_dir.iterdir():
md5.update(content)
blake2_256.update(content)
print(f"SHA256: {sha256.hexdigest()}")
print(f"MD5: {md5.hexdigest()}")
print(f"BLAKE2-256: {blake2_256.hexdigest()}")
print("")
print(f'SHA256: {sha256.hexdigest()}')
print(f'MD5: {md5.hexdigest()}')
print(f'BLAKE2-256: {blake2_256.hexdigest()}')
print('')
+35
View File
@@ -0,0 +1,35 @@
import pathlib
import sys
from packaging import utils
def debug(msg: str):
print(f'::debug::{msg.title()}', file=sys.stderr)
def safe_parse_pkg_name(file_path: pathlib.Path) -> str | None:
if file_path.suffix == '.whl':
try:
return utils.parse_wheel_filename(file_path.name)[0]
except utils.InvalidWheelFilename:
debug(f'Invalid wheel filename: {file_path.name}')
return None
elif file_path.suffix == '.gz':
try:
return utils.parse_sdist_filename(file_path.name)[0]
except utils.InvalidSdistFilename:
debug(f'Invalid sdist filename: {file_path.name}')
return None
return None
packages_dir = pathlib.Path(sys.argv[1]).resolve()
pkg_names = {
pkg_name for file_path in packages_dir.iterdir() if
(pkg_name := safe_parse_pkg_name(file_path)) is not None
}
for package_name in sorted(pkg_names):
print(package_name)
+20
View File
@@ -0,0 +1,20 @@
###############################################################################
# #
# This file is only meant to exclude broken dependency versions, not feature #
# dependencies. #
# #
# GUIDELINES: #
# 1. Only list PyPI project versions that need to be excluded using `!=` #
# and `<`. #
# 2. It is allowed to have transitive dependency limitations in this file. #
# 3. Apply bare minimum constraints under narrow conditions, use #
# environment markers if possible. E.g. `; python_version < "3.12"`. #
# 4. Whenever there are no constraints, let the file and this header #
# remain in Git. #
# #
###############################################################################
# NOTE: 1.12.0 and later enable support for metadata 2.4
# NOTE: This can be dropped once twine stops using pkginfo
# Ref: https://github.com/pypa/twine/pull/1180
pkginfo >= 1.12.0
+4 -4
View File
@@ -1,12 +1,12 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile --allow-unsafe --output-file=requirements/runtime-prerequisites.txt --resolver=backtracking --strip-extras requirements/runtime-prerequisites.in
# pip-compile --allow-unsafe --config=../.pip-tools.toml --output-file=runtime-prerequisites.txt --strip-extras runtime-prerequisites.in
#
pip-with-requires-python==1.0.1
# via -r requirements/runtime-prerequisites.in
# via -r runtime-prerequisites.in
# The following packages are considered to be unsafe in a requirements file:
pip==22.3.1
pip==24.0
# via pip-with-requires-python
+12 -11
View File
@@ -1,6 +1,10 @@
twine
-c runtime-constraints.in # limits known broken versions
# NOTE: Used to detect an ambient OIDC credential for OIDC publishing.
# NOTE: v6 is needed to support metadata v2.4
twine >= 6.0
# NOTE: Used to detect an ambient OIDC credential for OIDC publishing,
# NOTE: as well as PEP 740 attestations.
id ~= 1.0
# NOTE: This is pulled in transitively through `twine`, but we also declare
@@ -8,12 +12,9 @@ id ~= 1.0
# Ref: https://github.com/di/id
requests
# NOTE: `pkginfo` is a transitive dependency for us that is coming from Twine.
# NOTE: It is declared here only to avoid installing a broken combination of
# NOTE: the distribution packages. This should be removed once a fixed version
# NOTE: of Twine is out.
# Refs:
# * https://github.com/pypa/gh-action-pypi-publish/issues/107
# * https://github.com/pypa/twine/issues/940
# * https://github.com/pypa/twine/pull/941
pkginfo != 1.9.0
# NOTE: Used to generate attestations.
pypi-attestations ~= 0.0.15
sigstore ~= 3.5.1
# NOTE: Used to detect the PyPI package name from the distribution files
packaging
+119 -46
View File
@@ -1,80 +1,153 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
# pip-compile --allow-unsafe --output-file=requirements/runtime.txt --resolver=backtracking --strip-extras requirements/runtime.in
# pip-compile --allow-unsafe --config=../.pip-tools.toml --output-file=runtime.txt --strip-extras runtime.in
#
bleach==5.0.1
# via readme-renderer
certifi==2022.12.7
annotated-types==0.6.0
# via pydantic
betterproto==2.0.0b6
# via sigstore-protobuf-specs
certifi==2024.2.2
# via requests
cffi==1.15.1
cffi==1.16.0
# via cryptography
charset-normalizer==2.1.1
charset-normalizer==3.3.2
# via requests
commonmark==0.9.1
# via rich
cryptography==39.0.1
# via secretstorage
docutils==0.19
# via readme-renderer
id==1.0.0
# via -r requirements/runtime.in
idna==3.4
# via requests
importlib-metadata==5.1.0
cryptography==42.0.7
# via
# keyring
# twine
jaraco-classes==3.2.3
# pyopenssl
# pypi-attestations
# secretstorage
# sigstore
dnspython==2.6.1
# via email-validator
docutils==0.21.2
# via readme-renderer
email-validator==2.1.1
# via pydantic
grpclib==0.4.7
# via betterproto
h2==4.1.0
# via grpclib
hpack==4.0.0
# via h2
hyperframe==6.0.1
# via h2
id==1.4.0
# via
# -r runtime.in
# sigstore
idna==3.7
# via
# email-validator
# requests
jaraco-classes==3.4.0
# via keyring
jaraco-context==5.3.0
# via keyring
jaraco-functools==4.0.1
# via keyring
jeepney==0.8.0
# via
# keyring
# secretstorage
keyring==23.11.0
keyring==25.2.1
# via twine
more-itertools==9.0.0
# via jaraco-classes
pkginfo==1.9.2
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
more-itertools==10.2.0
# via
# -r requirements/runtime.in
# jaraco-classes
# jaraco-functools
multidict==6.0.5
# via grpclib
nh3==0.2.17
# via readme-renderer
packaging==24.1
# via
# -r runtime.in
# pypi-attestations
# twine
pycparser==2.21
pkginfo==1.12.0
# via
# -c runtime-constraints.in
# twine
platformdirs==4.2.2
# via sigstore
pyasn1==0.6.0
# via
# pypi-attestations
# sigstore
pycparser==2.22
# via cffi
pydantic==1.10.6
# via id
pygments==2.13.0
pydantic==2.7.1
# via
# id
# pypi-attestations
# sigstore
# sigstore-rekor-types
pydantic-core==2.18.2
# via pydantic
pygments==2.18.0
# via
# readme-renderer
# rich
readme-renderer==37.3
pyjwt==2.8.0
# via sigstore
pyopenssl==24.1.0
# via sigstore
pypi-attestations==0.0.15
# via -r runtime.in
python-dateutil==2.9.0.post0
# via betterproto
readme-renderer==43.0
# via twine
requests==2.28.1
requests==2.32.3
# via
# -r requirements/runtime.in
# -r runtime.in
# id
# requests-toolbelt
# sigstore
# tuf
# twine
requests-toolbelt==0.10.1
requests-toolbelt==1.0.0
# via twine
rfc3986==2.0.0
# via twine
rich==12.6.0
# via twine
rfc8785==0.1.2
# via sigstore
rich==13.7.1
# via
# sigstore
# twine
secretstorage==3.3.3
# via keyring
securesystemslib==1.0.0
# via tuf
sigstore==3.5.1
# via
# -r runtime.in
# pypi-attestations
sigstore-protobuf-specs==0.3.2
# via
# pypi-attestations
# sigstore
sigstore-rekor-types==0.0.13
# via sigstore
six==1.16.0
# via bleach
twine==4.0.1
# via -r requirements/runtime.in
typing-extensions==4.5.0
# via pydantic
urllib3==1.26.13
# via python-dateutil
tuf==5.0.0
# via sigstore
twine==6.0.1
# via -r runtime.in
typing-extensions==4.11.0
# via
# pydantic
# pydantic-core
urllib3==2.2.1
# via
# requests
# twine
webencodings==0.5.1
# via bleach
zipp==3.11.0
# via importlib-metadata
+104 -11
View File
@@ -39,24 +39,108 @@ INPUT_PACKAGES_DIR="$(get-normalized-input 'packages-dir')"
INPUT_VERIFY_METADATA="$(get-normalized-input 'verify-metadata')"
INPUT_SKIP_EXISTING="$(get-normalized-input 'skip-existing')"
INPUT_PRINT_HASH="$(get-normalized-input 'print-hash')"
INPUT_ATTESTATIONS="$(get-normalized-input 'attestations')"
if [[ "${INPUT_USER}" == "__token__" && -z "${INPUT_PASSWORD}" ]] ; then
REPOSITORY_NAME="$(echo ${GITHUB_REPOSITORY} | cut -d'/' -f2)"
WORKFLOW_FILENAME="$(echo ${GITHUB_WORKFLOW_REF} | cut -d'/' -f5- | cut -d'@' -f1)"
PACKAGE_NAMES=()
while IFS='' read -r line; do PACKAGE_NAMES+=("$line"); done < <(python /app/print-pkg-names.py "${INPUT_PACKAGES_DIR%%/}")
PASSWORD_DEPRECATION_NUDGE="::error title=Password-based uploads disabled::\
As of 2024, PyPI requires all users to enable Two-Factor \
Authentication. This consequently requires all users to switch \
to either Trusted Publishers (preferred) or API tokens for package \
uploads. Read more: \
https://blog.pypi.org/posts/2023-05-25-securing-pypi-with-2fa/"
TRUSTED_PUBLISHING_NUDGE="::warning title=Upgrade to Trusted Publishing::\
Trusted Publishers allows publishing packages to PyPI from automated \
environments like GitHub Actions without needing to use username/password \
combinations or API tokens to authenticate with PyPI. Read more: \
https://docs.pypi.org/trusted-publishers"
ATTESTATIONS_WITHOUT_TP_WARNING="::warning title=attestations input ignored::\
The workflow was run with the 'attestations: true' input, but an explicit \
password was also set, disabling Trusted Publishing. As a result, the \
attestations input is ignored."
ATTESTATIONS_WRONG_INDEX_WARNING="::warning title=attestations input ignored::\
The workflow was run with 'attestations: true' input, but the specified \
repository URL does not support PEP 740 attestations. As a result, the \
attestations input is ignored."
MAGIC_LINK_MESSAGE="A new Trusted Publisher for the currently running \
publishing workflow can be created by accessing the following link(s) while \
logged-in as an owner of the package(s):"
[[ "${INPUT_USER}" == "__token__" && -z "${INPUT_PASSWORD}" ]] \
&& TRUSTED_PUBLISHING=true || TRUSTED_PUBLISHING=false
if [[ "${TRUSTED_PUBLISHING}" == true || ! "${INPUT_REPOSITORY_URL}" =~ pypi\.org || ${#PACKAGE_NAMES[@]} -eq 0 ]] ; then
TRUSTED_PUBLISHING_MAGIC_LINK_NUDGE=""
else
if [[ "${INPUT_REPOSITORY_URL}" =~ test\.pypi\.org ]] ; then
INDEX_URL="https://test.pypi.org"
else
INDEX_URL="https://pypi.org"
fi
ALL_LINKS=""
for PACKAGE_NAME in "${PACKAGE_NAMES[@]}"; do
LINK="- ${INDEX_URL}/manage/project/${PACKAGE_NAME}/settings/publishing/?provider=github&owner=${GITHUB_REPOSITORY_OWNER}&repository=${REPOSITORY_NAME}&workflow_filename=${WORKFLOW_FILENAME}"
ALL_LINKS+="$LINK"$'\n'
done
# Construct the summary message without the warning header
MAGIC_LINK_MESSAGE_WITH_LINKS="${MAGIC_LINK_MESSAGE}"$'\n'"${ALL_LINKS}"
echo "${MAGIC_LINK_MESSAGE_WITH_LINKS}" >> $GITHUB_STEP_SUMMARY
# The actual nudge in the log is formatted as a warning
TRUSTED_PUBLISHING_MAGIC_LINK_NUDGE="::warning title=Create a Trusted Publisher::${MAGIC_LINK_MESSAGE_WITH_LINKS}"
fi
if [[ "${INPUT_ATTESTATIONS}" != "false" ]] ; then
# Setting `attestations: true` without Trusted Publishing indicates
# user confusion, since attestations (currently) require it.
if ! "${TRUSTED_PUBLISHING}" ; then
echo "${ATTESTATIONS_WITHOUT_TP_WARNING}"
INPUT_ATTESTATIONS="false"
fi
# Setting `attestations: true` with an index other than PyPI or TestPyPI
# indicates user confusion, since attestations are not supported on other
# indices presently.
if [[ ! "${INPUT_REPOSITORY_URL}" =~ pypi\.org ]] ; then
echo "${ATTESTATIONS_WRONG_INDEX_WARNING}"
INPUT_ATTESTATIONS="false"
fi
fi
if "${TRUSTED_PUBLISHING}" ; then
# No password supplied by the user implies that we're in the OIDC flow;
# retrieve the OIDC credential and exchange it for a PyPI API token.
echo \
'::notice::Attempting to perform trusted publishing exchange' \
'to retrieve a temporary short-lived API token for authentication' \
"against ${INPUT_REPOSITORY_URL} due to __token__ username with no" \
'supplied password field'
echo "::debug::Authenticating to ${INPUT_REPOSITORY_URL} via Trusted Publishing"
INPUT_PASSWORD="$(python /app/oidc-exchange.py)"
elif [[ "${INPUT_USER}" == '__token__' ]]; then
echo \
'::notice::Using a user-provided API token for authentication' \
'::debug::Using a user-provided API token for authentication' \
"against ${INPUT_REPOSITORY_URL}"
if [[ "${INPUT_REPOSITORY_URL}" =~ pypi\.org ]]; then
echo "${TRUSTED_PUBLISHING_NUDGE}"
echo "${TRUSTED_PUBLISHING_MAGIC_LINK_NUDGE}"
fi
else
echo \
'::notice::Using a username + password pair for authentication' \
"against ${INPUT_REPOSITORY_URL}}"
'::debug::Using a username + password pair for authentication' \
"against ${INPUT_REPOSITORY_URL}"
if [[ "${INPUT_REPOSITORY_URL}" =~ pypi\.org ]]; then
echo "${PASSWORD_DEPRECATION_NUDGE}"
echo "${TRUSTED_PUBLISHING_NUDGE}"
echo "${TRUSTED_PUBLISHING_MAGIC_LINK_NUDGE}"
exit 1
fi
fi
if [[
@@ -102,15 +186,24 @@ if [[ ${INPUT_VERIFY_METADATA,,} != "false" ]] ; then
twine check ${INPUT_PACKAGES_DIR%%/}/*
fi
TWINE_EXTRA_ARGS=
TWINE_EXTRA_ARGS=--disable-progress-bar
if [[ ${INPUT_SKIP_EXISTING,,} != "false" ]] ; then
TWINE_EXTRA_ARGS=--skip-existing
TWINE_EXTRA_ARGS="${TWINE_EXTRA_ARGS} --skip-existing"
fi
if [[ ${INPUT_VERBOSE,,} != "false" ]] ; then
TWINE_EXTRA_ARGS="--verbose $TWINE_EXTRA_ARGS"
fi
if [[ ${INPUT_ATTESTATIONS,,} != "false" ]] ; then
# NOTE: Intentionally placed after `twine check`, to prevent attestation
# NOTE: generation on distributions with invalid metadata.
echo "::notice::Generating and uploading digital attestations"
python /app/attestations.py "${INPUT_PACKAGES_DIR%%/}"
TWINE_EXTRA_ARGS="--attestations $TWINE_EXTRA_ARGS"
fi
if [[ ${INPUT_PRINT_HASH,,} != "false" || ${INPUT_VERBOSE,,} != "false" ]] ; then
python /app/print-hash.py ${INPUT_PACKAGES_DIR%%/}
fi