From 7c64189e887783e133966d99bb6e8b309b6d0d3e Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 28 Jul 2026 22:50:29 +0000 Subject: [PATCH 1/7] Added application testing to the job template verification. The job template pipeline verified every template except ApplicationTesting. Added AppTestingParams and AppTesting using the same wiring as CompletePipeline: AppTesting depends on ConfigParams, AppTestingParams, UnitTestingParams and Package, reuses the wheel and the 'apptesting_xml' artifact name of UnitTestingParams, and feeds PublishTestResults. PublishTestResults now merges the application test results too (reduce-depth:pytest.tests.app) and uses '!cancelled()', so a skipped or failing AppTesting no longer takes the report - and the chain behind it - down with it. Reduced the pipeline matrices to cut queueing time. Windows, macOS and MSYS2 runners are limited and dominate the wall time, so both pipelines now run on 'ubuntu ubuntu-arm' only. SimplePackage tests Python 3.14 and NamespacePackage tests Python 3.11, so together they still cover both ends of the supported range. Generated with the workflow's own matrix generator: * SimplePackage unit tests: 31 -> 2 jobs * NamespacePackage unit tests: 31 -> 2 jobs * SimplePackage app tests: 7 -> 2 jobs * new AppTesting in JobTemplates: 2 jobs Bumped the fixture packages from 7.13.1 to 7.14.0. Co-Authored-By: Claude Opus 5 --- .github/workflows/_Checking_JobTemplates.yml | 25 ++++++++++++++++++- .../_Checking_NamespacePackage_Pipeline.yml | 5 +++- .../_Checking_SimplePackage_Pipeline.yml | 7 +++++- myFramework/Extension/__init__.py | 2 +- myPackage/__init__.py | 2 +- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index d638c52b..5e8a2ec5 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -32,6 +32,13 @@ jobs: system_list: 'ubuntu ubuntu-arm windows windows-arm macos mingw64 clang64 ucrt64' documentation_steps: 'none' + AppTestingParams: + uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev + with: + package_name: 'myPackage' + python_version_list: '' # just the default Python version + system_list: 'ubuntu ubuntu-arm' + InstallParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev with: @@ -132,6 +139,19 @@ jobs: wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }} + AppTesting: + uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@dev + needs: + - ConfigParams + - AppTestingParams + - UnitTestingParams + - Package + with: + jobs: ${{ needs.AppTestingParams.outputs.python_jobs }} + wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} + apptest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} + apptest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).apptesting_xml }} + PublishCoverageResults: uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@dev needs: @@ -158,8 +178,10 @@ jobs: - UnitTestingParams - UnitTesting - PlatformTesting + - AppTesting + if: ${{ !cancelled() }} # not 'success() || failure()', because that's false if a dependency was skipped with: - additional_merge_args: '-d "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit;reduce-depth:pytest.tests.platform"' + additional_merge_args: '-d "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit;reduce-depth:pytest.tests.platform;reduce-depth:pytest.tests.app"' testsuite-summary-name: ${{ needs.UnitTestingParams.outputs.package_fullname }} merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} codecov: true @@ -305,6 +327,7 @@ jobs: codecoverage_json codecoverage_html statictyping_html + #apptesting_xml:-* documentation_html documentation_latex json2: ${{ needs.PlatformTestingParams.outputs.artifact_names }} diff --git a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml index b0ee96a9..6dfe8103 100644 --- a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml +++ b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml @@ -14,7 +14,10 @@ jobs: with: package_namespace: 'myFramework' package_name: 'Extension' - unittest_python_version_list: '3.11 3.12 3.13 3.14 pypy-3.11' + # Reduced matrix: slow-starting platforms (windows, macos, msys2) have limited runners and dominate the wall time. + # SimplePackage covers Python 3.14, so both pipelines together still test the version range's ends. + unittest_python_version_list: '3.11' + unittest_system_list: 'ubuntu ubuntu-arm' bandit: 'true' pylint: 'true' codecov: 'true' diff --git a/.github/workflows/_Checking_SimplePackage_Pipeline.yml b/.github/workflows/_Checking_SimplePackage_Pipeline.yml index d068e188..8451a350 100644 --- a/.github/workflows/_Checking_SimplePackage_Pipeline.yml +++ b/.github/workflows/_Checking_SimplePackage_Pipeline.yml @@ -13,8 +13,13 @@ jobs: uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@dev with: package_name: 'myPackage' - unittest_python_version_list: '3.11 3.12 3.13 3.14 pypy-3.11' + # Reduced matrix: slow-starting platforms (windows, macos, msys2) have limited runners and dominate the wall time. + # NamespacePackage covers Python 3.11, so both pipelines together still test the version range's ends. + unittest_python_version_list: '3.14' + unittest_system_list: 'ubuntu ubuntu-arm' apptest: 'true' + apptest_python_version_list: '3.14' + apptest_system_list: 'ubuntu ubuntu-arm' bandit: 'true' pylint: 'true' codecov: 'true' diff --git a/myFramework/Extension/__init__.py b/myFramework/Extension/__init__.py index 06ab2d69..954bf398 100644 --- a/myFramework/Extension/__init__.py +++ b/myFramework/Extension/__init__.py @@ -36,7 +36,7 @@ __email__ = "Paebbels@gmail.com" __copyright__ = "2017-2026, Patrick Lehmann" __license__ = "Apache License, Version 2.0" -__version__ = "7.13.1" +__version__ = "7.14.0" __keywords__ = ["GitHub Actions"] __project_url__ = "https://github.com/pyTooling/Actions" __documentation_url__ = "https://pyTooling.github.io/Actions" diff --git a/myPackage/__init__.py b/myPackage/__init__.py index 3bbb6084..3fa36ada 100644 --- a/myPackage/__init__.py +++ b/myPackage/__init__.py @@ -36,7 +36,7 @@ __email__ = "Paebbels@gmail.com" __copyright__ = "2017-2026, Patrick Lehmann" __license__ = "Apache License, Version 2.0" -__version__ = "7.13.1" +__version__ = "7.14.0" __keywords__ = ["GitHub Actions"] __project_url__ = "https://github.com/pyTooling/Actions" __documentation_url__ = "https://pyTooling.github.io/Actions" From 964a7f5bd9816f6a4c5922f0b9e8628e5735f701 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 28 Jul 2026 23:00:10 +0000 Subject: [PATCH 2/7] Fixed the 'package_version_file' log message. The variable summary printed package_directory instead of package_version_file, so the log showed e.g. 'myPackage' where 'myPackage/__init__.py' was meant. Only the message was wrong; the job output itself always used the correct variable. Co-Authored-By: Claude Opus 5 --- .github/workflows/Parameters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Parameters.yml b/.github/workflows/Parameters.yml index b52f7e04..e2fe0ee5 100644 --- a/.github/workflows/Parameters.yml +++ b/.github/workflows/Parameters.yml @@ -215,7 +215,7 @@ jobs: print(f" python_version: {python_version}") print(f" package_fullname: {package_fullname}") print(f" package_directory: {package_directory}") - print(f" package_version_file: {package_directory}") + print(f" package_version_file: {package_version_file}") print(f" artifact_basename: {artifact_basename}") # Write jobs to special file From a0a4cdc6f2ad5a36a4a51abbc3e60eeb33102568 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 29 Jul 2026 06:11:23 +0000 Subject: [PATCH 3/7] Applied review suggestions. * AppTesting in the job template verification now uses the default system list, so all systems are tested with one Python version (2 -> 8 jobs). * Moved the matrix comments onto the value line they describe. Co-Authored-By: Claude Opus 5 --- .github/workflows/_Checking_JobTemplates.yml | 3 +-- .github/workflows/_Checking_NamespacePackage_Pipeline.yml | 4 +--- .github/workflows/_Checking_SimplePackage_Pipeline.yml | 4 +--- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 5e8a2ec5..a6fe3319 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -36,8 +36,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev with: package_name: 'myPackage' - python_version_list: '' # just the default Python version - system_list: 'ubuntu ubuntu-arm' + python_version_list: '' # just the default Python version, but on all systems InstallParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev diff --git a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml index 6dfe8103..7b3c4ad3 100644 --- a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml +++ b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml @@ -14,9 +14,7 @@ jobs: with: package_namespace: 'myFramework' package_name: 'Extension' - # Reduced matrix: slow-starting platforms (windows, macos, msys2) have limited runners and dominate the wall time. - # SimplePackage covers Python 3.14, so both pipelines together still test the version range's ends. - unittest_python_version_list: '3.11' + unittest_python_version_list: '3.11' # SimplePackage covers Python 3.14, so both pipelines together still test the version range's ends. unittest_system_list: 'ubuntu ubuntu-arm' bandit: 'true' pylint: 'true' diff --git a/.github/workflows/_Checking_SimplePackage_Pipeline.yml b/.github/workflows/_Checking_SimplePackage_Pipeline.yml index 8451a350..b91fb6f4 100644 --- a/.github/workflows/_Checking_SimplePackage_Pipeline.yml +++ b/.github/workflows/_Checking_SimplePackage_Pipeline.yml @@ -13,9 +13,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@dev with: package_name: 'myPackage' - # Reduced matrix: slow-starting platforms (windows, macos, msys2) have limited runners and dominate the wall time. - # NamespacePackage covers Python 3.11, so both pipelines together still test the version range's ends. - unittest_python_version_list: '3.14' + unittest_python_version_list: '3.14' # NamespacePackage covers Python 3.11, so both pipelines together still test the version range's ends. unittest_system_list: 'ubuntu ubuntu-arm' apptest: 'true' apptest_python_version_list: '3.14' From 6ef7f1d3994bc147ae043da80769997c48333151 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 31 Jul 2026 19:30:41 +0000 Subject: [PATCH 4/7] Guard artifact deletion with `condition`, fix `VersionCheck`'s prefix comparison **`CleanupArtifacts.yml`: `condition` and `condition2`** Two boolean inputs, defaulting to `true`, placed between each JSON dictionary and its id list: `condition` for the first set, `condition2` for the second. Each is wired into the `if:` of both its compute *and* its delete step, so a disabled set neither resolves names nor deletes anything. Existing callers are unaffected. **`CompletePipeline.yml`: the package artifact survives a tagged run** `ArtifactCleanUp` listed `package_all` in the unconditional set while its `needs:` has `# - PublishOnPyPI` commented out, so on a release-tag run the cleanup and the upload ran concurrently and the wheel could be deleted from under `PublishOnPyPI`. It now moves to the second set with condition2: ${{ ! startsWith(github.ref, 'refs/tags') }} which is the same guard `ArtifactCleanUp.yml` has always had. On a tagged run `PublishOnPyPI` consumes the artifact and deletes it itself. Measured window on pyTooling v8.19.0 (run 30612877620): cleanup 07:34:28-07:34:40, publish started 07:34:58. That release survived only because pyTooling doesn't use `CompletePipeline.yml`. **`CompletePipeline.yml`: `VersionCheck` accepts a tag prefix** `Prepare.outputs.version` comes from the tag and may carry a `v`/`r` prefix, while `extractVersionInformation(...).Version` is a plain `str` that never does, so the string comparison failed for every prefixed-tag repository - pyVHDLModel v0.38.0 and sphinx-reports v0.11.2 both hit it. Both sides are now parsed with `SemanticVersion`, which ignores the prefix, with a parse failure reported as an error rather than crashing the step. Checked on pyTooling 8.17.0 and 8.19.0: `v0.11.2`/`0.11.2`, `v0.38.0`/`0.38.0`, `v8.19.0`/`8.19.0`, `r7.13.1`/`7.13.1` and `0.11.2`/`0.11.2` all compare equal, while `v0.11.2`/`0.11.1` and `v1.0.0`/`0.11.2` still fail as they must. **`ArtifactCleanUp.yml`: deprecation warning** Emits a `::warning` annotation plus a migration note - `package` becomes `json2`/`artifact-json-ids2` guarded by `condition2`, `remaining` becomes `json`/`artifact-json-ids` or `others`. Removed in r8. The message avoids literal expression braces, since Actions substitutes those inside `run:` blocks. **Test pipelines** `_Checking_CleanupArtifacts.yml` moves `package_all` into the guarded second set. `_Checking_JobTemplates.yml` already binds both sets to two different name dictionaries, so the package artifact gets its own `PackageArtifactCleanUp` job using `condition` - which exercises the first-set guard and removes the same race from that pipeline, since it has a `PublishOnPyPI` job too. Co-Authored-By: Patrick Lehmann --- .github/workflows/ArtifactCleanUp.yml | 22 +++++++++++++++++++ .github/workflows/CleanupArtifacts.yml | 18 +++++++++++---- .github/workflows/CompletePipeline.yml | 19 +++++++++++++--- .../workflows/_Checking_CleanupArtifacts.yml | 4 ++++ .github/workflows/_Checking_JobTemplates.yml | 15 ++++++++++++- 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ArtifactCleanUp.yml b/.github/workflows/ArtifactCleanUp.yml index 41c2e755..b65eb2b1 100644 --- a/.github/workflows/ArtifactCleanUp.yml +++ b/.github/workflows/ArtifactCleanUp.yml @@ -46,6 +46,28 @@ jobs: runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" steps: + - name: ⚠️ Deprecation warning + shell: bash + run: | + printf "::warning title=Deprecated workflow::%s\n" \ + "'ArtifactCleanUp.yml' is deprecated and will be removed in r8. Use 'CleanupArtifacts.yml' instead." + printf "\n" + printf "%s\n" "'ArtifactCleanUp.yml' is deprecated and will be removed in r8." + printf "%s\n" "Use 'CleanupArtifacts.yml' instead, which resolves artifact names from the" + printf "%s\n" "'artifact_names' JSON dictionary produced by 'Parameters.yml' and can delete" + printf "%s\n" "two independently guarded sets of artifacts." + printf "\n" + printf "%s\n" "Migration:" + printf "%s\n" " * 'package' -> 'json2' + 'artifact-json-ids2', guarded by 'condition2'." + printf "%s\n" " Set 'condition2' to an expression that is false on a tag ref" + printf "%s\n" " (negate a startsWith check against 'refs/tags'), so that the" + printf "%s\n" " current behaviour is kept: don't delete the package" + printf "%s\n" " artifact on tagged runs, so 'PublishOnPyPI.yml' can consume it." + printf "%s\n" " * 'remaining' -> 'json' + 'artifact-json-ids' (deleted unconditionally)," + printf "%s\n" " or the 'others' input for literal artifact names." + printf "\n" + printf "%s\n" "See 'CompletePipeline.yml' for a worked example." + - name: 🗑️ Delete package Artifacts uses: geekyeggo/delete-artifact@v6 if: ${{ ! startsWith(github.ref, 'refs/tags') }} diff --git a/.github/workflows/CleanupArtifacts.yml b/.github/workflows/CleanupArtifacts.yml index 2c5fab2e..59f62b82 100644 --- a/.github/workflows/CleanupArtifacts.yml +++ b/.github/workflows/CleanupArtifacts.yml @@ -34,6 +34,11 @@ on: required: false default: '{}' type: string + condition: + description: 'Delete the first set of artifacts only if this is true.' + required: false + default: true + type: boolean artifact-json-ids: description: 'Artifacts to be removed by JSON name.' required: false @@ -44,6 +49,11 @@ on: required: false default: '{}' type: string + condition2: + description: 'Delete the second set of artifacts only if this is true.' + required: false + default: true + type: boolean artifact-json-ids2: description: 'Second set of artifacts to be removed by JSON name.' required: false @@ -62,7 +72,7 @@ jobs: steps: - name: 🧮 Compute artifact names id: compute - if: inputs.json != '' && inputs.json != '{}' + if: inputs.condition && inputs.json != '' && inputs.json != '{}' shell: python run: | from json import loads, dumps @@ -100,7 +110,7 @@ jobs: - name: 🗑️ Delete artifacts uses: geekyeggo/delete-artifact@v6 - if: inputs.artifact-json-ids != '' && steps.compute.outputs.artifacts != '' + if: inputs.condition && inputs.artifact-json-ids != '' && steps.compute.outputs.artifacts != '' continue-on-error: true with: name: | @@ -109,7 +119,7 @@ jobs: - name: 🧮 Compute artifact names for second JSON dictionary id: compute2 - if: inputs.json2 != '' && inputs.json2 != '{}' + if: inputs.condition2 && inputs.json2 != '' && inputs.json2 != '{}' shell: python run: | from json import loads, dumps @@ -147,7 +157,7 @@ jobs: - name: 🗑️ Delete artifacts uses: geekyeggo/delete-artifact@v6 - if: inputs.artifact-json-ids2 != '' && steps.compute2.outputs.artifacts != '' + if: inputs.condition2 && inputs.artifact-json-ids2 != '' && steps.compute2.outputs.artifacts != '' continue-on-error: true with: name: | diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index 5c2cb811..484c94da 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -235,7 +235,8 @@ jobs: from pathlib import Path from sys import exit from textwrap import dedent - from pyTooling.Packaging import extractVersionInformation + from pyTooling.Packaging import extractVersionInformation + from pyTooling.Versioning import SemanticVersion expectedVersion = "${{ needs.Prepare.outputs.version }}".strip() @@ -248,7 +249,16 @@ jobs: print(f"expected: {expectedVersion}") print(f"from code: {versionInformation.Version}") - if expectedVersion != versionInformation.Version: + # The expected version is derived from the tag, so it may carry a 'v' or 'r' prefix, while + # a version in Python code never does. Compare the parsed versions, which ignore the prefix. + try: + expected = SemanticVersion.Parse(expectedVersion) + fromCode = SemanticVersion.Parse(versionInformation.Version) + except Exception as ex: + print(f"::error title=CompletePipeline::Can't parse version ('{expectedVersion}' / '{versionInformation.Version}'): {ex}") + exit(2) + + if expected != fromCode: print(f"::error title=CompletePipeline::Expected version ({expectedVersion}) doesn't match the version in Python code ({versionInformation.Version}).") exit(2) @@ -522,7 +532,6 @@ jobs: with: json: ${{ needs.UnitTestingParams.outputs.artifact_names }} artifact-json-ids: >- - package_all unittesting_html:-* codecoverage_xml:-* codecoverage_json:-* @@ -538,3 +547,7 @@ jobs: documentation_html #documentation_latex #documentation_pdf + json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} + condition2: ${{ ! startsWith(github.ref, 'refs/tags') }} # on a tagged run, 'PublishOnPyPI' consumes the package artifact and deletes it itself + artifact-json-ids2: >- + package_all diff --git a/.github/workflows/_Checking_CleanupArtifacts.yml b/.github/workflows/_Checking_CleanupArtifacts.yml index 0f28836b..1a755a73 100644 --- a/.github/workflows/_Checking_CleanupArtifacts.yml +++ b/.github/workflows/_Checking_CleanupArtifacts.yml @@ -59,4 +59,8 @@ jobs: json: ${{ needs.Params.outputs.artifact_names }} artifact-json-ids: >- unittesting_xml:-* + # The package artifact is kept on tagged runs, so 'PublishOnPyPI.yml' can still consume it. + json2: ${{ needs.Params.outputs.artifact_names }} + condition2: ${{ ! startsWith(github.ref, 'refs/tags') }} + artifact-json-ids2: >- package_all diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index a6fe3319..992a31d9 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -295,6 +295,20 @@ jobs: artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} secrets: inherit + PackageArtifactCleanUp: + uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@dev + needs: + - UnitTestingParams + - Install + - PublishOnPyPI + if: ${{ !cancelled() }} # 'PublishOnPyPI' is skipped on a non-release run + with: + json: ${{ needs.UnitTestingParams.outputs.artifact_names }} + # Kept on tagged runs: 'PublishOnPyPI.yml' consumes the package artifact and deletes it itself. + condition: ${{ ! startsWith(github.ref, 'refs/tags') }} + artifact-json-ids: >- + package_all + ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@dev needs: @@ -313,7 +327,6 @@ jobs: with: json: ${{ needs.UnitTestingParams.outputs.artifact_names }} artifact-json-ids: >- - package_all unittesting_xml:-* unittesting_html:-* codecoverage_xml:-* From e67efd5f7fe8ea3040a9cbb95d9d06041a3395b2 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 31 Jul 2026 21:35:27 +0000 Subject: [PATCH 5/7] Review: use `is_release_tag`, drop the extra cleanup job Two review comments on #240. **`condition2` keys off the same flag as `PublishOnPyPI`** `${{ ! startsWith(github.ref, 'refs/tags') }}` was the low-level proxy inherited from `ArtifactCleanUp.yml`. `needs.Prepare.outputs.is_release_tag` is what `PublishOnPyPI` and `ReleasePage` already gate on, so keying the cleanup off its inverse makes producer and consumer agree by construction instead of by two expressions that happen to coincide. It is also more precise: a non-release tag no longer leaves the package artifact behind forever. `Prepare` is added to the `needs:` of `ArtifactCleanUp` in both pipelines, since the flag is only reachable through it. **One cleanup job, not two** `condition` and `condition2` are the same implementation, so testing them separately buys nothing. The extra `PackageArtifactCleanUp` job is gone and `_Checking_JobTemplates.yml` uses `condition2` in its own `ArtifactCleanUp`, as `CompletePipeline.yml` does. That needed the second JSON slot, which was bound to the platform-testing names. Those move to the `others` input, spelled literally - which exercises `others` as a side effect. All three groups are still cleaned: unit-testing artifacts and platform artifacts unconditionally, the package artifact only off a release tag. The deprecation hint in `ArtifactCleanUp.yml` now points at `is_release_tag` too, rather than the `startsWith` form. Co-Authored-By: Patrick Lehmann --- .github/workflows/ArtifactCleanUp.yml | 9 +++--- .github/workflows/CompletePipeline.yml | 3 +- .github/workflows/_Checking_JobTemplates.yml | 34 ++++++++------------ 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ArtifactCleanUp.yml b/.github/workflows/ArtifactCleanUp.yml index b65eb2b1..f03c480f 100644 --- a/.github/workflows/ArtifactCleanUp.yml +++ b/.github/workflows/ArtifactCleanUp.yml @@ -59,10 +59,11 @@ jobs: printf "\n" printf "%s\n" "Migration:" printf "%s\n" " * 'package' -> 'json2' + 'artifact-json-ids2', guarded by 'condition2'." - printf "%s\n" " Set 'condition2' to an expression that is false on a tag ref" - printf "%s\n" " (negate a startsWith check against 'refs/tags'), so that the" - printf "%s\n" " current behaviour is kept: don't delete the package" - printf "%s\n" " artifact on tagged runs, so 'PublishOnPyPI.yml' can consume it." + printf "%s\n" " Set 'condition2' so it is false on a release-tag run - the" + printf "%s\n" " inverse of the 'is_release_tag' output of 'PrepareJob.yml'," + printf "%s\n" " which is the same flag 'PublishOnPyPI.yml' keys off. That" + printf "%s\n" " keeps the current behaviour: the package artifact survives" + printf "%s\n" " a release run, and 'PublishOnPyPI.yml' deletes it itself." printf "%s\n" " * 'remaining' -> 'json' + 'artifact-json-ids' (deleted unconditionally)," printf "%s\n" " or the 'others' input for literal artifact names." printf "\n" diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index 484c94da..6f313027 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -517,6 +517,7 @@ jobs: ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@dev needs: + - Prepare - UnitTestingParams - UnitTesting - StaticTypeCheck @@ -548,6 +549,6 @@ jobs: #documentation_latex #documentation_pdf json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} - condition2: ${{ ! startsWith(github.ref, 'refs/tags') }} # on a tagged run, 'PublishOnPyPI' consumes the package artifact and deletes it itself + condition2: ${{ needs.Prepare.outputs.is_release_tag != 'true' }} # the same flag 'PublishOnPyPI' keys off: it consumes the package artifact and deletes it itself artifact-json-ids2: >- package_all diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 992a31d9..8e729791 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -295,23 +295,10 @@ jobs: artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} secrets: inherit - PackageArtifactCleanUp: - uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@dev - needs: - - UnitTestingParams - - Install - - PublishOnPyPI - if: ${{ !cancelled() }} # 'PublishOnPyPI' is skipped on a non-release run - with: - json: ${{ needs.UnitTestingParams.outputs.artifact_names }} - # Kept on tagged runs: 'PublishOnPyPI.yml' consumes the package artifact and deletes it itself. - condition: ${{ ! startsWith(github.ref, 'refs/tags') }} - artifact-json-ids: >- - package_all - ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@dev needs: + - Prepare - UnitTestingParams - PlatformTestingParams - UnitTesting @@ -342,10 +329,17 @@ jobs: #apptesting_xml:-* documentation_html documentation_latex - json2: ${{ needs.PlatformTestingParams.outputs.artifact_names }} + # The package artifact is kept on a release-tag run: 'PublishOnPyPI' consumes it and deletes + # it itself. Same flag that job keys off. + json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} + condition2: ${{ needs.Prepare.outputs.is_release_tag != 'true' }} artifact-json-ids2: >- - unittesting_xml:-* - unittesting_html:-* - codecoverage_xml:-* - codecoverage_json:-* - codecoverage_html:-* + package_all + # The platform artifacts are unconditional, and both JSON slots are taken, so they are listed + # literally - which exercises the 'others' input as a side effect. + others: | + ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}-* + ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}-* + ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}-* + ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}-* + ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}-* From b86199a7a0c8e477168dce9d2cef4a8284a380bd Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 31 Jul 2026 21:42:25 +0000 Subject: [PATCH 6/7] Let the job-template cleanup run when an unrelated job failed `_Checking_JobTemplates.yml`'s `ArtifactCleanUp` carried no `if:`, so a failed dependency skipped it. With the currently-red `Documentation` jobs that meant the restructured cleanup - the thing this PR changes - never executed in that pipeline, and its artifacts leaked on every failed run. Guarded with `!cancelled()`, matching what `CompletePipeline.yml` already does for the same job. Co-Authored-By: Patrick Lehmann --- .github/workflows/_Checking_JobTemplates.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 8e729791..2f20eb1b 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -311,6 +311,7 @@ jobs: - PublishToGitHubPages - Install - IntermediateCleanUp + if: ${{ !cancelled() }} # match 'CompletePipeline.yml': still clean up when an unrelated job failed with: json: ${{ needs.UnitTestingParams.outputs.artifact_names }} artifact-json-ids: >- From 986428897111b11c448492f10238d062187f3144 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 31 Jul 2026 21:35:27 +0000 Subject: [PATCH 7/7] Review: use `is_release_tag`, drop the extra cleanup job Two review comments on #240. **`condition2` keys off the same flag as `PublishOnPyPI`** `${{ ! startsWith(github.ref, 'refs/tags') }}` was the low-level proxy inherited from `ArtifactCleanUp.yml`. `needs.Prepare.outputs.is_release_tag` is what `PublishOnPyPI` and `ReleasePage` already gate on, so keying the cleanup off its inverse makes producer and consumer agree by construction instead of by two expressions that happen to coincide. It is also more precise: a non-release tag no longer leaves the package artifact behind forever. `Prepare` is added to the `needs:` of `ArtifactCleanUp` in both pipelines, since the flag is only reachable through it. **One cleanup job, not two** `condition` and `condition2` are the same implementation, so testing them separately buys nothing. The extra `PackageArtifactCleanUp` job is gone and `_Checking_JobTemplates.yml` uses `condition2` in its own `ArtifactCleanUp`, as `CompletePipeline.yml` does. That needed the second JSON slot, which was bound to the platform-testing names. Those move to the `others` input, spelled literally - which exercises `others` as a side effect. All three groups are still cleaned: unit-testing artifacts and platform artifacts unconditionally, the package artifact only off a release tag. The deprecation hint in `ArtifactCleanUp.yml` now points at `is_release_tag` too, rather than the `startsWith` form. Co-Authored-By: Patrick Lehmann --- .github/workflows/CompletePipeline.yml | 14 +++++++------- .github/workflows/_Checking_JobTemplates.yml | 8 +++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index 6f313027..5ec22429 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -231,10 +231,10 @@ jobs: if: endsWith(needs.UnitTestingParams.outputs.package_version_file, '.py') shell: python run: | - from os import getenv - from pathlib import Path - from sys import exit - from textwrap import dedent + from os import getenv + from pathlib import Path + from sys import exit + from textwrap import dedent from pyTooling.Packaging import extractVersionInformation from pyTooling.Versioning import SemanticVersion @@ -303,7 +303,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@dev needs: - UnitTestingParams - - PublishTestResults # artificial dependency to delay start when pipeline has free job resources + - PublishTestResults # artificial dependency to delay start when pipeline has free job resources if: ${{ !cancelled() }} # the artificial dependency above is for ordering only and must not gate this job with: python_version: ${{ needs.UnitTestingParams.outputs.python_version }} @@ -316,7 +316,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@dev needs: - UnitTestingParams - - StaticTypeCheck # artificial dependency to delay start when pipeline has free job resources + - StaticTypeCheck # artificial dependency to delay start when pipeline has free job resources if: ${{ !cancelled() }} # the artificial dependency above is for ordering only and must not gate this job with: python_version: ${{ needs.UnitTestingParams.outputs.python_version }} @@ -548,7 +548,7 @@ jobs: documentation_html #documentation_latex #documentation_pdf - json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} + json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} condition2: ${{ needs.Prepare.outputs.is_release_tag != 'true' }} # the same flag 'PublishOnPyPI' keys off: it consumes the package artifact and deletes it itself artifact-json-ids2: >- package_all diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 2f20eb1b..29af6ffa 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -330,14 +330,12 @@ jobs: #apptesting_xml:-* documentation_html documentation_latex - # The package artifact is kept on a release-tag run: 'PublishOnPyPI' consumes it and deletes - # it itself. Same flag that job keys off. - json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} + # The package artifact is kept on a release-tag run: 'PublishOnPyPI' consumes it and deletes it itself. + json2: ${{ needs.UnitTestingParams.outputs.artifact_names }} condition2: ${{ needs.Prepare.outputs.is_release_tag != 'true' }} artifact-json-ids2: >- package_all - # The platform artifacts are unconditional, and both JSON slots are taken, so they are listed - # literally - which exercises the 'others' input as a side effect. + # The platform artifacts are unconditional, and both JSON slots are taken, so they are listed literally. others: | ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}-*