diff --git a/.github/workflows/ArtifactCleanUp.yml b/.github/workflows/ArtifactCleanUp.yml index 41c2e755..f03c480f 100644 --- a/.github/workflows/ArtifactCleanUp.yml +++ b/.github/workflows/ArtifactCleanUp.yml @@ -46,6 +46,29 @@ 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' 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" + 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 64cda901..6becb7cf 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -231,11 +231,12 @@ 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 pyTooling.Packaging import extractVersionInformation + 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 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) @@ -293,7 +303,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@main 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 }} @@ -306,7 +316,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@main 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 }} @@ -507,6 +517,7 @@ jobs: ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@main needs: + - Prepare - UnitTestingParams - UnitTesting - StaticTypeCheck @@ -522,7 +533,6 @@ jobs: with: json: ${{ needs.UnitTestingParams.outputs.artifact_names }} artifact-json-ids: >- - package_all unittesting_html:-* codecoverage_xml:-* codecoverage_json:-* @@ -538,3 +548,7 @@ jobs: documentation_html #documentation_latex #documentation_pdf + 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/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 diff --git a/.github/workflows/_Checking_CleanupArtifacts.yml b/.github/workflows/_Checking_CleanupArtifacts.yml index e291ce78..c1a6add0 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 2bf7a4ff..3f728bfe 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -32,6 +32,12 @@ jobs: system_list: 'ubuntu ubuntu-arm windows windows-arm macos mingw64 clang64 ucrt64' documentation_steps: 'none' + AppTestingParams: + uses: pyTooling/Actions/.github/workflows/Parameters.yml@main + with: + package_name: 'myPackage' + python_version_list: '' # just the default Python version, but on all systems + InstallParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: @@ -132,6 +138,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@main + 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@main needs: @@ -158,8 +177,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 @@ -277,6 +298,7 @@ jobs: ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/CleanupArtifacts.yml@main needs: + - Prepare - UnitTestingParams - PlatformTestingParams - UnitTesting @@ -289,10 +311,10 @@ 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: >- - package_all unittesting_xml:-* unittesting_html:-* codecoverage_xml:-* @@ -305,12 +327,18 @@ jobs: codecoverage_json codecoverage_html statictyping_html + #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. + 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. + 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 }}-* diff --git a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml index 641f18c7..43c89a1d 100644 --- a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml +++ b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml @@ -14,7 +14,8 @@ jobs: with: package_namespace: 'myFramework' package_name: 'Extension' - unittest_python_version_list: '3.11 3.12 3.13 3.14 pypy-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' codecov: 'true' diff --git a/.github/workflows/_Checking_SimplePackage_Pipeline.yml b/.github/workflows/_Checking_SimplePackage_Pipeline.yml index f87c60eb..c6d7ddd3 100644 --- a/.github/workflows/_Checking_SimplePackage_Pipeline.yml +++ b/.github/workflows/_Checking_SimplePackage_Pipeline.yml @@ -13,8 +13,11 @@ jobs: uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@main with: package_name: 'myPackage' - unittest_python_version_list: '3.11 3.12 3.13 3.14 pypy-3.11' + 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' + 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"