Skip to content

Commit 46ded47

Browse files
authored
Merge branch 'unstable/v1' into ww/reusable-warn
2 parents ce05432 + db8f07d commit 46ded47

13 files changed

+297
-80
lines changed

.github/FUNDING.yml

+6
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@ ko_fi: webknjaz
1313

1414
liberapay: webknjaz
1515

16+
open_collective: webknjaz
17+
18+
# patreon: webknjaz # not in use because of the ties with ruscism
19+
20+
thanks_dev: u/gh/webknjaz
21+
1622
...

.github/workflows/build-and-push-docker-image.yml

+18-1
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,28 @@ on: # yamllint disable-line rule:truthy
1616
jobs:
1717
smoke-test:
1818
uses: ./.github/workflows/reusable-smoke-test.yml
19+
20+
check: # This job does nothing and is only used for the branch protection
21+
if: always()
22+
23+
needs:
24+
- smoke-test
25+
26+
runs-on: ubuntu-latest
27+
28+
timeout-minutes: 1
29+
30+
steps:
31+
- name: Decide whether the needed jobs succeeded or failed
32+
uses: re-actors/alls-green@release/v1
33+
with:
34+
jobs: ${{ toJSON(needs) }}
35+
1936
build-and-push:
2037
if: github.event_name != 'pull_request'
2138
runs-on: ubuntu-latest
2239
needs:
23-
- smoke-test
40+
- check
2441
timeout-minutes: 10
2542
steps:
2643
- uses: actions/checkout@v4

.github/workflows/reusable-smoke-test.yml

+45-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ env:
2626
PYTEST_THEME_MODE
2727
2828
jobs:
29+
legit-pr:
30+
31+
runs-on: ubuntu-latest
32+
33+
timeout-minutes: 1
34+
35+
steps:
36+
- name: Fail on PRs against illegal branches (`${{ github.base_ref }}`)
37+
if: >-
38+
github.event_name == 'pull_request'
39+
&& github.base_ref != github.event.repository.default_branch
40+
run: exit 1
41+
2942
fail-fast:
3043

3144
strategy:
@@ -53,7 +66,13 @@ jobs:
5366
5467
smoke-test:
5568

56-
runs-on: ubuntu-latest
69+
strategy:
70+
matrix:
71+
os:
72+
- ubuntu-24.04
73+
- ubuntu-22.04
74+
75+
runs-on: ${{ matrix.os }}
5776

