Skip to content

v8.1.0 - #264

Merged
Paebbels merged 35 commits into
mainfrom
dev
Sep 16, 2026
Merged

v8.1.0#264
Paebbels merged 35 commits into
mainfrom
dev

Conversation

@pytooling-claude

@pytooling-claude pytooling-claude Bot commented Sep 15, 2026

Copy link
Copy Markdown

New Features

  • CheckReleaseVersion.yml - a new job template refusing a release before it is tagged (Refuse a release before it's tagged, if its version is already released or doesn't match the code #261). Its VersionCheck job compares the version from the pull-request title or tag with the __version__ in the Python code, and RegistryCheck asks a package registry whether that version is already released. Both run early in the pipeline, so a missing version bump or a duplicate release shows within the first minutes.
    • CompletePipeline.yml calls it as VersionCheck and gates TriggerTaggedRelease, ReleasePage and PublishOnPyPI on its result.
    • New inputs on CompletePipeline.yml: version_file (default '__init__.py'), check_pypi_duplicate (default 'true'), pypi_url (default 'https://pypi.org') and pypi_upload_url (default 'https://upload.pypi.org/legacy/').
  • UpdateVersionBranch.yml - proposes the update of a major-version branch as a pull-request, titled like Updating r8 from v8.1.0 (Add the 'UpdateVersionBranch' and 'CheckMarketplaceMetadata' job templates #260). A new major branches off the highest existing lower major, and self-references (<owner>/<repo>[/<path>]@<ref>, workflow-status badges) are rewritten on an update/<branch> branch, so no consumable branch is force-pushed. Proven in pyTooling/SynchronizeForks across v1.0.0 and v1.0.1.
  • CheckMarketplaceMetadata.yml - validates an action's metadata against the GitHub Marketplace rules on every push: file at the repository root, name, description shorter than 125 characters, branding icon and colour, public repository (Add the 'UpdateVersionBranch' and 'CheckMarketplaceMetadata' job templates #260). The Marketplace itself checks this only at publish time, in a web form.
  • PrepareJob.yml: new input publish_pages_on and output publish_pages (Let the caller choose the refs on which GitHub Pages are published #259) - a newline separated list of or-ed conditions on the pipeline's ref: default-branch, main-branch, development-branch, release-branch, release-tag, nightly-tag, branch=<regexp> and tag=<regexp>. CompletePipeline.yml forwards it and gates PublishToGitHubPages on the result, so a repository chooses when GitHub Pages are published. An unknown condition or a broken regular expression fails every pipeline, not only the tag run.
  • PublishOnPyPI.yml: new input dry_run, forwarded by CompletePipeline.yml as pypi_dry_run (Add a dry-run mode to PublishOnPyPI #256). The two twine upload steps are replaced by one twine check, so a pipeline building a package that is never published validates its metadata instead of failing at the upload.
  • PublishOnPyPI.yml: new input pypi_upload_url, passed to twine upload --repository-url, so packages can go to another registry (Refuse a release before it's tagged, if its version is already released or doesn't match the code #261).
  • Pipeline.yml - this repository releases itself from a workflow of its own (Move the release into a workflow of its own #265). The release logic used to sit inside _Checking_JobTemplates.yml, a test workflow, which is why the v8.0.0 tag had to be created by hand. The five _Checking_*.yml workflows keep their own push trigger and their own, separately readable runs; Pipeline.yml holds no test jobs.
    • Prepare classifies the commit, and Verifications waits for the runs of those five workflows for that commit and fails if one of them didn't succeed - one request per poll covers all five, a workflow that concluded is dropped from the wait, and the interval and timeout are the poll_interval (15 s) and poll_timeout (6600 s) dispatch inputs.
    • TriggerTaggedRelease then tags the release commit. A tag created with the pipeline's token raises no push event, so the job starts this workflow again through workflow_dispatch at the tag; in that run ReleasePage publishes the notes and UpdateVersionBranch opens the pull-request moving r8.
    • It runs on every branch, so the roll-up of the five verification workflows is available for any commit, while the release jobs need a merge commit on main or a release tag.

Changes

Bug Fixes

  • A tag publishes to GitHub Pages again (8fc18bc). CompletePipeline.yml guards PublishToGitHubPages on the ref, because the github-pages environment rejects a deployment from a feature branch before a runner is assigned. The guard was an allowlist of the default branch and dev; on a tag push github.ref_name is the tag, so the job was skipped - silently, on the release run, whose documentation matters most. github.ref_type == 'tag' is admitted now, feature branches still aren't.
  • PublishReleaseNotes.yml keeps a release page's existing text when re-run, instead of replacing it with the preliminary notes (Add a dry-run mode to PublishOnPyPI #256).

Documentation

  • New pages CheckReleaseVersion.rst, UpdateVersionBranch.rst and CheckMarketplaceMetadata.rst, listed in their categories and in the templates overview.
  • CompletePipeline.rst documents version_file, check_pypi_duplicate, pypi_url, pypi_upload_url, pypi_dry_run and publish_pages_on; its Behavior names the new checks and what they refuse.
  • PublishOnPyPI.rst documents dry_run and pypi_upload_url; PrepareJob.rst documents publish_pages_on and publish_pages; Parameters.rst documents package_version.
  • Development.rst gains the rule behind the Pages fix: a condition gating a job on the ref is a blocklist of refs that cannot work, never an allowlist of refs that have been seen to work.

GitHub Pipeline

  • _Checking_JobTemplates.yml runs CheckReleaseVersion.yml for an unreleased version of pyTooling, and _Checking_Parameters.yml checks package_version and package_version_file for the simple and the namespace fixture package.
  • _Checking_Parameters.yml checks publish_pages for a branch, a release tag and a nightly tag.
  • The release jobs left _Checking_JobTemplates.yml for Pipeline.yml (Move the release into a workflow of its own #265). PublishOnPyPI there became a dry-run test that runs on every pipeline instead of only on a release tag, and both fixture pipelines set pypi_dry_run: 'true'.
  • latex_table_style = ["booktabs"] in doc/conf.py. Sphinx' default adds colorrows, which makes colortbl's \CT@everycr and \everycr expand into each other until TeX's input stack is full - the PDF build died in the first table of the documentation. Tables keep the booktabs rules and lose their alternating row colours.

Known Issues

  • Verifications occupies a runner while it waits - up to poll_timeout, with the job's two hour timeout-minutes behind it.
  • A failed run has to be re-run completely - gh run rerun <id>, never gh run rerun <id> --failed, and only after the run has finished. CleanupArtifacts is guarded by !cancelled(), so it deletes the intermediate artifacts even when the job consuming them failed; re-running just the failed jobs then stops at Unable to download artifact(s): Artifact not found. This affects every pipeline built from CompletePipeline.yml, not only this repository.

Related Issues and Pull-Requests

Note

Why 8.1.0 and not 9.0.0. No parameter changes its meaning: every change either adds an input with a default that keeps the previous behaviour, or makes a check that was already there refuse a release it should never have allowed (#261).

Two defaults are worth naming. check_pypi_duplicate defaults to 'true', so a release commit asks PyPI - a package that isn't published there answers 404 and passes. And VersionCheck now fails where it used to be skipped: a repository keeping __version__ somewhere other than the package's root __init__.py has to set version_file. Every repository calling CompletePipeline.yml keeps it in the root __init__.py.

claude-code and others added 26 commits September 8, 2026 06:31
Both verification pipelines fail on every tag at *⤴ Publish Python wheel package to PyPI* -
`_Checking_SimplePackage_Pipeline.yml` and `_Checking_NamespacePackage_Pipeline.yml`, on v7.14.1 and
again on v7.15.0. `myPackage` and `myFramework.Extension` are fixtures that exist to exercise the job
templates; nobody publishes them. The failure only appears on tags, which is exactly the run someone
checks before cutting a release.

`PublishOnPyPI.yml` gains a `dry_run` input. When enabled, the two `twine upload` steps are replaced
by a single `twine check dist/*.whl dist/*.tar.gz`. Everything before them is unchanged, so the
artifact download, the Python setup and the dependency install are still exercised, and the package
metadata is validated rather than merely built.

`CompletePipeline.yml` forwards it as `pypi_dry_run`, defaulting to `'false'` so no consumer changes
behaviour.

Skipping the job entirely - an input that drops `PublishOnPyPI` from the pipeline - was the
alternative. It would leave the job template unverified, which is what the verification pipelines
exist to prevent, so a dry run that still reaches the packages was preferred.

Setting `pypi_dry_run: 'true'` in the two verification pipelines is deliberately *not* part of this
commit. They call `CompletePipeline.yml@dev`, so an input that exists only on this branch makes
GitHub reject the workflow file before a runner is assigned - "This run likely failed because of a
workflow file issue". The switch is flipped in a follow-up once this input is on `dev`.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…lates.

Both were written as local reusable workflows in 'pyTooling/SynchronizeForks' and have run there through two
releases; they are generic and belong here.

'UpdateVersionBranch' proposes the update of a major-version branch as a pull-request, titled like
'Updating r8 from v8.1.0'. A major without a branch is created from the highest existing lower major rather than
from the main branch - a branch cut from the main branch is already identical to it, so there would be nothing to
open a pull-request about. References to the repository itself are rewritten to name the version branch; where a
rewrite is needed it becomes a commit on an update branch, and the pull-request is opened from there.

'CheckMarketplaceMetadata' validates an action's metadata file against the GitHub Marketplace rules on every
push, instead of discovering a violation in the publish form. Publishing itself has no API and stays manual.

The 'workflow_dispatch' trigger both carried in their original repository is dropped: no job template here has
one.

Neither is wired into this repository's own checking workflows. 'UpdateVersionBranch' creates branches and
pull-requests, and 'CheckMarketplaceMetadata' would fail here by design, because this repository has no
'action.yml' at its root.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Taking the template into 'pyTooling/download-artifact' and 'pyTooling/upload-artifact' showed the assumption was
too narrow: both tag releases as 'v1.x.y' while publishing 'v4' ... 'v8' branches, so the branch number is not the
released version's major and deriving it would target 'v1'.

The optional 'major' parameter states it. Empty, the behaviour is unchanged.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
* The check is written in Python via 'shell: python' instead of a shell script with an embedded Python fragment.
  The step reads YAML and validates five rules, which is what Python is for; the metadata is parsed with
  'ruamel.yaml', installed after 'actions/setup-python', and a new 'python_version' parameter selects the
  interpreter.
* 'ubuntu_image' is the first input parameter in both templates and in both documentation pages, matching the
  other job templates.
* The instantiation example names its job 'CheckMarketplace' rather than 'Marketplace', so the instance says
  which template it is.
* One empty line before 'outputs:' in 'UpdateVersionBranch.yml', and its environment block is aligned again -
  'MAIN_BRANCH' had pushed the column.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
`CompletePipeline.yml` guards `PublishToGitHubPages` on the ref, because the `github-pages`
environment rejects a deployment from a feature branch before a runner is assigned - one second, no
steps, no log - so every feature-branch pipeline showed a red job unrelated to its changes.

The guard was written as an allowlist of the two refs that had been observed to deploy: the default
branch and `dev`. On a tag push `github.ref_name` is the tag, so the job is **skipped** - silently,
on the release run, which is the run whose documentation matters most.

Tags are admitted by the environment. pyVHDLModel's v0.39.0 pipeline, still on `@r7`, deployed Pages
from `ref_name = v0.39.0`
(https://github.com/VHDL/pyVHDLModel/actions/runs/30951229155, job `PublishToGitHubPages` success).

`github.ref_type == 'tag'` is added to the condition. Feature branches are still blocked, which is
what the guard was for.

`documentation_steps` defaults to `'html pages'`, so all eleven repositories calling
`CompletePipeline.yml` publish Pages and all eleven would have lost their release-run documentation
on moving to `@r8`. None has moved yet.

`doc/Development.rst` gains the rule this came from: a condition gating a job on the ref is a
blocklist of the refs that cannot work, never an allowlist of the refs that have been seen to work -
an allowlist built from one observed rejection drops every ref that was never tried, and drops it by
skipping, which no pipeline reports.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
PrepareJob evaluates the new 'publish_pages_on' input into the 'publish_pages' output; CompletePipeline forwards
the input and gates 'PublishToGitHubPages' on that output instead of a hard-coded list of refs.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…ions'.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…lib'.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…tplotlib' as well.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…sed or doesn't match the code.

'VersionCheck' gates 'TriggerTaggedRelease', 'ReleasePage' and 'PublishOnPyPI' and fails instead of being skipped;
it compares with the new 'package_version' output of 'Parameters.yml'. The new 'CheckReleaseVersion.yml' asks PyPI
for an existing release ('check_pypi_duplicate'), and 'TagReleaseCommit.yml' refuses an existing tag or release page.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
'CheckReleaseVersion.yml' runs 'VersionCheck' (compare with 'PythonVersion', no 'packaging') and 'RegistryCheck'
(new input 'pypi_url', default 'https://pypi.org'; no 'r' prefix handling). 'CompletePipeline.yml' calls it once
instead of a local job, staying at 20 distinct reusable workflows. Inputs are inlined instead of passed through 'env:',
and 'TagReleaseCommit.yml' exports 'GH_TOKEN' like 'PrepareJob.yml' and 'PublishReleaseNotes.yml'.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…her than PyPI.

'PublishOnPyPI.yml' gains 'pypi_upload_url' (default 'https://upload.pypi.org/legacy/'), passed to 'twine upload
--repository-url'. 'CompletePipeline.yml' gains 'pypi_url', forwarded to 'CheckReleaseVersion.yml', and
'pypi_upload_url', forwarded to 'PublishOnPyPI.yml' - a registry's upload URL isn't its base URL.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
It only named the file in error messages. An empty 'package_version' now fails the check with one message, and
'Parameters.yml' already warns about a missing version file.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
…m through 'env:'.

The three steps read '${{ inputs.* }}', '${{ github.repository }}' and the previous steps' outputs directly, and export
'GH_TOKEN' in the script like 'PrepareJob.yml' and 'PublishReleaseNotes.yml'. A shell variable remains only where the
value is changed: the version without its prefix and the escaped repository name.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
@codacy-production

codacy-production Bot commented Sep 15, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

🟢 Coverage 100.00% diff coverage · +0.00% coverage variation

Metric Results
Coverage variation +0.00% coverage variation
Diff coverage 100.00% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (8e631fc) 45 41 91.11%
Head commit (4b1ca13) 45 (+0) 41 (+0) 91.11% (+0.00%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#264) 1 1 100.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

'Pipeline.yml' classifies the ref, calls the five verification workflows, tags a release commit when all of them
succeeded, and on the tag run publishes the release page and opens the pull-request moving the version branch. The
verification workflows exchange their 'push' trigger for 'workflow_call', and the release jobs leave
'_Checking_JobTemplates.yml'. The fixture pipelines and the template test publish nothing: 'pypi_dry_run' and
'dry_run' are 'true'.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
claude-code and others added 8 commits September 15, 2026 23:16
…over all jobs.

'Pipeline.yml' called the five verification workflows, which collected about 170 jobs into a single run - unreadable
when one of them fails. They keep their own 'push' trigger and their own runs; the new 'Release.yml' waits for their
runs of the release commit, tags it, and on the tag run publishes the release page and opens the version-branch
pull-request.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
'Prepare' -> 'Verifications' could not be exercised before the workflow sits
on 'main': the push trigger is 'main' only, and 'workflow_dispatch' needs the
file on the default branch ('dev'), where it isn't either. The PR branch is
added to the push trigger as a scaffold, to be removed before merging.

The release jobs need no new condition to stay out of the way - 'Classify' in
'PrepareJob.yml' sets 'is_release_commit' for a merge commit on the main
branch only. 'TriggerTaggedRelease' compares the ref a second time regardless,
because creating a tag is the one irreversible step.

'Verifications' waited out its 120 minute timeout when a workflow had no run
for the commit at all: the API reports 'null', which never equals 'completed',
so the 'NO RUN' error below the loop was unreachable. The wait for a run to
appear is now bounded by 'noRunTimeout'.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
'Verifications' queried one workflow at a time and blocked on it, so a
workflow that finished later in the list stayed unseen until its turn came,
and every interval cost one request for the whole wait.

'actions/runs?head_sha=' reports every run of the commit, so one request per
pass now covers all five workflows. Runs are listed newest first, so the first
line for a workflow is its current run and a superseded re-run is ignored -
'per_page=1' had read whichever run was newest. A workflow that concluded is
removed from the wait and neither queried nor printed again.

The interval is 15 s (was 60 s), and the loop has its own timeout, both
overridable per run through the new 'poll_interval' and 'poll_timeout' dispatch
inputs. The job's 'timeout-minutes' stays the outer bound; the loop's default
of 6600 s reports the workflows still pending before that hits.

Local harness '_scratchpad/release-checks/test-poll.py' (stubbed 'gh'): all
five green in one pass, a failure named and refused, a missing run reported as
'NO RUN' without consuming the timeout, a shrinking wait list reporting each
workflow exactly once over three passes, and a refusal when the timeout
expires while runs are still in progress.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
'Release.yml' had 'claude/actions-pipeline' in its push trigger so the gate
could run before the workflow was on 'main'. It is on 'main' now, and the
branch is gone, so the trigger is 'main' alone again.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
Every consumer's main workflow is 'Pipeline.yml' with 'name: Pipeline', and
the workflows this repository checks itself with are prefixed '_Checking_'.
This one is the repository's pipeline, so it takes that name.

'Prepare' and 'Verifications' are useful on any branch: they report in one
job whether the five verification workflows of a commit succeeded, which is
otherwise five runs to read. The push trigger is no longer limited to 'main'.
The release jobs need no new condition - 'is_release_commit' is only true for
a merge commit on the main branch, 'is_release_tag' only at a tag, and
'TriggerTaggedRelease' compares the ref a second time. A branch run skips all
three, as observed on 'claude/actions-pipeline'.

'TagReleaseCommit.yml' defaults its 'workflow' input to 'Pipeline.yml', so the
explicit input is gone with the rename.

Branch runs now cancel their predecessor: a branch only needs the roll-up of
its newest commit, and 'Verifications' occupies a runner until the checks it
waits for finish. 'main' and tags keep the old behaviour.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
The PDF build died in the first table of the document with

    ./myPackage.tex:1260: TeX capacity exceeded, sorry [input stack size=10000].
    <inserted text>          \the \everycr
    l.1260 \end{tabulary}

Sphinx' default 'latex_table_style' is ['booktabs', 'colorrows']. For
'colorrows' it sets colortbl's '\CT@everycr' to the token list '\the\everycr',
while colortbl sets '\everycr' to '\the\CT@everycr': each expands into the
other until TeX's input stack is full. colortbl is 2026/05/01 v1.0l.

The 56 "undefined reference" warnings in that job were a consequence - lualatex
returned 1, latexmk stopped, and nothing resolved the references on a second
pass.

Checked with MiKTeX 26.5 installed locally, the same version the image uses:
the document builds to 275 pages with no capacity error. Tables lose their
alternating row colors and keep the booktabs rules.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
The default release-notes footer credited 'pyTooling/Actions::Release.yml'.
That file was renamed to 'Pipeline.yml', but naming it there would be wrong
either way: the footer appears on a **consumer's** release page, and what
wrote it is the 'PublishReleaseNotes.yml' job template, not this repository's
own pipeline.

The documentation copy of the default is updated with it.

Co-Authored-By: Patrick Lehmann <Paebbels@gmail.com>
@Paebbels
Paebbels merged commit 41364cf into main Sep 16, 2026
173 of 175 checks passed
This was referenced Sep 16, 2026

This branch was successfully deployed

1 active deployment
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.

2 participants