Skip to content

Commit 8278928

Browse files
stonebigclaude
andcommitted
2026-04 cycle, on python-build-standalone 20260825
cycle_config.py now emits only the flavors a Python can build: matching architecture, and a pylock present in the cycle directory. Flavors kept but unbuilt cost no runner, and a cycle with no lockfiles fails instead of going green having built nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DePo4xZA8cKDkHEzbPjHmo
1 parent 5c927fd commit 8278928

3 files changed

Lines changed: 109 additions & 2 deletions

File tree

‎.github/scripts/cycle_config.py‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
Writes to $GITHUB_OUTPUT when set, otherwise stdout, so it can be run locally
66
to see exactly what a workflow run would get. tomllib is stdlib from 3.11, and
77
GitHub runners are newer than that, so this needs no dependency.
8+
9+
The emitted matrix holds only the flavors this Python can actually build: the
10+
right architecture, and a pylock file present in the cycle directory. A flavor
11+
the cycle declares but has no lockfile for -- `whl` today -- costs nothing, so
12+
it can stay declared until it comes back. Paths are relative to the working
13+
directory, which in CI is the checkout root.
814
"""
915
import json
1016
import os
@@ -13,6 +19,27 @@
1319
from pathlib import Path
1420

1521

22+
def buildable_flavors(cfg: dict, requested: str, ver2: str) -> list[dict]:
23+
"""Flavors with the right architecture and a lockfile on disk.
24+
25+
Same two conditions the build job used to re-test in PowerShell, one per
26+
matrix leg. Deciding here instead means a leg with nothing to do never
27+
starts a runner, and a cycle with no lockfiles at all fails loudly rather
28+
than going green having built nothing.
29+
"""
30+
arch = "64F" if requested.endswith("F") else "64"
31+
cycle_dir = Path(cfg["cycle_dir"])
32+
level = cfg.get("release_level", "")
33+
tag = ver2.replace(".", "_")
34+
kept = []
35+
for flavor in cfg["flavors"]:
36+
if str(flavor.get("WINPYARCHDET", "")) != arch:
37+
continue
38+
if (cycle_dir / f"pylock.64-{tag}{flavor['name']}{level}.toml").is_file():
39+
kept.append(flavor)
40+
return kept
41+
42+
1643
def build_config(cfg: dict, requested: str) -> dict:
1744
pythons = cfg["pythons"]
1845
if requested not in pythons:
@@ -27,6 +54,14 @@ def build_config(cfg: dict, requested: str) -> dict:
2754
if short not in entry["src"]:
2855
raise SystemExit(f"{requested}: '{short}' not found in src {entry['src']}")
2956

57+
flavors = buildable_flavors(cfg, requested, entry["ver2"])
58+
if not flavors:
59+
raise SystemExit(
60+
f"{requested}: no pylock.64-{entry['ver2'].replace('.', '_')}<flavor>"
61+
f"{cfg.get('release_level', '')}.toml under {cfg['cycle_dir']}; "
62+
"commit the lockfiles for this cycle before dispatching"
63+
)
64+
3065
return {
3166
"ver2": entry["ver2"],
3267
"src": entry["src"],
@@ -36,7 +71,7 @@ def build_config(cfg: dict, requested: str) -> dict:
3671
"pandoc_source": cfg["pandoc"]["source"],
3772
"pandoc_sha256": cfg["pandoc"]["sha256"],
3873
# consumed by the build job as strategy.matrix via fromJSON
39-
"matrix": json.dumps({"flavor": cfg["flavors"]}, separators=(",", ":")),
74+
"matrix": json.dumps({"flavor": flavors}, separators=(",", ":")),
4075
}
4176

4277

‎.github/workflows/build_winpython_cycle.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
cycle:
1111
description: 'Build cycle (file name in cycles/, without .toml)'
1212
required: true
13-
default: '2026_03'
13+
default: '2026_04'
1414
type: string
1515
python_versionf:
1616
description: 'Python version to build; must exist in that cycle file'

‎cycles/2026_04.toml‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# WinPython 2026-04 build cycle.
2+
#
3+
# Everything that changes from one cycle to the next lives here, so a new cycle
4+
# is a new file rather than a copy of the workflow.
5+
6+
cycle_dir = "winpython/portable/cycle_2026_04"
7+
release_level = ""
8+
9+
[pandoc]
10+
source = "https://github.com/jgm/pandoc/releases/download/3.1.9/pandoc-3.1.9-windows-x86_64.zip"
11+
sha256 = "11eb6dbe5286c9e5edb0cca4412e7d99ec6578ec04158b0b7fe11f7fd96688e5"
12+
13+
# The python-build-standalone tarball for each version offered by the workflow.
14+
# ver2 is the WinPython 4-part version; its first 3 parts must appear in src.
15+
16+
[pythons."3.13"]
17+
ver2 = "3.13.15.1"
18+
src = "https://github.com/astral-sh/python-build-standalone/releases/download/20260825/cpython-3.13.15+20260825-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
19+
sha = "c1dc1e267f2a81493ce6e94837263f648f1eb6d0df73a1492469c1fed025ce8f"
20+
21+
[pythons."3.14"]
22+
ver2 = "3.14.7.1"
23+
src = "https://github.com/astral-sh/python-build-standalone/releases/download/20260825/cpython-3.14.7+20260825-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
24+
sha = "dec3faa3445d9efc4979e6c74a839be0d1039877367db32ee9c5f6bc91fae3c8"
25+
26+
[pythons."3.14F"]
27+
ver2 = "3.14.7.1"
28+
src = "https://github.com/astral-sh/python-build-standalone/releases/download/20260825/cpython-3.14.7+20260825-x86_64-pc-windows-msvc-freethreaded-install_only_stripped.tar.gz"
29+
sha = "485cf42bdd3e49389e754a9cd1dd65e974f9934c883c478f752024a674fc1056"
30+
31+
[pythons."3.15"]
32+
ver2 = "3.15.0.5"
33+
src = "https://github.com/astral-sh/python-build-standalone/releases/download/20260825/cpython-3.15.0rc1+20260825-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
34+
sha = "3c8fa74403a891aeb6e6116154882ef17ad891b6f12aa52f9dda6d29400bedd6"
35+
36+
[pythons."3.15F"]
37+
ver2 = "3.15.0.5"
38+
src = "https://github.com/astral-sh/python-build-standalone/releases/download/20260825/cpython-3.15.0rc1+20260825-x86_64-pc-windows-msvc-freethreaded-install_only_stripped.tar.gz"
39+
sha = "36fd8350ebaf8a577f311099a6683870d0d83f23085b7087159dd53ee03e28cf"
40+
41+
# Flavors become the job matrix. 2026-02 renamed "free" to "dotf", so this
42+
# varies per cycle too and belongs in the cycle file.
43+
44+
[[flavors]]
45+
name = "dot"
46+
PANDOC = "0"
47+
WINPYARCHDET = "64"
48+
formats = { zip = true, "7z" = false, exe = true }
49+
50+
[[flavors]]
51+
name = "slim"
52+
PANDOC = "1"
53+
WINPYARCHDET = "64"
54+
formats = { zip = false, "7z" = true, exe = true }
55+
56+
[[flavors]]
57+
name = "whl"
58+
PANDOC = "0"
59+
WINPYARCHDET = "64"
60+
formats = { zip = false, "7z" = true, exe = false }
61+
62+
[[flavors]]
63+
name = "dotf"
64+
PANDOC = "0"
65+
WINPYARCHDET = "64F"
66+
formats = { zip = true, "7z" = false, exe = true }
67+
68+
[[flavors]]
69+
name = "slimf"
70+
PANDOC = "1"
71+
WINPYARCHDET = "64F"
72+
formats = { zip = false, "7z" = true, exe = true }

0 commit comments

Comments
 (0)