5877
services:
5978
devpi:
@@ -86,7 +105,7 @@ jobs:
86105
CONTENTS: |
87106
[build-system]
88107
requires = [
89-
"setuptools == 65.6.3",
108+
"setuptools == 75.8.0",
90109
]
91110
build-backend = "setuptools.build_meta"
92111
@@ -96,8 +115,31 @@ jobs:
96115
readme = "README.md"
97116
- name: Build the stub package sdist and wheel distributions
98117
run: python3 -m build
118+
- name: Create the Rust package directory
119+
run: mkdir -pv rust-example
120+
- name: Initialize a Rust project
121+
run: cargo init
122+
working-directory: rust-example
123+
- name: Populate the Rust package `pyproject.toml`
124+
run: echo "$CONTENTS" > pyproject.toml
125+
env:
126+
CONTENTS: |
127+
[build-system]
128+
requires = [
129+
"maturin ~=1.0",
130+
]
131+
build-backend = "maturin"
132+
working-directory: rust-example
133+
- name: Build the stub package sdist and wheel distributions
134+
run: python3 -m build -o ../dist/
135+
working-directory: rust-example
99136
- name: Register the stub package in devpi
100-
run: twine register dist/*.tar.gz
137+
run: |
138+
for dist in dist/*.tar.gz
139+
do
140+
echo "Registering ${dist}..."
141+
twine register "${dist}"
142+
done
101143
env:
102144
TWINE_USERNAME: ${{ env.devpi-username }}
103145
TWINE_PASSWORD: ${{ env.devpi-password }}

.pre-commit-config.yaml

+11-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
- id: add-trailing-comma
1111

1212
- repo: https://github.com/PyCQA/isort.git
13-
rev: 5.13.2
13+
rev: 6.0.0
1414
hooks:
1515
- id: isort
1616
args:
@@ -22,7 +22,7 @@ repos:
2222
- id: remove-tabs
2323

2424
- repo: https://github.com/python-jsonschema/check-jsonschema.git
25-
rev: 0.29.2
25+
rev: 0.31.1
2626
hooks:
2727
- id: check-github-actions
2828
- id: check-github-workflows
@@ -37,7 +37,7 @@ repos:
3737
- id: check-readthedocs
3838

3939
- repo: https://github.com/pre-commit/pre-commit-hooks.git
40-
rev: v4.6.0
40+
rev: v5.0.0
4141
hooks:
4242
# Side-effects:
4343
- id: end-of-file-fixer
@@ -62,7 +62,7 @@ repos:
6262
language_version: python3
6363

6464
- repo: https://github.com/codespell-project/codespell
65-
rev: v2.2.6
65+
rev: v2.4.1
6666
hooks:
6767
- id: codespell
6868

@@ -78,7 +78,7 @@ repos:
7878
- --strict
7979

8080
- repo: https://github.com/PyCQA/flake8.git
81-
rev: 7.0.0
81+
rev: 7.1.1
8282
hooks:
8383
- id: flake8
8484
args:
@@ -97,6 +97,7 @@ repos:
9797
WPS111,
9898
WPS202,
9999
WPS305,
100+
WPS318,
100101
WPS326,
101102
WPS332,
102103
WPS347,
@@ -109,14 +110,14 @@ repos:
109110
WPS440,
110111
WPS441,
111112
WPS453,
113+
- --max-module-members=8 # WPS202
112114
additional_dependencies:
113-
- flake8-2020 ~= 1.7.0
114-
- flake8-pytest-style ~= 1.6.0
115-
- wemake-python-styleguide ~= 0.19.0
116-
language_version: python3.11 # flake8-commas doesn't work w/ Python 3.12
115+
- flake8-2020 ~= 1.8.1
116+
- flake8-pytest-style ~= 2.1.0
117+
- wemake-python-styleguide ~= 1.0.0
117118

118119
- repo: https://github.com/PyCQA/pylint.git
119-
rev: v3.3.0
120+
rev: v3.3.4
120121
hooks:
121122
- id: pylint
122123
args:

Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.12-slim
1+
FROM python:3.13-slim
22

33
LABEL "maintainer" "Sviatoslav Sydorenko <wk+pypa@sydorenko.org.ua>"
44
LABEL "repository" "https://github.com/pypa/gh-action-pypi-publish"
@@ -12,7 +12,7 @@ ENV PIP_NO_CACHE_DIR 1
1212
ENV PIP_ROOT_USER_ACTION ignore
1313

1414
ENV PATH "/root/.local/bin:${PATH}"
15-
ENV PYTHONPATH "/root/.local/lib/python3.12/site-packages"
15+
ENV PYTHONPATH "/root/.local/lib/python3.13/site-packages"
1616

1717
COPY requirements requirements
1818
RUN \

README.md

+89
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
[![SWUbanner]][SWUdocs]
22

3+
![PyPA badge]
34
[![🧪 GitHub Actions CI/CD workflow tests badge]][GHA workflow runs list]
45
[![pre-commit.ci status badge]][pre-commit.ci results page]
6+
[![GH Sponsors badge]][GH Sponsors URL]
57

68
# PyPI publish GitHub Action
79

@@ -13,6 +15,10 @@ walkthrough check out the [PyPA guide].
1315
If you have any feedback regarding specific action versions, please leave
1416
comments in the corresponding [per-release announcement discussions].
1517

18+
> [!TIP]
19+
> A limited number of usage scenarios is supported, including the
20+
> [PyPA guide] example. See the [non-goals] for more detail.
21+
1622

1723
## 🌇 `master` branch sunset ❗
1824

@@ -131,6 +137,9 @@ same identity.
131137
This GitHub Action [has nothing to do with _building package
132138
distributions_]. Users are responsible for preparing dists for upload
133139
by putting them into the `dist/` folder prior to running this Action.
140+
They are typically expected to do this in a _separate GitHub Actions
141+
CI/CD job_ running before the one where they call this action and having
142+
restricted privileges.
134143

135144
> [!IMPORTANT]
136145
> Since this GitHub Action is docker-based, it can only
@@ -155,6 +164,72 @@ by putting them into the `dist/` folder prior to running this Action.
155164
> sharing the built dists across stages and jobs. Then, use the `needs`
156165
> setting to order the build, test and publish stages.
157166

167+
The expected environment for running `pypi-publish` is the
168+
GitHub-provided Ubuntu VM. We are running a smoke-test against
169+
`ubuntu-latest` in CI but any currently available numbered versions
170+
should do. We'll consider them supported for as long as GitHub itself
171+
supports them.
172+
173+
Running the action in a job that has a `container:` set is not
174+
supported. It might work for you but you're on your own when it breaks.
175+
If you feel the need to use it, it's likely that you're not following
176+
the recommendation of invoking the build automation in a separate job,
177+
which is considered a security issue (especially, when using [Trusted
178+
Publishing][trusted publisher] that may cause privilege escalation and
179+
would enable the attackers to impersonate the GitHub-backed identity of
180+
the repository through transitive build dependency poisoning). The
181+
solution is to have one job (or multiple, in case of projects with
182+
C-extensions) for building the distribution packages, followed by
183+
another that publishes them.
184+
185+
Self-hosted runners are best effort, provided no other unsupported
186+
things influence them. We are unable to test this in CI and they may
187+
break. This is often the case when using custom runtimes and not the
188+
official GitHub-provided VMs. In general, if you follow the
189+
recommendation of building in a separate job, you shouldn't need to run
190+
this action within a self-hosted runner — it should be possible to
191+
build your dists in a self-hosted runner, save them as a GitHub Actions
192+
artifact in that job, and then invoke the publishing job that would run
193+
within GitHub-provided runners, downloading the artifact with the dists
194+
and publishing them. Such separation is the _recommended_/**supported**
195+
way of handling this scenario.
196+
Our understandng is that Trusted publishing is expected to work on
197+
self-hosted runners. It is backed by OIDC. If it doesn't work, you
198+
should probably ask GitHub if you missed something. We wouldn't be able
199+
to assist here.
200+
201+
Trusted Publishing cannot be tested in CI at the moment, sadly. It is
202+
supported and bugs should be reported but it may take time to sort out
203+
as it often requires cross-project collaboration to debug (sometimes,
204+
problems occur due to changes in PyPI and not in the action).
205+
206+
The only case that is explicitly unsupported at the moment is [Trusted
207+
Publishing][trusted publisher] in reusable workflows. This requires
208+
support on the PyPI side and is being worked on. Please, do not report
209+
bugs related to this case. The current recommendation is to put
210+
everything else you want into a reusable workflow but keep the job
211+
calling `pypi-publish` in a top-level one.
212+
213+
Invoking `pypi-publish` from composite actions is unsupported. It is not
214+
tested. GitHub Runners have limitations and bugs in this case. But more
215+
importantly, this is usually an indication of using it insecurely. When
216+
using [Trusted Publishing][trusted publisher], it is imperative to keep
217+
build machinery invocation in a separate job with restrictive privileges
218+
as [Trusted Publishing][trusted publisher] itself requires elevated
219+
permissions to make use of OIDC. Our observation is that the users
220+
sometimes create in-project composite actions that invoke building and
221+
publishing in the same job. As such, we don't seek to support such a
222+
dangerous configuration in the first place. The solution is pretty much
223+
the same as with the previous problem — use a separate job with
224+
dedicated and scoped privileges just for publishing; and invoke that
225+
in-project composite action from a different job.
226+
227+
And finally, invoking `pypi-publish` more than once in the same job is
228+
not considered supported. It may work in a limited number of scenarios
229+
but please, don't do this. If you want to publish to several indexes,
230+
build the dists in one job and add several publishing jobs, one per
231+
upload.
232+
158233

159234
## Advanced release management
160235

@@ -277,6 +352,8 @@ on supported platforms (like GitHub).
277352
The Dockerfile and associated scripts and documentation in this project
278353
are released under the [BSD 3-clause license](LICENSE.md).
279354

355+
[PyPA badge]:
356+
https://img.shields.io/badge/project-yellow?label=PyPA&labelColor=ffd242&color=3775a9
280357

281358
[🧪 GitHub Actions CI/CD workflow tests badge]:
282359
https://github.com/pypa/gh-action-pypi-publish/actions/workflows/build-and-push-docker-image.yml/badge.svg?branch=unstable%2Fv1&event=push
@@ -288,12 +365,24 @@ https://results.pre-commit.ci/latest/github/pypa/gh-action-pypi-publish/unstable
288365
[pre-commit.ci status badge]:
289366
https://results.pre-commit.ci/badge/github/pypa/gh-action-pypi-publish/unstable/v1.svg
290367

368+
[docs badge]:
369+
https://img.shields.io/badge/guide-gray?logo=readthedocs&label=PyPUG&color=white
370+
[PyPUG guide]:
371+
https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/
372+
373+
[GH Sponsors badge]:
374+
https://img.shields.io/badge/%40webknjaz-transparent?logo=githubsponsors&logoColor=%23EA4AAA&label=Sponsor&color=2a313c
375+
[GH Sponsors URL]:
376+
https://github.com/sponsors/webknjaz
377+
291378
[use a full Git commit SHA]:
292379
https://julienrenaux.fr/2019/12/20/github-actions-security-risk/
293380

294381
[per-release announcement discussions]:
295382
https://github.com/pypa/gh-action-pypi-publish/discussions/categories/announcements
296383

384+
[non-goals]: #Non-goals
385+
297386
[Creating & using secrets]:
298387
https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets
299388
[has nothing to do with _building package distributions_]:

0 commit comments

Comments
 (0)