Skip to content

Cycle build publishes itself - #2098

Merged
stonebig merged 2 commits into
winpython:masterfrom
stonebig:cycle-build-publishes-itself
Sep 6, 2026
Merged

stonebig merged 2 commits into
winpython:masterfrom
stonebig:cycle-build-publishes-itself

Conversation

@stonebig

@stonebig stonebig commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Two commits: the workflow change below, and 2026-04 b1 update carrying the 2026-04 b1 lockfile refresh (including the new 3.15 slim pair, which is what takes the cycle to nine buildable legs).

The cycle workflow built one Python version per dispatch and stopped at upload-artifact, so a cycle cost five dispatches, five artifact downloads (~2 GB) and a release upload done by hand. This makes one dispatch build the whole cycle and hands the build its own publishing.

One dispatch per cycle

cycle_config.py hangs the version, tarball and paths off each matrix leg rather than the job, so a leg names its own Python and the matrix can be (Python × flavor) instead of flavor alone. python_versionf gains all, which takes every Python the cycle has lockfiles for and quietly skips the ones whose lockfiles are not committed yet. A single version still dispatches exactly as before, through the same code path.

The build publishes its own output

A new publish input (default on) makes one job open a draft release, so the legs cannot race to create it, and each leg then uploads its own files with --clobber. Re-running a missing flavor lands on the release the others are already on. With publish off, everything stays artifacts as it does today.

The tag comes from the cycle file: <cycle name with dashes><release_level>, so 2026_04 at b1 publishes as 2026-04b1, and a respin is a release_level bump rather than a tag to invent. A release_tag key overrides it. There is deliberately no date in the tag — a date would fix it to a run rather than a release, so re-running one flavor tomorrow would open a second release, and the download URLs the site publishes outlive the run. A tag git would refuse as a ref is rejected in the 5-second config job rather than an hour into a build.

Smaller things that came with it

  • With publish on, the artifact keeps only the metadata the changelog commit needs, so a 670 MB slim.7z is no longer re-zipped by upload-artifact for nothing.
  • fail-fast goes off: a cycle is nine legs, and one bad flavor should not bin the eight that built.
  • permissions: {} at the top with least privilege per job, persist-credentials: false on the build checkout, and the dispatch inputs passed through env: rather than interpolated into the shell.

Testing

Not yet run on Actions — it needs a dispatch. Checked statically against what the script emits: every matrix.leg.* and needs.config.outputs.* the workflow reads exists on every leg in all six dispatch choices, no stale matrix.flavor references, the artifact steps are mutually exclusive, and dotwheelhouse keeps its exact value. cycles/2026_04.toml yields 9 legs and cycles/2026_03.toml the 10 that cycle shipped. The tag derivation was checked over four cases and five malformed tags, and the upload shell logic ran against a mock publish_output, including a slash-bearing tag and the guard that fails a leg producing no binary.

Worth a trial dispatch on a fork with publish off, then one with it on, before this drives a real cycle.

The per-cycle copies (github_workflows_build-*.yml) are left in place: only 2026_03 has a matching TOML today, and this path has not had a green run yet. They can go once it has.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MBk5k7WdpPvx4SUFyEk3U3

stonebig and others added 2 commits September 6, 2026 13:40
The cycle workflow built one Python version per dispatch and stopped at
upload-artifact, so a cycle cost five dispatches, five artifact downloads
and a release upload done by hand.

cycle_config.py now hangs the version, tarball and paths off each matrix
leg rather than the job, so a leg names its own Python and "all" builds
the whole cycle in one go. It also derives the release tag from the cycle
name and release level -- 2026_04 at b1 publishes as 2026-04b1 -- which a
release_tag key overrides, and refuses a tag git would not take as a ref
while that still costs five seconds rather than an hour of build.

One job opens the draft release, so the legs cannot race to create it,
and each leg uploads its own files with --clobber: re-running a missing
flavor lands on the release the others are already on. No date in the
tag, for that reason and because the download URLs outlive the run. With
publish off the output stays artifacts as before; with it on the artifact
keeps only the metadata the changelog commit needs, so a 670 MB slim.7z
is no longer re-zipped to no purpose.

fail-fast goes off with it: a cycle is nine legs now, and one bad flavor
should not bin the eight that built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBk5k7WdpPvx4SUFyEk3U3
@stonebig
stonebig merged commit fa2a118 into winpython:master Sep 6, 2026
2 checks passed
@stonebig
stonebig deleted the cycle-build-publishes-itself branch September 6, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant