Compare commits

...

112 Commits

Author SHA1 Message Date
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
Sviatoslav Sydorenko a56da0b891 Merge pull request #151 from asherf/trusted 2023-05-02 22:30:51 +02:00
Asher Foa e4b9031741 password input is no longer required, since not specifying it implies trusted publishing
Signed-off-by: Asher Foa <1268088+asherf@users.noreply.github.com>
2023-04-27 11:31:44 -04:00
Sviatoslav Sydorenko 5a085bf49e Merge pull request #150 from trail-of-forks/tob-doc-tweaks 2023-04-24 22:34:21 -06:00
William Woodruff 0811f991bd README: small doc tweaks
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-24 09:30:35 -06:00
Sviatoslav Sydorenko f47b34707f 📝🎨 Put OIDC on pedestal @ README
This patch makes sure that the new users would go for the secretless
publishing when integrating the action, from the beginning.
2023-04-24 07:26:17 +02:00
Sviatoslav Sydorenko 7a1a355fb5 🎨 Show GH environments use in README examples
It is a useful protection feature giving the end-users more control
over the release flow and trust.
2023-04-24 07:07:39 +02:00
Sviatoslav Sydorenko 3b6670b0bd Merge pull request #147 from trail-of-forks/tob-stabilize-oidc
README, oidc-exchange: remove beta references
2023-04-22 18:56:18 -06:00
William Woodruff c008c2f40a README: re-add OIDC note
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-22 07:27:01 -06:00
William Woodruff fe431ff9ad README, oidc-exchange: remove beta references
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-21 16:09:58 -06:00
Sviatoslav Sydorenko c542b72dc6 Bump WPS flake8 plugin set to v0.17.0 2023-04-04 03:22:09 +02:00
Sviatoslav Sydorenko f437f577c3 Merge pull request #145 from pypa/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2023-04-04 02:33:37 +02:00
Sviatoslav Sydorenko ba7045370c Revert WPS flake8 hook version to 4.0.1 2023-04-04 01:28:01 +02:00
pre-commit-ci[bot] 6cbdb5439a [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/Lucas-C/pre-commit-hooks.git: v1.3.1 → v1.5.1](https://github.com/Lucas-C/pre-commit-hooks.git/compare/v1.3.1...v1.5.1)
- [github.com/python-jsonschema/check-jsonschema.git: 0.19.2 → 0.22.0](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.19.2...0.22.0)
- [github.com/codespell-project/codespell: v2.2.2 → v2.2.4](https://github.com/codespell-project/codespell/compare/v2.2.2...v2.2.4)
- [github.com/adrienverge/yamllint.git: v1.28.0 → v1.30.0](https://github.com/adrienverge/yamllint.git/compare/v1.28.0...v1.30.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)
- [github.com/PyCQA/pylint.git: v2.15.9 → v3.0.0a6](https://github.com/PyCQA/pylint.git/compare/v2.15.9...v3.0.0a6)
2023-04-03 23:10:34 +00:00
Sviatoslav Sydorenko 82695c57c9 📝 Link the announcement discussions from README
This patch encourages the end-users to share feedback using GitHub
Discussions instead of issues.
2023-04-03 18:19:33 +02:00
Sviatoslav Sydorenko 0bf742be3e Merge pull request #143 from trail-of-forks/tob-rewrite-oidc-refs
This patch updates the user-facing OIDC mentions with the new "Trusted Publishing" term
to make it cohesive with how the PyPI docs names things now.
2023-04-03 17:56:36 +02:00
William Woodruff 30c382209e oidc-exchange: another link
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-03 23:39:43 +09:00
William Woodruff 89ddbeae04 README: retitle, add note
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-03 23:37:32 +09:00
William Woodruff a0f29a5690 Apply suggestions from code review
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2023-04-03 23:14:57 +09:00
William Woodruff 0b567d5b01 oidc-exchange, twine-upload: remove more OIDC refs
...but not all, since some make sense in a debugging
context.

Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-03 21:32:49 +09:00
William Woodruff 4372cb5585 README: replace OIDC with "trusted publishing"
Also updates the link to reference the public documentation
for trusted publishing, rather than the PyPI short help
section (which also needs to be updated).

Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-03 21:26:53 +09:00
Sviatoslav Sydorenko 69efb8cbfb Merge pull request #142 from trail-of-forks/tob-indicate-oidc
Add explanation of why the OIDC publishing was chosen to the log output.
2023-04-03 02:07:09 +02:00
William Woodruff dfde872acc Apply suggestions from code review
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2023-04-02 22:20:08 +09:00
William Woodruff 3d567f44ce twine-upload: expound
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-01 21:09:00 +09:00
William Woodruff 67b747a9c8 oidc-exchange: more explanation
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-04-01 14:41:16 +09:00
Sviatoslav Sydorenko 29930c9cf5 Merge pull request #139 from trail-of-forks/tob-improve-errors
This change improves the error output produced within the OIDC token exchange script by adding a title and a link to the Warehouse documentation for trusted publishers.

Ref #138.
2023-04-01 04:10:38 +02:00
Sviatoslav Sydorenko 9c859e9a77 Merge pull request #140 from hugovk/oidc-whitespace
This change removes accidental double whitespaces from the OIDC CI log that were caused by a misconception that the arguments of `echo` would be joined the same way as Python's implicit string concatenation works.
2023-04-01 04:04:52 +02:00
Hugo van Kemenade 65bf8a81de Remove double spaces 2023-03-29 21:22:09 +03:00
William Woodruff 486ec8dd23 oidc-exchange: improve errors
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-03-30 01:45:41 +09:00
Sviatoslav Sydorenko 48b317d84d Merge PR #136 into unstable/v1
This patch improves the logging detalization of which authentication
mode is selected when the action runs. It uses the `::notice` workflow
command to surface this detail to the workflow run summary page as
annotations.
2023-03-22 16:22:52 +01:00
William Woodruff ae295504b3 twine-upload: increase detail on console notices
Signed-off-by: William Woodruff <william@trailofbits.com>

Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2023-03-22 11:19:01 -04:00
Sviatoslav Sydorenko f3ce18f699 Merge pull request #134 from trail-of-forks/tob-better-errors
oidc-exchange: avoid splitting the error message
2023-03-21 23:13:05 +01:00
William Woodruff ea29ccc08c oidc-exchange: avoid splitting the error message
Signed-off-by: William Woodruff <william@trailofbits.com>
2023-03-21 11:17:21 -04:00
Sviatoslav Sydorenko a3a3bafbb3 🐛 Merge PR #131 from into unstable/v1
This patch fixes the default PyPI upload URL in the action metadata — a regression that happened in v1.8.0.
2023-03-16 10:10:51 +01:00
Jay Chung d5417dc8be 🐛Correct default upload URL
close: #130
2023-03-16 16:39:57 +08:00
Sviatoslav Sydorenko 8ef2b3d46c Merge PR #123 into unstable/v1
This patch implements support for secret-less OIDC-based publishing to
PyPI-like package indexes. The OIDC flow is activated when neither
username, nor password action inputs are set.

The OIDC "token exchange," is an authentication technique that PyPI
(and TestPyPI, and hopefully some future others) supports as an
alternative to long-lived username/password combinations or API
tokens.

OIDC token exchange boils down to the following set of steps:

1. A user (currently only someone in the OIDC beta on PyPI) configured
   a particular GitHub Actions workflow in their repository as a
   trusted OIDC publisher;
2. That workflow uses this action to mint an OIDC token;
3. That OIDC token is sent to PyPI (or another index), which exchanges
   it for a temporary API token;
4. That API token is used as normal.

For the seamless configuration-free upload to work, the end-users are
expected to explicitly assign the `id-token: write` privilege to the
auto-injected `GITHUB_TOKEN` secret on the job level. They should also
set up GHA workflow trust on the PyPI side.

PyPI's documentation: https://pypi.org/help/#openid-connect
Beta test enrollment: https://github.com/pypi/warehouse/issues/12965
2023-03-16 02:48:42 +01:00
William Woodruff 2b46bad8cb OIDC beta support
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
2023-03-15 17:08:09 -04:00
Sviatoslav Sydorenko 22b4d1f125 🐛 Make kebab options fall back for snake_case
The previous release didn't take into account the action defaults so
the promised fallbacks for the old input names didn't work. This patch
corrects that mistake.
2023-03-11 03:06:39 +01:00
Sviatoslav Sydorenko 7104b6e981 Merge branch 'maintenance/kebab-case-inputs' into unstable/v1
This patch normalizes the action inputs to be kebab-case while keeping
the old snake_case fallbacks working.
2023-03-11 02:01:32 +01:00
Sviatoslav Sydorenko f131721e84 🎨 Convert action inputs to use kebab-case
Up until now, the action input names followed the snake_case naming
pattern that is well familiar to the pythonistas. But in GitHub
actions, the de-facto standard is using kebab-case, which is what
this patch achieves.
This style helps make the keys in YAML better standardized and
distinguishable from other identifiers.
The old snake_case names remain functional for the time being and will
not be removed until at least v3 release of this action.
2023-03-11 01:24:52 +01:00
Sviatoslav Sydorenko 32b5e93709 Merge pull request #122 from colindean/empty-token
This patch implements displaying a warning when the password
input passed in to the action is empty.
2023-03-10 20:39:33 +01:00
Colin Dean efcb9babc8 🎨 Warn about empty password/token action input
Before this patch, the warning would say that the token was
expected to start with `pypi-` but it may be unobvious. With this
change, the end-users are warned when they're passing a completely
empty password value.

Fixes #25.
2023-03-10 20:37:53 +01:00
Sviatoslav Sydorenko d2ce3ec872 ⇪ Bump isort to v5.12.0
The previous version had a Poetry packaging problem. This patch
fixes that.
2023-03-10 20:27:46 +01:00
Sviatoslav Sydorenko 0eaf3a11fd Merge pull request #121 from pypa/dependabot/pip/requirements/cryptography-39.0.1
Bump cryptography from 38.0.4 to 39.0.1 in /requirements
2023-03-06 23:17:07 +01:00
dependabot[bot] 6a2da9bc3b Bump cryptography from 38.0.4 to 39.0.1 in /requirements
Bumps [cryptography](https://github.com/pyca/cryptography) from 38.0.4 to 39.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/38.0.4...39.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-08 04:41:02 +00:00
Sviatoslav Sydorenko 7eb3b701d1 Merge pull request #119 from pypa/pre-commit-ci-update-config 2023-01-02 22:23:27 +01:00
Sviatoslav Sydorenko 91e612128c Revert WPS flake8 hook version to 4.0.1 2023-01-02 22:21:39 +01:00
pre-commit-ci[bot] c13a06eb2f [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/add-trailing-comma.git: v2.3.0 → v2.4.0](https://github.com/asottile/add-trailing-comma.git/compare/v2.3.0...v2.4.0)
- [github.com/PyCQA/isort.git: 5.10.1 → 5.11.4](https://github.com/PyCQA/isort.git/compare/5.10.1...5.11.4)
- [github.com/python-jsonschema/check-jsonschema.git: 0.18.3 → 0.19.2](https://github.com/python-jsonschema/check-jsonschema.git/compare/0.18.3...0.19.2)
- [github.com/pre-commit/pre-commit-hooks.git: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks.git/compare/v4.3.0...v4.4.0)
- [github.com/codespell-project/codespell: v2.2.1 → v2.2.2](https://github.com/codespell-project/codespell/compare/v2.2.1...v2.2.2)
- [github.com/PyCQA/flake8.git: 4.0.1 → 6.0.0](https://github.com/PyCQA/flake8.git/compare/4.0.1...6.0.0)
- [github.com/PyCQA/pylint.git: v2.15.3 → v2.15.9](https://github.com/PyCQA/pylint.git/compare/v2.15.3...v2.15.9)
2023-01-02 20:49:16 +00:00
Sviatoslav Sydorenko a260c7e54e Merge pull request #117 from pypa/dependabot/pip/requirements/certifi-2022.12.7 2022-12-10 03:25:32 +01:00
dependabot[bot] 5a2ea379cb Bump certifi from 2022.9.24 to 2022.12.7 in /requirements
Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.9.24 to 2022.12.7.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2022.09.24...2022.12.07)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-09 09:36:08 +00:00
Sviatoslav Sydorenko c7f29f7ade 🐛 Override $HOME in the container with /root
This is necessary to let `python -m site` locate the real install
directories.
This fixes #115 — the bug caused by GitHub passing the value of
`$HOME` from the host system that does not match the container's
expectations.
2022-12-07 02:41:32 +01:00
Sviatoslav Sydorenko 644926c972 🧪 Always run smoke testing in debug mode 2022-12-07 02:34:31 +01:00
Sviatoslav Sydorenko e71a4a4c1d Add support for verbose bash execusion w/ $DEBUG 2022-12-07 00:07:43 +01:00
Sviatoslav Sydorenko e56e8212f4 🐛 Make id always available in twine-upload 2022-12-07 00:07:20 +01:00
Sviatoslav Sydorenko c879b84594 🐛 Use full path to bash in shebang 2022-12-07 00:02:01 +01:00
Sviatoslav Sydorenko 57e7d53102 🐛Ensure the default $PATH value is pre-loaded
This patch imports the system-global profile script to
populate the `$PATH` variable with the typically available binary
paths.

Ref:
https://github.com/pypa/gh-action-pypi-publish/issues/112#issuecomment-1340065840
2022-12-06 23:58:05 +01:00
Sviatoslav Sydorenko ce291dce5b 🎨🐛Fix the branch @ pre-commit.ci badge links 2022-12-06 23:24:07 +01:00
Sviatoslav Sydorenko 102d8ab13f 🐛 Rehardcode devpi port for GHA srv container 2022-12-06 23:18:25 +01:00
Sviatoslav Sydorenko 3a9eaef3ef 🐛Use different ports in/out of GHA containers 2022-12-06 23:13:58 +01:00
Sviatoslav Sydorenko a01fa7442e 🐛 Use localhost @ GHA outside the containers 2022-12-06 23:04:43 +01:00
Sviatoslav Sydorenko ee892fd7f2 🐛Move Twine repository URL definitions to steps 2022-12-06 23:01:39 +01:00
Sviatoslav Sydorenko 47622d7eb0 🎨 Add CI/CD badges to README 2022-12-06 22:59:26 +01:00
Sviatoslav Sydorenko 54b250c7bf 🧪 Add Python linters to pre-commit.ci 2022-12-06 22:52:30 +01:00
Sviatoslav Sydorenko 967acbb201 🎨Reuse existing requirement pins @ smoke-test 2022-12-06 22:30:01 +01:00
Sviatoslav Sydorenko 5755482491 Stop upgrading pip @ GHA unnecessarily 2022-12-06 22:27:07 +01:00
Sviatoslav Sydorenko dfc70e7dc8 Make the stub package dir creation verbose @ GHA 2022-12-06 22:26:35 +01:00
Sviatoslav Sydorenko 1c4183fd91 🎨 Make the GHA steps named 2022-12-06 22:26:08 +01:00
Sviatoslav Sydorenko efa83bf521 Pin setuptools for pkg stub to v65.6.3
This patch is meant to improve the reproducibility of smoke-testing.
2022-12-06 22:20:59 +01:00
Sviatoslav Sydorenko 3f27ae2c90 Drop unnecessary wheel declaration from stub pkg 2022-12-06 22:18:59 +01:00
Sviatoslav Sydorenko ee83abeb35 🎨 Put devpi creds into reusable vars @ GHA 2022-12-06 22:17:42 +01:00
Sviatoslav Sydorenko 08af49986e 🎭 Enable output ANSI-colorization in CI 2022-12-06 22:08:08 +01:00
Sviatoslav Sydorenko 713d81cca0 Update the test job ID to smoke-test 2022-12-06 22:06:28 +01:00
Sviatoslav Sydorenko 3a5c774287 Update the workflow name to 🧪 2022-12-06 22:06:05 +01:00
Sviatoslav Sydorenko 819df810af Rename the GHA workflow to self-smoke-test-action 2022-12-06 22:02:49 +01:00
Sviatoslav Sydorenko fedca4a9a8 🎨Use explicit strings for action input defaults 2022-12-06 21:44:30 +01:00
Sviatoslav Sydorenko 1350b8bd72 🐛 Avoid broken env vars passed by GHA from host
Fixes https://github.com/pypa/gh-action-pypi-publish/issues/112.
2022-12-06 21:40:38 +01:00
Sviatoslav Sydorenko dfae161eab Add a config for pre-commit.ci 2022-12-06 00:17:52 +01:00
Sviatoslav Sydorenko b3f93a1ad6 Expect the GHA workflow to complete in 2 minutes 2022-12-06 00:16:34 +01:00
Sviatoslav Sydorenko 595429bec5 Drop the execute privilege bit from print-hash.py
This is requested by the pre-commit.ci linting
2022-12-06 00:13:22 +01:00
Sviatoslav Sydorenko cde4774fb1 Adjust the GHA workflow per yamllint rules 2022-12-06 00:11:43 +01:00
Sviatoslav Sydorenko a737e68aed Sync funding config with other places I maintain 2022-12-06 00:08:31 +01:00
Sviatoslav Sydorenko f68ac3ea4e Merge PR #111 into unstable/v1
This patch adds a simple GHA-based smoke test that uses devpi to
verify the main success path of the action.
2022-12-05 23:34:15 +01:00
Sviatoslav Sydorenko 7ef975b955 Run CI smoke-tests in PRs 2022-12-05 23:33:07 +01:00
S2 9022aae148 Add test upload workflow 2022-12-06 09:01:24 +11:00
Sviatoslav Sydorenko 5d1679fa6b Use py3.11 user-global site-packages in PYTHONPATH 2022-12-03 04:18:27 +01:00
Sviatoslav Sydorenko d2a2496a01 Switch the runtime from Python 3.9 to Python 3.11 2022-12-03 02:46:45 +01:00
Sviatoslav Sydorenko d7edd4c957 Add user-global site-packages to $PYTHONPATH 2022-11-30 11:37:45 +01:00
Sviatoslav Sydorenko 8d5f27cca4 Install Twine in the user-global site-packages 2022-11-30 11:17:41 +01:00
Sviatoslav Sydorenko b0dc178d8e Disable pip cache dir with an env var 2022-11-30 11:17:12 +01:00
Sviatoslav Sydorenko bbf6e0b2f0 Copy requirements to corresponding dir @ container
This corrects the mistake of setting wrong target directory made
earlier in c54db9c2b7.
2022-11-30 11:03:33 +01:00
Sviatoslav Sydorenko 0b69a8c2df Document broken pkginfo==1.9.0 transitive dep
Refs:
* https://github.com/pypa/gh-action-pypi-publish/issues/107
2022-11-30 10:32:45 +01:00
Sviatoslav Sydorenko c54db9c2b7 Integrate pip-tools-generated constraint files
This patch adds constraint files with the dependency tree
generated by `pip-compile` under Python 3.9. They are now integrated
into the action container image.

Refs:
* https://github.com/pypa/gh-action-pypi-publish/issues/101
* https://github.com/pypa/gh-action-pypi-publish/issues/107
2022-11-30 10:17:33 +01:00
Sviatoslav Sydorenko 480ec4ed58 Inherit yamllint config from the default preset
This patch also fixes the misconfigured rules definition.
2022-11-30 10:05:59 +01:00
Sviatoslav Sydorenko 5fb2f047e2 Drop __token__ from README code usage snippets
This patch reduces the emphasis on the `__token__` value of the `user`
input since it's default anyway. It also adds a separate paragraph
showing how to specify a custom username if the need be.

Ref: https://github.com/pypa/packaging.python.org/issues/1108
2022-07-25 23:13:35 +02:00
Sviatoslav Sydorenko 7bbdccd64f Update the mention of master with unstable/v1 2022-07-25 23:07:43 +02:00
Sviatoslav Sydorenko 328cf89e05 📝 Fix a link to the "Distribution Package" term 2022-07-25 22:55:14 +02:00
14 changed files with 877 additions and 82 deletions
+15 -5
View File
@@ -1,6 +1,16 @@
github: [webknjaz]
patreon: webknjaz
# open_collective: # Replace with a single Open Collective username
---
custom:
- https://www.comebackalive.in.ua/donate
- https://github.com/vshymanskyy/StandWithUkraine#for-maintainers-and-authors
- https://www.paypal.me/webknjazCZ
- https://webknjaz.me
github:
- webknjaz
ko_fi: webknjaz
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://www.paypal.me/webknjazCZ
liberapay: webknjaz
...
@@ -0,0 +1,97 @@
---
name: 🧪
on: # yamllint disable-line rule:truthy
push:
pull_request:
env:
devpi-password: abcd1234
devpi-username: root
devpi-port: 3141
FORCE_COLOR: 1 # Request colored output from CLI tools supporting it
MYPY_FORCE_COLOR: 1 # MyPy's color enforcement
PIP_DISABLE_PIP_VERSION_CHECK: 1
PIP_NO_PYTHON_VERSION_WARNING: 1
PIP_NO_WARN_SCRIPT_LOCATION: 1
PY_COLORS: 1 # Recognized by the `py` package, dependency of `pytest`
TOX_PARALLEL_NO_SPINNER: 1
TOX_TESTENV_PASSENV: >- # Make tox-wrapped tools see color requests
FORCE_COLOR
MYPY_FORCE_COLOR
NO_COLOR
PY_COLORS
PYTEST_THEME
PYTEST_THEME_MODE
jobs:
smoke-test:
runs-on: ubuntu-latest
services:
devpi:
image: muccg/devpi
env:
DEVPI_PASSWORD: ${{ env.devpi-password }}
ports:
- 3141
timeout-minutes: 2
steps:
- name: Check out the action locally
uses: actions/checkout@v3
with:
path: test
- name: Install the packaging-related tools
run: python3 -m pip install build twine
env:
PIP_CONSTRAINT: test/requirements/runtime.txt
- name: Create the stub package importable directory
run: mkdir -pv src/test_package
- name: Populate the stub package `__init__.py`
run: echo '__version__ = "0.1"' > src/test_package/__init__.py
- name: Populate the stub package `README.md`
run: echo "# Test Package" > README.md
- name: Populate the stub package `pyproject.toml`
run: echo "$CONTENTS" > pyproject.toml
env:
CONTENTS: |
[build-system]
requires = [
"setuptools == 65.6.3",
]
build-backend = "setuptools.build_meta"
[project]
name = "test-package"
version = "0.1"
readme = "README.md"
- name: Build the stub package sdist and wheel distributions
run: python3 -m build
- name: Register the stub package in devpi
run: twine register dist/*.tar.gz
env:
TWINE_USERNAME: ${{ env.devpi-username }}
TWINE_PASSWORD: ${{ env.devpi-password }}
TWINE_REPOSITORY_URL: >-
http://localhost:${{
job.services.devpi.ports[env.devpi-port]
}}/${{
env.devpi-username
}}/public/
- name: ✅ Smoke-test the locally checked out action
uses: ./test
env:
DEBUG: >-
true
PATH: utter-nonsense
with:
user: ${{ env.devpi-username }}
password: ${{ env.devpi-password }}
repository-url: >-
http://devpi:${{ env.devpi-port }}/${{ env.devpi-username }}/public/
...
+184
View File
@@ -0,0 +1,184 @@
---
ci:
autoupdate_schedule: quarterly
default_language_version: python3.11
repos:
- repo: https://github.com/asottile/add-trailing-comma.git
rev: v3.0.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/PyCQA/isort.git
rev: 5.12.0
hooks:
- id: isort
args:
- --honor-noqa
- repo: https://github.com/Lucas-C/pre-commit-hooks.git
rev: v1.5.1
hooks:
- id: remove-tabs
- repo: https://github.com/python-jsonschema/check-jsonschema.git
rev: 0.23.2
hooks:
- id: check-github-actions
- id: check-github-workflows
- id: check-jsonschema
name: Check GitHub Workflows set timeout-minutes
args:
- --builtin-schema
- github-workflows-require-timeout
files: ^\.github/workflows/[^/]+$
types:
- yaml
- id: check-readthedocs
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.4.0
hooks:
# Side-effects:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
# Non-modifying checks:
- id: name-tests-test
files: >-
^tests/[^_].*\.py$
- id: check-added-large-files
- id: check-byte-order-marker
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-json
- id: check-symlinks
- id: check-yaml
- id: detect-private-key
# Heavy checks:
- id: check-ast
- id: debug-statements
language_version: python3
- repo: https://github.com/codespell-project/codespell
rev: v2.2.5
hooks:
- id: codespell
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.32.0
hooks:
- id: yamllint
files: \.(yaml|yml)$
types:
- file
- yaml
args:
- --strict
- repo: https://github.com/PyCQA/flake8.git
rev: 6.0.0
hooks:
- id: flake8
alias: flake8-no-wps
name: flake8 WPS-excluded
args:
- --ignore
- >-
D100,
D101,
D103,
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,
WPS326,
WPS332,
WPS347,
WPS360,
WPS421,
WPS422,
WPS432,
WPS433,
WPS437,
WPS440,
WPS441,
WPS453,
- --select
- WPS
additional_dependencies:
- wemake-python-styleguide ~= 0.17.0
- repo: https://github.com/PyCQA/pylint.git
rev: v3.0.0a6
hooks:
- id: pylint
args:
- --disable
- >-
import-error,
invalid-name,
line-too-long,
missing-class-docstring,
missing-function-docstring,
missing-module-docstring,
protected-access,
super-init-not-called,
unused-argument,
wrong-import-position,
- --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
...
+10 -2
View File
@@ -1,2 +1,10 @@
indentation:
indent-sequences: false
---
extends: default
rules:
indentation:
level: error
indent-sequences: false
...
+15 -3
View File
@@ -1,4 +1,4 @@
FROM python:3.9-slim
FROM python:3.11-slim
LABEL "maintainer" "Sviatoslav Sydorenko <wk+pypa@sydorenko.org.ua>"
LABEL "repository" "https://github.com/pypa/gh-action-pypi-publish"
@@ -7,14 +7,26 @@ LABEL "homepage" "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"
COPY requirements requirements
RUN \
pip install --upgrade --no-cache-dir pip-with-requires-python && \
pip install --upgrade --no-cache-dir --prefer-binary twine
PIP_CONSTRAINT=requirements/runtime-prerequisites.txt \
pip install --user --upgrade --no-cache-dir \
-r requirements/runtime-prerequisites.in && \
PIP_CONSTRAINT=requirements/runtime.txt \
pip install --user --upgrade --no-cache-dir --prefer-binary \
-r requirements/runtime.in
WORKDIR /app
COPY LICENSE.md .
COPY twine-upload.sh .
COPY print-hash.py .
COPY oidc-exchange.py .
RUN chmod +x twine-upload.sh
ENTRYPOINT ["/app/twine-upload.sh"]
+118 -47
View File
@@ -1,5 +1,8 @@
[![SWUbanner]][SWUdocs]
[![🧪 GitHub Actions CI/CD workflow tests badge]][GHA workflow runs list]
[![pre-commit.ci status badge]][pre-commit.ci results page]
# PyPI publish GitHub Action
This action allows you to upload your [Python distribution packages]
@@ -7,6 +10,9 @@ in the `dist/` directory to PyPI.
This text suggests a minimalistic usage overview. For more detailed
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].
## 🌇 `master` branch sunset ❗
@@ -17,49 +23,72 @@ tag, or a full Git commit SHA.
## Usage
To use the action add the following step to your workflow file (e.g.
`.github/workflows/main.yml`)
### Trusted publishing
> **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.
```yml
- name: Publish a Python distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
This example jumps right into the current best practice. If you want to
use API tokens directly or a less secure username and password, check out
[how to specify username and password].
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.
To enter the trusted publishing flow, configure this action's job with the
`id-token: write` permission and **without** an explicit username or password:
```yaml
# .github/workflows/ci-cd.yml
jobs:
pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/<your-pypi-project-name>
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
# retrieve your distributions here
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
```
> **Pro tip**: instead of using branch pointers, like `master`, 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.
> **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:
```yaml
- name: Publish package distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
```
_(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
> 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
> the radar.
A common use case is to upload packages only on a tagged commit, to do so add a
filter to the step:
filter to the job:
```yml
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
```
So the full step would look like:
```yml
- name: Publish package
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
```
The example above uses the new [API token][PyPI API token] feature of
PyPI, which is recommended to restrict the access the action has.
The secret used in `${{ secrets.PYPI_API_TOKEN }}` needs to be created on the
settings page of your project on GitHub. See [Creating & using secrets].
## Non-goals
@@ -95,26 +124,28 @@ by putting them into the `dist/` folder prior to running this Action.
For best results, figure out what kind of workflow fits your
project's specific needs.
For example, you could implement a parallel workflow that
For example, you could implement a parallel job that
pushes every commit to TestPyPI or your own index server,
like `devpi`. For this, you'd need to (1) specify a custom
`repository_url` value and (2) generate a unique version
`repository-url` value and (2) generate a unique version
number for each upload so that they'd not create a conflict.
The latter is possible if you use `setuptools_scm` package but
you could also invent your own solution based on the distance
to the latest tagged commit.
You'll need to create another token for a separate host and then
[save it as a GitHub repo secret][Creating & using secrets].
You'll need to create another token for a separate host and then [save it as a
GitHub repo secret][Creating & using secrets] under an environment used in
your job. Though, passing a password would disable the secretless [trusted
publishing] so it's better to configure it instead, when publishing to TestPyPI
and not something custom.
The action invocation in this case would look like:
```yml
- name: Publish package to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
repository-url: https://test.pypi.org/legacy/
```
### Customizing target package dists directory
@@ -127,9 +158,7 @@ would now look like:
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: custom-dir/
packages-dir: custom-dir/
```
### Disabling metadata verification
@@ -140,7 +169,7 @@ check with:
```yml
with:
verify_metadata: false
verify-metadata: false
```
### Tolerating release package file duplicates
@@ -150,12 +179,12 @@ may hit race conditions. For example, when publishing from multiple CIs
or even having workflows with the same steps triggered within GitHub
Actions CI/CD for different events concerning the same high-level act.
To facilitate this use-case, you may use `skip_existing` (disabled by
To facilitate this use-case, you may use `skip-existing` (disabled by
default) setting as follows:
```yml
with:
skip_existing: true
skip-existing: true
```
> **Pro tip**: try to avoid enabling this setting where possible. If you
@@ -178,15 +207,52 @@ It will show SHA256, MD5, BLAKE2-256 values of files to be uploaded.
```yml
with:
print_hash: true
print-hash: true
```
### Specifying a different username
The default username value is `__token__`. If you publish to a custom
registry that does not provide API tokens, like `devpi`, you may need to
specify a custom username and password pair. This is how it's done.
```yml
with:
user: guido
password: ${{ secrets.DEVPI_PASSWORD }}
```
The secret used in `${{ secrets.DEVPI_PASSWORD }}` needs to be created on the
environment page under the settings of your project on GitHub.
See [Creating & using secrets].
In the past, when publishing to PyPI, the most secure way of the access scoping
for automatic publishing was to use the [API tokens][PyPI API token] feature of
PyPI. One would make it project-scoped and save as an environment-bound secret
in their GitHub repository settings, naming it `${{ secrets.PYPI_API_TOKEN }}`,
for example. See [Creating & using secrets]. While still secure,
[trusted publishing] is now encouraged over API tokens as a best practice
on supported platforms (like GitHub).
## License
The Dockerfile and associated scripts and documentation in this project
are released under the [BSD 3-clause license](LICENSE.md).
[🧪 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
[GHA workflow runs list]:
https://github.com/pypa/gh-action-pypi-publish/actions/workflows/self-smoke-test-action.yml?query=branch%3Aunstable%2Fv1
[pre-commit.ci results page]:
https://results.pre-commit.ci/latest/github/pypa/gh-action-pypi-publish/unstable/v1
[pre-commit.ci status badge]:
https://results.pre-commit.ci/badge/github/pypa/gh-action-pypi-publish/unstable/v1.svg
[per-release announcement discussions]:
https://github.com/pypa/gh-action-pypi-publish/discussions/categories/announcements
[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_]:
@@ -195,8 +261,13 @@ https://github.com/pypa/gh-action-pypi-publish/issues/11#issuecomment-530480449
https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
[PyPI API token]: https://pypi.org/help/#apitoken
[Python distribution packages]:
https://packaging.python.org/glossary/#term-distribution-package
https://packaging.python.org/glossary/#term-Distribution-Package
[SWUbanner]:
https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg
[SWUdocs]:
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/
[how to specify username and password]: #specifying-a-different-username
+61 -15
View File
@@ -8,32 +8,78 @@ inputs:
default: __token__
password:
description: Password for your PyPI user or an access token
required: true
repository_url:
required: false
repository-url: # Canonical alias for `repository_url`
description: The repository URL to use
required: false
packages_dir:
repository_url: # DEPRECATED ALIAS; TODO: Remove in v3+
description: >-
[DEPRECATED]
The repository URL to use
deprecationMessage: >-
The inputs have been normalized to use kebab-case.
Use `repository-url` instead.
required: false
default: https://upload.pypi.org/legacy/
packages-dir: # Canonical alias for `packages_dir`
description: The target directory for distribution
required: false
# default: dist # TODO: uncomment once alias removed
packages_dir: # DEPRECATED ALIAS; TODO: Remove in v3+
description: >-
[DEPRECATED]
The target directory for distribution
deprecationMessage: >-
The inputs have been normalized to use kebab-case.
Use `packages-dir` instead.
required: false
default: dist
verify_metadata:
verify-metadata: # Canonical alias for `verify_metadata`
description: Check metadata before uploading
required: false
default: true
skip_existing:
# default: 'true' # TODO: uncomment once alias removed
verify_metadata: # DEPRECATED ALIAS; TODO: Remove in v3+
description: >-
[DEPRECATED]
Check metadata before uploading
deprecationMessage: >-
The inputs have been normalized to use kebab-case.
Use `verify-metadata` instead.
required: false
default: 'true'
skip-existing: # Canonical alias for `skip_existing`
description: >-
Do not fail if a Python package distribution
exists in the target package index
required: false
default: false
# default: 'false' # TODO: uncomment once alias removed
skip_existing: # DEPRECATED ALIAS; TODO: Remove in v3+
description: >-
[DEPRECATED]
Do not fail if a Python package distribution
exists in the target package index
deprecationMessage: >-
The inputs have been normalized to use kebab-case.
Use `skip-existing` instead.
required: false
default: 'false'
verbose:
description: Show verbose output.
required: false
default: false
print_hash:
default: 'false'
print-hash: # Canonical alias for `print_hash`
description: Show hash values of files to be uploaded
required: false
default: false
# default: 'false' # TODO: uncomment once alias removed
print_hash: # DEPRECATED ALIAS; TODO: Remove in v3+
description: >-
[DEPRECATED]
Show hash values of files to be uploaded
deprecationMessage: >-
The inputs have been normalized to use kebab-case.
Use `print-hash` instead.
required: false
default: 'false'
branding:
color: yellow
icon: upload-cloud
@@ -43,9 +89,9 @@ runs:
args:
- ${{ inputs.user }}
- ${{ inputs.password }}
- ${{ inputs.repository_url }}
- ${{ inputs.packages_dir }}
- ${{ inputs.verify_metadata }}
- ${{ inputs.skip_existing }}
- ${{ inputs.repository-url }}
- ${{ inputs.packages-dir }}
- ${{ inputs.verify-metadata }}
- ${{ inputs.skip-existing }}
- ${{ inputs.verbose }}
- ${{ inputs.print_hash }}
- ${{ inputs.print-hash }}
+178
View File
@@ -0,0 +1,178 @@
import os
import sys
from http import HTTPStatus
from pathlib import Path
from typing import NoReturn
from urllib.parse import urlparse
import id # pylint: disable=redefined-builtin
import requests
_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 = """
Trusted publishing exchange failure:
{message}
You're seeing this because the action wasn't given the inputs needed to
perform password-based or token-based authentication. If you intended to
perform one of those authentication methods instead of trusted
publishing, then you should double-check your secret configuration and variable
names.
Read more about trusted publishers at https://docs.pypi.org/trusted-publishers/
Read more about how this action uses trusted publishers at
https://github.com/marketplace/actions/pypi-publish#trusted-publishing
"""
# Rendered if OIDC identity token retrieval fails for any reason.
_TOKEN_RETRIEVAL_FAILED_MESSAGE = """
OpenID Connect token retrieval failed: {identity_error}
This generally indicates a workflow configuration error, such as insufficient
permissions. Make sure that your workflow has `id-token: write` configured
at the job level, e.g.:
```yaml
permissions:
id-token: write
```
Learn more at https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings.
"""
# 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}
"""
# Rendered if the package index's token response isn't valid JSON.
_SERVER_TOKEN_RESPONSE_MALFORMED_JSON = """
Token request failed: the index produced an unexpected
{status_code} response.
This strongly suggests a server configuration or downtime issue; wait
a few minutes and try again.
"""
# Rendered if the package index's token response isn't a valid API token payload.
_SERVER_TOKEN_RESPONSE_MALFORMED_MESSAGE = """
Token response error: the index gave us an invalid response.
This strongly suggests a server configuration or downtime issue; wait
a few minutes and try again.
"""
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::Trusted publishing exchange failure: {msg}", file=sys.stderr)
sys.exit(1)
def debug(msg: str):
print(f"::debug::{msg.title()}", file=sys.stderr)
def get_normalized_input(name: str) -> str | None:
name = f"INPUT_{name.upper()}"
if val := os.getenv(name):
return val
return os.getenv(name.replace("-", "_"))
def assert_successful_audience_call(resp: requests.Response, domain: str):
if resp.ok:
return
match resp.status_code:
case HTTPStatus.FORBIDDEN:
# 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",
)
case HTTPStatus.NOT_FOUND:
# This index does not support OIDC.
die(
"audience retrieval failed: repository at "
f"{domain} does not indicate trusted publishing support",
)
case other:
status = HTTPStatus(other)
# Unknown: the index may or may not support OIDC, but didn't respond with
# 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}",
)
repository_url = get_normalized_input("repository-url")
repository_domain = urlparse(repository_url).netloc
token_exchange_url = f"https://{repository_domain}/_/oidc/github/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)
assert_successful_audience_call(audience_resp, repository_domain)
oidc_audience = audience_resp.json()["audience"]
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))
# Now we can do the actual token exchange.
mint_token_resp = requests.post(
token_exchange_url,
json={"token": oidc_token},
)
try:
mint_token_payload = mint_token_resp.json()
except requests.JSONDecodeError:
# Token exchange failure normally produces a JSON error response, but
# we might have hit a server error instead.
die(
_SERVER_TOKEN_RESPONSE_MALFORMED_JSON.format(
status_code=mint_token_resp.status_code,
),
)
# 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"]
)
die(_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE.format(reasons=reasons))
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)
# This final print will be captured by the subshell in `twine-upload.sh`.
print(pypi_token)
Executable → Regular
View File
+1
View File
@@ -0,0 +1 @@
pip-with-requires-python
+12
View File
@@ -0,0 +1,12 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --allow-unsafe --output-file=requirements/runtime-prerequisites.txt --resolver=backtracking --strip-extras requirements/runtime-prerequisites.in
#
pip-with-requires-python==1.0.1
# via -r requirements/runtime-prerequisites.in
# The following packages are considered to be unsafe in a requirements file:
pip==22.3.1
# via pip-with-requires-python
+9
View File
@@ -0,0 +1,9 @@
twine
# NOTE: Used to detect an ambient OIDC credential for OIDC publishing.
id ~= 1.0
# NOTE: This is pulled in transitively through `twine`, but we also declare
# NOTE: it explicitly here because `oidc-exchange.py` uses it.
# Ref: https://github.com/di/id
requests
+86
View File
@@ -0,0 +1,86 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --allow-unsafe --output-file=requirements/runtime.txt --resolver=backtracking --strip-extras requirements/runtime.in
#
annotated-types==0.5.0
# via pydantic
bleach==6.0.0
# via readme-renderer
certifi==2023.5.7
# via requests
cffi==1.15.1
# via cryptography
charset-normalizer==3.2.0
# via requests
cryptography==41.0.2
# via secretstorage
docutils==0.20.1
# via readme-renderer
id==1.0.0
# via -r requirements/runtime.in
idna==3.4
# via requests
importlib-metadata==6.8.0
# via
# keyring
# twine
jaraco-classes==3.3.0
# via keyring
jeepney==0.8.0
# via
# keyring
# secretstorage
keyring==24.2.0
# via twine
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
more-itertools==9.1.0
# via jaraco-classes
pkginfo==1.9.6
# via twine
pycparser==2.21
# via cffi
pydantic==2.0.2
# via id
pydantic-core==2.1.2
# via pydantic
pygments==2.15.1
# via
# readme-renderer
# rich
readme-renderer==40.0
# via twine
requests==2.31.0
# via
# -r requirements/runtime.in
# id
# requests-toolbelt
# twine
requests-toolbelt==1.0.0
# via twine
rfc3986==2.0.0
# via twine
rich==13.4.2
# via twine
secretstorage==3.3.3
# via keyring
six==1.16.0
# via bleach
twine==4.0.2
# via -r requirements/runtime.in
typing-extensions==4.7.1
# via
# pydantic
# pydantic-core
urllib3==2.0.3
# via
# requests
# twine
webencodings==0.5.1
# via bleach
zipp==3.16.0
# via importlib-metadata
+91 -10
View File
@@ -1,21 +1,102 @@
#! /usr/bin/env bash
#! /bin/bash
if [[ -n "${DEBUG}" ]]
then
set -x
fi
set -Eeuo pipefail
# NOTE: These variables are needed to combat GitHub passing broken env vars
# NOTE: from the runner VM host runtime.
# Ref: https://github.com/pypa/gh-action-pypi-publish/issues/112
export HOME="/root" # So that `python -m site` doesn't get confused
export PATH="/usr/bin:${PATH}" # To find `id`
. /etc/profile # Makes python and other executables findable
export PATH="$(python -m site --user-base)/bin:${PATH}"
export PYTHONPATH="$(python -m site --user-site):${PYTHONPATH}"
function get-normalized-input() {
local var_name=${1}
python -c \
'
from os import getenv
from sys import argv
envvar_name = f"INPUT_{argv[1].upper()}"
print(
getenv(envvar_name) or getenv(envvar_name.replace("-", "_")) or "",
end="",
)
' \
"${var_name}"
}
INPUT_REPOSITORY_URL="$(get-normalized-input 'repository-url')"
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')"
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"
if [[ "${INPUT_USER}" == "__token__" && -z "${INPUT_PASSWORD}" ]] ; 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'
INPUT_PASSWORD="$(python /app/oidc-exchange.py)"
elif [[ "${INPUT_USER}" == '__token__' ]]; then
echo \
'::notice::Using a user-provided API token for authentication' \
"against ${INPUT_REPOSITORY_URL}"
if [[ "${INPUT_REPOSITORY_URL}" =~ pypi\.org ]]; then
echo "${TRUSTED_PUBLISHING_NUDGE}"
fi
else
echo \
'::notice::Using a username + password pair for authentication' \
"against ${INPUT_REPOSITORY_URL}"
if [[ "${INPUT_REPOSITORY_URL}" =~ pypi\.org ]]; then
echo "${TRUSTED_PUBLISHING_NUDGE}"
fi
fi
if [[
"$INPUT_USER" == "__token__" &&
! "$INPUT_PASSWORD" =~ ^pypi-
]]
then
echo \
::warning file='# >>' PyPA publish to PyPI GHA'%3A' \
POTENTIALLY INVALID TOKEN \
'<< ':: \
It looks like you are trying to use an API token to \
authenticate in the package index and your token value does \
not start with '"pypi-"' as it typically should. This may \
cause an authentication error. Please verify that you have \
copied your token properly if such an error occurs.
if [[ -z "$INPUT_PASSWORD" ]]; then
echo \
::warning file='# >>' PyPA publish to PyPI GHA'%3A' \
EMPTY TOKEN \
'<< ':: \
It looks like you have not passed a password or it \
is otherwise empty. Please verify that you have passed it \
directly or, preferably, through a secret.
else
echo \
::warning file='# >>' PyPA publish to PyPI GHA'%3A' \
POTENTIALLY INVALID TOKEN \
'<< ':: \
It looks like you are trying to use an API token to \
authenticate in the package index and your token value does \
not start with '"pypi-"' as it typically should. This may \
cause an authentication error. Please verify that you have \
copied your token properly if such an error occurs.
fi
fi
if ( ! ls -A ${INPUT_PACKAGES_DIR%%/}/*.tar.gz &> /dev/null && \