From e912219157d9b7e2022722aaad13dc9fbaca4d8e Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Wed, 8 Jul 2026 21:13:31 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20release=20stamping=20slim=20=E2=80=94?= =?UTF-8?q?=20wheels+tags=20only,=20no=20daily=20bump=20commits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scheduled releases version as .1, never . (the 2026.6.25.641->649 accidental-series mechanism). The release job stamps __init__.py in the build tree without committing back; branch push dropped, tag push kept. release_workspaces no longer writes version.txt / general.yaml pins or README version stamps (obsolete under the compatibility-floor check, PyAutoConf#118). Notebook regen, Colab bumps, API baseline and workspace tags unchanged. R3-core of the version-pinning design review (PyAutoLabs/PyAutoBuild#118). Issue: PyAutoLabs/PyAutoBuild#120 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WGkd3Jf7tjNpWqeU27aShB --- .github/workflows/release.yml | 56 +++++++++++------------------------ docs/internals.md | 4 +-- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 46ba8b7..99e1da0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,12 @@ jobs: run: | export DATE_FORMATTED=`date +"%Y.%-m.%-d"` MINOR_VERSION="${{ github.event.inputs.minor_version }}" - VERSION="${DATE_FORMATTED}.${MINOR_VERSION:-${{ github.run_number }}}" + # Scheduled runs have no inputs, so the minor defaults. NEVER default + # to github.run_number: the 2026.6.25.641→2026.7.6.649 accidental + # series was scheduled runs publishing run-number versions. The date + # already makes a scheduled version unique; a same-day manual dispatch + # must choose its own minor_version (twine loudly rejects duplicates). + VERSION="${DATE_FORMATTED}.${MINOR_VERSION:-1}" RUN_ATTEMPT="${{ github.run_attempt }}" if [ "$RUN_ATTEMPT" -gt "1" ] then @@ -77,7 +82,7 @@ jobs: if [ "${{ needs.resolve_mode.outputs.rehearsal }}" = "true" ] then DEV_SEGMENT=$(( ${{ github.run_number }} * 100 + RUN_ATTEMPT )) - VERSION="${DATE_FORMATTED}.${MINOR_VERSION:-${{ github.run_number }}}.dev${DEV_SEGMENT}" + VERSION="${DATE_FORMATTED}.${MINOR_VERSION:-1}.dev${DEV_SEGMENT}" fi export VERSION echo "::set-output name=version_number::${VERSION}" @@ -401,12 +406,16 @@ jobs: run: | git config --global user.email "richard@rghsoftware.co.uk" git config --global user.name "GitHub Actions bot" - - name: Update version + - name: Stamp version in build tree + # Stamps the wheels only — the stamp is NOT committed back to the library + # repo. Daily "Update version to X" commits to every library main were the + # stale-CI/noise engine behind the June/July 2026 accidental-release + # cascade (PyAutoBuild#118 / #120); pip users get __version__ from the + # stamped wheel, source checkouts use PYAUTO_SKIP_WORKSPACE_VERSION_CHECK. run: | pushd "${{ matrix.project.path }}" VERSION="${{ needs.version_number.outputs.version_number }}" sed -i "s/__version__ = [\.\"\'0-9]*/__version__ = \"$VERSION\"/g" */__init__.py - git commit "-am 'Updated version in __init__ to $VERSION" - name: Tag run: | pushd "${{ matrix.project.path }}" @@ -414,7 +423,6 @@ jobs: git tag -m "Release $VERSION" -a "$VERSION" PAT="${{ secrets[matrix.project.pat] }}" git remote set-url --push origin "https://$PAT@github.com/${{ matrix.project.repository }}.git" - git push git push --tags - name: Build run: | @@ -544,13 +552,6 @@ jobs: - name: Install Jupyter dependency run: | pip install jupyter ipynb-py-convert PyYAML - - name: Update version in README - run: | - VERSION="${{ needs.version_number.outputs.version_number }}" - PACKAGE="${{ matrix.workspace.package }}" - pushd workspace - sed -i "s/$PACKAGE v[0-9]\{4\}\.[0-9]*\.[0-9]*\.[0-9]*/$PACKAGE v$VERSION/g" README.rst README.md 2>/dev/null || true - git add README.rst README.md 2>/dev/null || true - name: Update jupyter notebooks if: "${{ matrix.workspace.generate_notebooks == true }}" run: | @@ -560,33 +561,10 @@ jobs: python3 "$AUTOBUILD_PATH/generate.py" ${{ matrix.workspace.name }} git add *.ipynb git commit -m "Release ${{ needs.version_number.outputs.version_number }}: update notebooks and version" || true - - name: Write workspace version - run: | - VERSION="${{ needs.version_number.outputs.version_number }}" - pushd workspace - echo "$VERSION" > version.txt - python3 - "$VERSION" <<'PY' - import re, sys, pathlib - version = sys.argv[1] - path = pathlib.Path("config/general.yaml") - if not path.exists(): - sys.exit(0) - text = path.read_text() - line_re = re.compile(r"^(?P\s*)workspace_version:\s*.*$", re.MULTILINE) - if line_re.search(text): - text = line_re.sub(lambda m: f"{m.group('indent')}workspace_version: {version}", text) - else: - block_re = re.compile(r"^version:\s*\n", re.MULTILINE) - if block_re.search(text): - text = block_re.sub(f"version:\n workspace_version: {version}\n", text, count=1) - else: - if not text.endswith("\n"): - text += "\n" - text += f"version:\n workspace_version: {version}\n" - path.write_text(text) - PY - git add version.txt config/general.yaml 2>/dev/null || git add version.txt - git commit -m "Release $VERSION: pin workspace version" || true + # The former "Update version in README" and "Write workspace version" steps + # are gone: workspace version pins are obsolete under the compatibility-floor + # check (PyAutoConf#118) — the workspace's `version.minimum_library_version` + # is bumped deliberately when scripts need new API, never by the release cron. - name: Regenerate API audit baseline if: "${{ matrix.workspace.write_api_baseline == true }}" env: diff --git a/docs/internals.md b/docs/internals.md index d8eec32..f7b3316 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -56,7 +56,7 @@ This script does the following for each repo: Before the per-repo loop, `pre_build.sh` invokes `admin_jammy/software/ensure_workspace_labels.sh` to assert the canonical `pending-release` label across every release-window repo (idempotent — a no-op when nothing has drifted). -Release-readiness checking is **not** Build's job — PyAutoBuild is a pure executor. The version-skew check that used to live here (`verify_workspace_versions.sh`, a fail-fast guard against a workspace pinned ahead of its installed library, or a `config/general.yaml` ↔ `version.txt` disagreement) now lives in **PyAutoHeart** as the `version_skew` check feeding `pyauto-heart readiness`. The PyAutoBrain release agent gates on `pyauto-heart readiness` before invoking `pre_build`; a human running `pre_build` directly is trusted to have checked readiness first. See PyAutoHeart for the resolution precedence (`config/general.yaml:version.workspace_version`, then `version.txt`) — identical to `autoconf.workspace.check_version`, so workspace/library mismatches still surface on every script run via each library's `__init__.py`. +Release-readiness checking is **not** Build's job — PyAutoBuild is a pure executor. The version-skew check that used to live here (`verify_workspace_versions.sh`, a fail-fast guard against a workspace pinned ahead of its installed library, or a `config/general.yaml` ↔ `version.txt` disagreement) now lives in **PyAutoHeart** as the `version_skew` check feeding `pyauto-heart readiness`. The PyAutoBrain release agent gates on `pyauto-heart readiness` before invoking `pre_build`; a human running `pre_build` directly is trusted to have checked readiness first. See PyAutoHeart for the resolution precedence (`config/general.yaml:version.workspace_version`, then `version.txt`) — mirroring `autoconf.workspace.check_version`. Since PyAutoBuild#120, releases no longer write workspace version pins or commit `__init__.py` stamps back to library mains (wheels are stamped at build time; tags are the release anchor): the runtime check enforces a compatibility **floor** (`version.minimum_library_version`, bumped deliberately — PyAutoConf#118), and Heart's `version_skew` check needs a follow-up rework to compare floors against release tags rather than stamp-vs-pin. `generate.py` is run from the workspace root with `PYTHONPATH` pointing at `PyAutoBuild/autobuild/`. Only specific safe directories are committed — never `output/`, `output_model/`, or run-generated artefacts. After all workspaces are done, PyAutoBuild itself is committed and pushed, then `gh workflow run release.yml` dispatches the GitHub Actions release. @@ -164,6 +164,6 @@ The workflow (`release.yml`) is manually dispatched with inputs: executor with no ad-hoc skip levers or inline notebook-visualisation job. "Build without releasing" is now exactly what `rehearsal` mode is for.) -`release.yml` is a **pure executor**: it builds, tests-the-install, publishes to PyPI, and commits generated notebooks + version pins to the workspaces. Workspace-integration validation (the old `find_scripts` / `generate_notebooks` / `run_scripts` / `run_notebooks` / `analyze_results` jobs) moved to **PyAutoHeart**'s `workspace-validation.yml`; release readiness is gated upstream by the PyAutoBrain release agent via `pyauto-heart readiness` before this workflow is dispatched. The `script_matrix.py` / `run_python.py` / `run.py` / `aggregate_results.py` primitives remain here and are checked out + reused by the Heart workflow. +`release.yml` is a **pure executor**: it builds, tests-the-install, publishes to PyPI, tags every library and workspace, and commits generated notebooks + Colab URL bumps to the workspaces (version stamps are build-tree-only since #120 — no `__init__.py` commit-backs to library mains, no workspace version pins). Workspace-integration validation (the old `find_scripts` / `generate_notebooks` / `run_scripts` / `run_notebooks` / `analyze_results` jobs) moved to **PyAutoHeart**'s `workspace-validation.yml`; release readiness is gated upstream by the PyAutoBrain release agent via `pyauto-heart readiness` before this workflow is dispatched. The `script_matrix.py` / `run_python.py` / `run.py` / `aggregate_results.py` primitives remain here and are checked out + reused by the Heart workflow. The never-rewrite-history rules live in [`AGENTS.md`](../AGENTS.md) and apply here as everywhere.