@@ -35,16 +35,55 @@ The general process:
35352 . Create a copy of the upstream build workflow in the ` python-wheels ` repo at
3636 ` .github/workflows/build-<package>.yml ` , where ` <package> ` matches the
3737 project name (e.g. ` build-numpy.yml ` for NumPy`).
38- 3 . Remove any workflow logic not related to the Linux glibc and musl (if
38+ 3 . Create ` docs/packages/<package>.yaml ` declaring the version to build (see
39+ [ Declaring Versions] ( #declaring-versions ) ).
40+ 4 . Remove any workflow logic not related to the Linux glibc and musl (if
3941 present) build processes, support for other architectures and operating
4042 systems (e.g. Windows, Mac OS). This includes the sdist build (unless it is
4143 consumed by a build or test step).
42- 4 . Repeat steps #2 and #3 for the corresponding test workflow, if it is separate
44+ 5 . Repeat steps #2 and #4 for the corresponding test workflow, if it is separate
4345 from the upstream build file.
4446
4547From this point, some customizations are required to enable builds targeting
4648riscv64.
4749
50+ ## Declaring Versions
51+
52+ ` docs/packages/<package>.yaml ` is the source of truth for what a workflow
53+ builds. A brand-new package starts with:
54+
55+ ``` yaml
56+ package-name : <package>
57+ source-code : <repository URL>
58+ license : <SPDX identifier>
59+ versions :
60+ - version : <version>
61+ ` ` `
62+
63+ ` version` is the version the built wheel will carry (its `METADATA` version,
64+ not necessarily the git tag : the workflow derives the tag from it). An entry
65+ with no `tag:` and no `files:` is **pending** : it has not been released yet.
66+ ` _setup.yml` reads the YAML at the start of every run and hands the list of
67+ pending versions to the build jobs as a matrix, so :
68+
69+ - a `pull_request` run builds every pending version and dry-runs the publish;
70+ - a `push` to `main` builds every pending version and publishes it;
71+ - a `workflow_dispatch` with a `version` glob (e.g. `2.5.*`) rebuilds every
72+ matching version, released or not, and republishes it from `main`;
73+ - a run with nothing pending (typically the push that lands `tag:`/`files:`)
74+ skips every job and stays green.
75+
76+ Publishing fills in `tag:`/`files:` under that entry through the documentation
77+ PR (`ci_scripts/update_doc.py`), which retires it from the pending set. The
78+ wheel's version must match a declared entry byte for byte, or the publish
79+ fails : a `setuptools_scm` dev version (`1.0.1.dev0+g...`) where `1.0.0` was
80+ declared is a bug in the build, not something to document.
81+
82+ To **upgrade** an existing package, append a `- version : <new version>` line to
83+ its YAML and open a PR; no workflow change is needed unless the new version
84+ needs one. The nightly `check_versions.py` opens exactly that PR
85+ automatically.
86+
4887# # Workflow Customizations for riscv64
4988
5089# ## riscv64 Runners
@@ -129,26 +168,81 @@ when invoked.
129168# ## Using the python-wheels Repository in Workflows
130169
131170The `python-wheels` repository contains reusable workflows and patch files to
132- apply for certain projects. Every ` build-<package>.yml ` workflow calls
133- ` _publish-wheel.yml ` , which performs the following steps:
171+ apply for certain projects. Every `build-<package>.yml` workflow starts with a
172+ ` setup` job that calls `_setup.yml` to turn the package YAML into the version
173+ matrix, and every other job iterates over that matrix :
174+
175+ ` ` `
176+ on:
177+ workflow_dispatch:
178+ inputs:
179+ version:
180+ description: 'Version glob to (re)build; empty builds every version of docs/packages/numpy.yaml not released yet'
181+ required: false
182+ default: ''
183+ pull_request:
184+ branches: [main]
185+ paths:
186+ - '.github/workflows/build-numpy.yml'
187+ - 'docs/packages/numpy.yaml'
188+ push:
189+ branches: [main]
190+ paths:
191+ - '.github/workflows/build-numpy.yml'
192+ - 'docs/packages/numpy.yaml'
193+
194+ jobs:
195+ setup:
196+ uses: $/.github/workflows/_setup.yml
197+ with:
198+ package: numpy
199+ version: ${{ inputs.version }}
200+
201+ build_wheels:
202+ needs: [setup]
203+ if: needs.setup.outputs.versions != '[]'
204+ name: Build numpy ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64
205+ strategy:
206+ fail-fast: false
207+ matrix:
208+ version: ${{ fromJSON(needs.setup.outputs.versions) }}
209+ python: ["cp312", "cp313", "cp314", "cp314t"]
210+ env:
211+ NUMPY_VERSION: ${{ matrix.version }}
212+ ...
213+ ` ` `
214+
215+ The `if:` guard is required : GitHub Actions rejects a job whose matrix vector is
216+ empty, and `versions` is `[]` whenever nothing is pending. Job outputs are not
217+ per matrix leg, so a job must not hand its version to another through
218+ ` outputs:` ; use `matrix.version` (or an artifact named after it) instead.
219+
220+ Every `build-<package>.yml` workflow ends with a call to `_publish-wheel.yml`,
221+ which performs the following steps :
134222
1352231. Downloads the built wheel(s) from the previous job
1362242. Creates an immutable GitHub Release containing the wheels
137- 3 . Opens a PR against ` docs/packages/<name>.yaml ` documenting the new version
138- (via ` ci_scripts/update_doc.py ` ), which ` ci_scripts/generate_packages_doc.py `
139- later renders into the published Markdown page. With it in place, the
140- ` build-numpy.yml ` script's ` publish ` job looks like this:
225+ 3. Opens a PR against `docs/packages/<name>.yaml` recording the release under
226+ the version's entry (via `ci_scripts/update_doc.py`), which
227+ ` ci_scripts/generate_packages_doc.py` later renders into the published
228+ Markdown page. With it in place, the `build-numpy.yml` script's `publish`
229+ job looks like this :
141230
142231```
143232publish:
144- name: Publish numpy ${{ inputs.version || '2.5.0' }}
145- needs: build_wheels
233+ name: Publish numpy ${{ matrix.version }}
234+ needs: [ setup, build_wheels]
235+ if: needs.setup.outputs.versions != '[ ] '
236+ strategy:
237+ fail-fast: false
238+ matrix:
239+ version: ${{ fromJSON(needs.setup.outputs.versions) }}
146240 permissions:
147241 contents: write
148242 pull-requests: write
149243 uses: $/.github/workflows/_ publish-wheel.yml
150244 with:
151- artifact-pattern: numpy-${{ inputs .version || '2.5.0' }}-*-manylinux_riscv64
245+ artifact-pattern: numpy-${{ matrix .version }}-* -manylinux_riscv64
152246```
153247
154248`permissions` needs `contents: write` and `pull-requests: write` here (not just
@@ -161,15 +255,16 @@ the example.
161255
162256## Testing a New Workflow
163257
164- Open a new draft PR with the workflow(s) included, and include a ` Trigger: ` line
165- in the PR description with a version for each package version you want to build,
166- like so:
258+ Open a new draft PR with the workflow and the package YAML. The `pull_request`
259+ trigger runs the workflow, which builds every version the YAML declares as
260+ pending; to build additional versions, add more `- version:` lines. To force a
261+ rebuild of an already released version, dispatch the workflow on the PR branch
262+ with a version glob:
167263
168- ` Trigger: numpy:v2.5.0 `
169- ` Trigger: numpy:v2.5.1 `
264+ `gh workflow run build-numpy.yml --ref <branch> -f version='2.5.*'`
170265
171- The repository's automation logic will pick up on and trigger the appropriate
172- build workflows for each version. Achieving a passing (green) build may require
266+ (A brand-new workflow can only be dispatched once its first `pull_request` run
267+ has registered it with GitHub.) Achieving a passing (green) build may require
173268several attempts including rework and possible patches, depending on the nature
174269of the failure.
175270
@@ -222,22 +317,29 @@ in dry-run mode. Review the publish job and confirm that it selected exactly the
222317expected wheels and derived the expected normalized package name and version.
223318Pull request runs never create releases or documentation pull requests.
224319
225- Merging the pull request does not publish its artifacts. After merge, a
226- maintainer must dispatch the package workflow again from the ` main ` branch with
227- the reviewed version. The successful ` main ` run:
320+ Merging the pull request pushes the pending version to `main`, which triggers
321+ the package workflow again through its `push` trigger. The successful `main`
322+ run:
228323
2293241. creates a draft GitHub Release targeting the workflow's commit;
2303252. names the release `<normalized-package>-v<version>` and gives its tag a UTC
231326 timestamp suffix, `<normalized-package>-v<version>-<yyyymmddhhmmss>`;
2323273. uploads all selected wheels and any required `gpl-sources.tar` asset;
2333284. publishes the release, at which point repository-level immutable releases
234329 freeze its tag and assets; and
235- 5 . opens or updates the shared documentation pull request with the release tag,
236- wheel filenames, SHA-256 hashes, and ` Requires-Python ` metadata.
330+ 5. opens or updates the shared documentation pull request, filling in the
331+ version's entry with the release tag, wheel filenames, SHA-256 hashes, and
332+ `Requires-Python` metadata.
237333
238334Every `main` run creates a new release; existing releases are never reused or
239335modified. Its description links back to the workflow run that produced it.
240- Publishing fails if immutable releases are disabled.
336+ Publishing fails if immutable releases are disabled, or if the wheel's version
337+ is not declared in the package YAML.
338+
339+ Merging the documentation pull request pushes the now-released entry to `main`
340+ and triggers the workflow once more; with nothing left pending, that run skips
341+ every job. Until then the version is still pending, so any other push touching
342+ the workflow or its YAML rebuilds and re-releases it.
241343
242344The generated `/simple/` package index reads only `docs/packages/*.yaml`. The
243345new release does not become discoverable through pip until the generated
0 commit comments