diff --git a/.github/workflows/ApplicationTesting.yml b/.github/workflows/ApplicationTesting.yml index 6f8322e..1c3ef4d 100644 --- a/.github/workflows/ApplicationTesting.yml +++ b/.github/workflows/ApplicationTesting.yml @@ -94,6 +94,7 @@ jobs: name: ${{ inputs.wheel }} path: install + # TODO: extract step to an Action so package lists are shared with UnitTesting (and GHDL?) - name: Compute pacman/pacboy packages id: pacboy if: matrix.system == 'msys2' @@ -199,7 +200,7 @@ jobs: ${{ inputs.pacboy }} - name: 🐍 Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 if: matrix.system != 'msys2' with: python-version: ${{ matrix.python }} @@ -225,7 +226,7 @@ jobs: python -m pip install --disable-pip-version-check -U install/*.whl - name: ✅ Run application tests (Ubuntu/macOS) - if: matrix.system != 'windows' + if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' ) run: | export ENVIRONMENT_NAME="${{ matrix.envname }}" @@ -240,7 +241,7 @@ jobs: fi - name: ✅ Run application tests (Windows) - if: matrix.system == 'windows' + if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' ) run: | $env:ENVIRONMENT_NAME = "${{ matrix.envname }}" diff --git a/.github/workflows/BuildTheDocs.yml b/.github/workflows/BuildTheDocs.yml index 409992f..3aec994 100644 --- a/.github/workflows/BuildTheDocs.yml +++ b/.github/workflows/BuildTheDocs.yml @@ -37,8 +37,8 @@ jobs: runs-on: ubuntu-24.04 steps: - - name: '❗ Deprecation message' - run: printf "::warning title=%s::%s\n" "Deprecated" "'BuildTheDocs.yml' is not maintained anymore. Please switch to 'SphinxDocumentation.yml', 'LaTeXDocumentation.yml' and 'ExtractConfiguration.yml'." + - name: ⚠️ Deprecation Warning + run: printf "::warning title=%s::%s\n" "Deprecated" "'BuildTheDocs.yml' template is deprecated. Please switch to 'SphinxDocumentation.yml'. See https://pytooling.github.io/Actions/JobTemplate/Documentation/SphinxDocumentation.html" - name: ⏬ Checkout repository uses: actions/checkout@v5 diff --git a/.github/workflows/CheckCodeQuality.yml b/.github/workflows/CheckCodeQuality.yml new file mode 100644 index 0000000..ce32362 --- /dev/null +++ b/.github/workflows/CheckCodeQuality.yml @@ -0,0 +1,201 @@ +# ==================================================================================================================== # +# Authors: # +# Patrick Lehmann # +# # +# ==================================================================================================================== # +# Copyright 2025-2025 The pyTooling Authors # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +# SPDX-License-Identifier: Apache-2.0 # +# ==================================================================================================================== # +name: Code Quality Checking + +on: + workflow_call: + inputs: + ubuntu_image_version: + description: 'Ubuntu image version.' + required: false + default: '24.04' + type: string + python_version: + description: 'Python version.' + required: false + default: '3.13' + type: string + package_directory: + description: 'The package''s directory' + required: true + type: string + requirements: + description: 'Python dependencies to be installed through pip.' + required: false + default: '-r requirements.txt' + type: string + bandit: + description: 'Run bandit checks.' + required: false + default: 'true' + type: string + radon: + description: 'Run radon checks.' + required: false + default: 'true' + type: string + pylint: + description: 'Run pylint checks.' + required: false + default: 'true' + type: string + artifact: + description: 'Name of the package artifact.' + required: true + type: string + +jobs: + Bandit: + name: 🚨 Security Scanning (Bandit) + runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" + if: inputs.bandit == 'true' + + steps: + - name: ⏬ Checkout repository + uses: actions/checkout@v5 + with: + lfs: true + submodules: true + + - name: 🐍 Setup Python ${{ inputs.python_version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python_version }} + + - name: ⚙ Install dependencies for running bandit + run: python -m pip install --disable-pip-version-check bandit + + - name: 👮 Bandit + id: bandit + if: inputs.artifact != '' + run: | + set +e + + ANSI_LIGHT_RED=$'\x1b[91m' + ANSI_LIGHT_GREEN=$'\x1b[92m' + ANSI_LIGHT_BLUE=$'\x1b[94m' + ANSI_NOCOLOR=$'\x1b[0m' + + bandit_directory=report/bandit + bandit_fullpath=report/bandit/report.xml + + tee "${GITHUB_OUTPUT}" <> "${GITHUB_OUTPUT}" + else + faults=$(grep -Poh '(?<=)' ${bandit_fullpath}) + + printf "Bandit result: ${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n" + printf " ${ANSI_LIGHT_RED}Bandit found %s issues.${ANSI_NOCOLOR}\n" "${faults}" + printf "::error title=%s::%s\n" "🚨 Security Scanning (Bandit)" "Bandi found ${faults} issues." + + printf "bandit_passed=false\n" >> "${GITHUB_OUTPUT}" + + printf "::group::${ANSI_LIGHT_BLUE}JUnit XML report created by Bandit ...${ANSI_NOCOLOR}\n" + cat ${bandit_fullpath} + printf "\n::endgroup::\n" + fi + + - name: 📊 Publish Bandit Results + uses: dorny/test-reporter@v2 + if: steps.bandit.outputs.bandit_passed == 'false' + continue-on-error: true + with: + name: 'Bandit Results' + path: ${{ steps.bandit.outputs.bandit_fullpath }} + reporter: java-junit + + Radon: + name: ☢️ Metrics and Complexity + runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" + if: inputs.radon == 'true' + + steps: + - name: ⏬ Checkout repository + uses: actions/checkout@v5 + with: + lfs: true + submodules: true + + - name: 🐍 Setup Python ${{ inputs.python_version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python_version }} + + - name: ⚙ Install dependencies for running radon + run: python -m pip install --disable-pip-version-check radon + + - name: Code Metrics +# if: inputs.artifact != '' + run: | + radon raw ${{ inputs.package_directory }} -s + + - name: Code Complexity +# if: inputs.artifact != '' + run: | + radon cc ${{ inputs.package_directory }} --total-average + + - name: Halstead Complexity Metrics +# if: inputs.artifact != '' + run: | + radon hal ${{ inputs.package_directory }} + + - name: Maintainability Index +# if: inputs.artifact != '' + run: | + radon mi ${{ inputs.package_directory }} -s + + PyLint: + name: 🩺 Linting + runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" + if: inputs.pylint == 'true' + + steps: + - name: ⏬ Checkout repository + uses: actions/checkout@v5 + with: + lfs: true + submodules: true + + - name: 🐍 Setup Python ${{ inputs.python_version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ inputs.python_version }} + + - name: ⚙ Install dependencies for running PyLint + run: | + python -m pip install --disable-pip-version-check pylint + python -m pip install --disable-pip-version-check ${{ inputs.requirements }} + + - name: 🩺 PyLint +# if: inputs.artifact != '' + run: | + pylint ${{ inputs.package_directory }} diff --git a/.github/workflows/CheckDocumentation.yml b/.github/workflows/CheckDocumentation.yml index b33c6b8..d519687 100644 --- a/.github/workflows/CheckDocumentation.yml +++ b/.github/workflows/CheckDocumentation.yml @@ -41,7 +41,7 @@ on: fail_under: description: 'Minimum required documentation coverage level' required: false - default: 80 + default: '80' type: string jobs: @@ -53,11 +53,11 @@ jobs: uses: actions/checkout@v5 - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} - - name: 🔧 Install wheel,tomli and pip dependencies (native) + - name: 🔧 Install docstr_coverage and interrogate dependencies run: | python -m pip install --disable-pip-version-check -U docstr_coverage interrogate[png] @@ -80,4 +80,3 @@ jobs: if [[ $? -ne 0 ]]; then printf "%s\n" "::error title=docstr-coverage::Insufficient documentation quality (goal: ${{ inputs.fail_under }})" fi - diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index d4cdec3..059ef46 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -46,7 +46,7 @@ on: unittest_system_list: description: 'Space separated list of systems to run tests on.' required: false - default: 'ubuntu windows macos macos-arm mingw64 ucrt64' + default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm mingw64 ucrt64' type: string unittest_include_list: description: 'Space separated list of system:python items to be included into the list of test.' @@ -56,12 +56,12 @@ on: unittest_exclude_list: description: 'Space separated list of system:python items to be excluded from the list of test.' required: false - default: '' + default: 'windows-arm:3.9 windows-arm:3.10' type: string unittest_disable_list: description: 'Space separated list of system:python items to be disabled from the list of test.' required: false - default: '' + default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11' type: string apptest_python_version: description: 'Python version.' @@ -71,12 +71,12 @@ on: apptest_python_version_list: description: 'Space separated list of Python versions to run tests with.' required: false - default: "" + default: '' type: string apptest_system_list: description: 'Space separated list of systems to run tests on.' required: false - default: 'ubuntu windows macos macos-arm ucrt64' + default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm ucrt64' type: string apptest_include_list: description: 'Space separated list of system:python items to be included into the list of test.' @@ -86,18 +86,18 @@ on: apptest_exclude_list: description: 'Space separated list of system:python items to be excluded from the list of test.' required: false - default: '' + default: 'windows-arm:3.9 windows-arm:3.10' type: string apptest_disable_list: description: 'Space separated list of system:python items to be disabled from the list of test.' required: false - default: '' + default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11' type: string codecov: description: 'Publish merged coverage and unittest reports to Codecov.' required: false default: 'false' - type: string + type: string codacy: description: 'Publish merged coverage report to Codacy.' required: false @@ -130,9 +130,6 @@ jobs: ConfigParams: uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@main - with: - package_namespace: ${{ inputs.package_namespace }} - package_name: ${{ inputs.package_name }} UnitTestingParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main @@ -177,13 +174,15 @@ jobs: - UnitTestingParams with: jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} +# TODO: shouldn't this be configured by a parameter? Same as directories requirements: "-r tests/unit/requirements.txt" # pacboy: "msys/git python-lxml:p" - unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} - unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} - coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }} - unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} - coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} + unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} + coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }} + unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} + coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} StaticTypeCheck: uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@main @@ -191,21 +190,30 @@ jobs: - ConfigParams - UnitTestingParams with: - python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - commands: | - ${{ needs.ConfigParams.outputs.mypy_prepare_command }} - mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }} - html_report: 'report/typing' - html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + cobertura_report: ${{ needs.ConfigParams.outputs.typing_report_cobertura }} + junit_report: ${{ needs.ConfigParams.outputs.typing_report_junit }} + html_report: ${{ needs.ConfigParams.outputs.typing_report_html }} + cobertura_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_cobertura }} + junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_junit }} + html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} + + CodeQuality: + uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@main + needs: + - UnitTestingParams + with: + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + package_directory: ${{ needs.UnitTestingParams.outputs.package_directory }} + artifact: CodeQuality DocCoverage: uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@main needs: - - ConfigParams - UnitTestingParams with: python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - directory: ${{ needs.ConfigParams.outputs.package_directory }} + directory: ${{ needs.UnitTestingParams.outputs.package_directory }} Package: uses: pyTooling/Actions/.github/workflows/Package.yml@main @@ -219,14 +227,13 @@ jobs: Install: uses: pyTooling/Actions/.github/workflows/InstallPackage.yml@main needs: - - ConfigParams - UnitTestingParams - InstallParams - Package with: jobs: ${{ needs.InstallParams.outputs.python_jobs }} wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} - package_name: ${{ needs.ConfigParams.outputs.package_fullname }} + package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }} # AppTesting: # uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@main @@ -251,13 +258,12 @@ jobs: # coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} # coverage_report_xml_directory: ${{ needs.ConfigParams.outputs.coverage_report_xml_directory }} # coverage_report_xml_filename: ${{ needs.ConfigParams.outputs.coverage_report_xml_filename }} - coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} - coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} - coverage_report_json_filename: ${{ needs.ConfigParams.outputs.coverage_report_json_filename }} - coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} - coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }} - codecov: ${{ inputs.codecov }} - codacy: ${{ inputs.codacy }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }} + coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} + coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} + codecov: ${{ inputs.codecov }} + codacy: ${{ inputs.codacy }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }} @@ -270,8 +276,8 @@ jobs: - UnitTesting if: success() || failure() with: - testsuite-summary-name: ${{ needs.ConfigParams.outputs.package_fullname }} - merged_junit_filename: ${{ needs.ConfigParams.outputs.unittest_merged_report_xml_filename }} + testsuite-summary-name: ${{ needs.UnitTestingParams.outputs.package_fullname }} + merged_junit_filename: ${{ fromJson(needs.ConfigParams.outputs.unittest_merged_report_xml).filename }} merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} dorny: ${{ inputs.dorny }} codecov: ${{ inputs.codecov }} @@ -295,12 +301,12 @@ jobs: # - VerifyDocs if: success() || failure() with: - python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} - unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} - coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} - html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} - latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} + coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} + html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} + latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} IntermediateCleanUp: uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@main @@ -313,15 +319,16 @@ jobs: sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}- xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}- -# PDFDocumentation: -# uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@main -# needs: -# - UnitTestingParams -# - Documentation -# with: -# document: pyEDAA.ProjectModel -# latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} -# pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }} + PDFDocumentation: + uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@main + needs: + - UnitTestingParams + - Documentation + with: + document: ${{ needs.UnitTestingParams.outputs.package_fullname }} + latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} + pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }} + can-fail: 'true' PublishToGitHubPages: uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main @@ -346,7 +353,7 @@ jobs: # - StaticTypeCheck - Package - PublishToGitHubPages - if: needs.Prepare.outputs.is_release_commit && github.event_name != 'schedule' + if: needs.Prepare.outputs.is_release_commit == 'true' && github.event_name != 'schedule' permissions: contents: write # required for create tag actions: write # required for trigger workflow @@ -383,10 +390,10 @@ jobs: if: needs.Prepare.outputs.is_release_tag == 'true' with: python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - requirements: -r dist/requirements.txt - artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} + requirements: '-r dist/requirements.txt' + artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} secrets: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} ArtifactCleanUp: uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 326d05a..7ae2299 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -71,8 +71,8 @@ jobs: runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" steps: - - name: '❗ Deprecation message' - run: printf "::warning title=%s::%s\n" "Deprecated" "'CoverageCollection.yml' is not maintained anymore. Please switch to 'UnitTesting.yml', 'PublishCoverageResults.yml' and 'PublishTestResults.yml'." + - name: ⚠️ Deprecation Warning + run: printf "::warning title=%s::%s\n" "Deprecated" "'CoverageCollection.yml' template is deprecated. Please switch to 'PublishReleaseNotes.yml'. See https://pytooling.github.io/Actions/JobTemplate/Testing/UnitTesting.html" - name: ⏬ Checkout repository uses: actions/checkout@v5 @@ -81,7 +81,7 @@ jobs: submodules: true - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} diff --git a/.github/workflows/ExtractConfiguration.yml b/.github/workflows/ExtractConfiguration.yml index 0067152..e32bc17 100644 --- a/.github/workflows/ExtractConfiguration.yml +++ b/.github/workflows/ExtractConfiguration.yml @@ -34,15 +34,6 @@ on: required: false default: '3.13' type: string - package_namespace: - description: 'Name of the tool''s namespace.' - required: false - default: '' - type: string - package_name: - description: 'Name of the tool''s package.' - required: true - type: string coverage_config: description: 'Path to the .coveragerc file. Use pyproject.toml by default.' required: false @@ -50,83 +41,51 @@ on: type: string outputs: - package_fullname: - description: "" - value: ${{ jobs.Extract.outputs.package_fullname }} - package_directory: - description: "" - value: ${{ jobs.Extract.outputs.package_directory }} - mypy_prepare_command: - description: "" - value: ${{ jobs.Extract.outputs.mypy_prepare_command }} - unittest_report_xml_directory: - description: "" - value: ${{ jobs.Extract.outputs.unittest_report_xml_directory }} - unittest_report_xml_filename: - description: "" - value: ${{ jobs.Extract.outputs.unittest_report_xml_filename }} unittest_report_xml: description: "" value: ${{ jobs.Extract.outputs.unittest_report_xml }} - unittest_merged_report_xml_directory: - description: "" - value: ${{ jobs.Extract.outputs.unittest_merged_report_xml_directory }} - unittest_merged_report_xml_filename: - description: "" - value: ${{ jobs.Extract.outputs.unittest_merged_report_xml_filename }} unittest_merged_report_xml: description: "" value: ${{ jobs.Extract.outputs.unittest_merged_report_xml }} - coverage_report_html_directory: + coverage_report_html: description: "" - value: ${{ jobs.Extract.outputs.coverage_report_html_directory }} - coverage_report_xml_directory: - description: "" - value: ${{ jobs.Extract.outputs.coverage_report_xml_directory }} - coverage_report_xml_filename: - description: "" - value: ${{ jobs.Extract.outputs.coverage_report_xml_filename }} + value: ${{ jobs.Extract.outputs.coverage_report_html }} coverage_report_xml: description: "" value: ${{ jobs.Extract.outputs.coverage_report_xml }} - coverage_report_json_directory: - description: "" - value: ${{ jobs.Extract.outputs.coverage_report_json_directory }} - coverage_report_json_filename: - description: "" - value: ${{ jobs.Extract.outputs.coverage_report_json_filename }} coverage_report_json: description: "" value: ${{ jobs.Extract.outputs.coverage_report_json }} + typing_report_cobertura: + description: "" + value: ${{ jobs.Extract.outputs.typing_report_cobertura }} + typing_report_junit: + description: "" + value: ${{ jobs.Extract.outputs.typing_report_junit }} + typing_report_html: + description: "" + value: ${{ jobs.Extract.outputs.typing_report_html }} jobs: Extract: name: 📓 Extract configurations from pyproject.toml runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" outputs: - package_fullname: ${{ steps.getPackageName.outputs.package_fullname }} - package_directory: ${{ steps.getPackageName.outputs.package_directory }} - mypy_prepare_command: ${{ steps.getPackageName.outputs.mypy_prepare_command }} - unittest_report_xml_directory: ${{ steps.getVariables.outputs.unittest_report_xml_directory }} - unittest_report_xml_filename: ${{ steps.getVariables.outputs.unittest_report_xml_filename }} - unittest_report_xml: ${{ steps.getVariables.outputs.unittest_report_xml }} - unittest_merged_report_xml_directory: ${{ steps.getVariables.outputs.unittest_merged_report_xml_directory }} - unittest_merged_report_xml_filename: ${{ steps.getVariables.outputs.unittest_merged_report_xml_filename }} - unittest_merged_report_xml: ${{ steps.getVariables.outputs.unittest_merged_report_xml }} - coverage_report_html_directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }} - coverage_report_xml_directory: ${{ steps.getVariables.outputs.coverage_report_xml_directory }} - coverage_report_xml_filename: ${{ steps.getVariables.outputs.coverage_report_xml_filename }} - coverage_report_xml: ${{ steps.getVariables.outputs.coverage_report_xml }} - coverage_report_json_directory: ${{ steps.getVariables.outputs.coverage_report_json_directory }} - coverage_report_json_filename: ${{ steps.getVariables.outputs.coverage_report_json_filename }} - coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }} + unittest_report_xml: ${{ steps.getVariables.outputs.unittest_report_xml }} + unittest_merged_report_xml: ${{ steps.getVariables.outputs.unittest_merged_report_xml }} + coverage_report_html: ${{ steps.getVariables.outputs.coverage_report_html }} + coverage_report_xml: ${{ steps.getVariables.outputs.coverage_report_xml }} + coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }} + typing_report_cobertura: ${{ steps.getVariables.outputs.typing_report_cobertura }} + typing_report_junit: ${{ steps.getVariables.outputs.typing_report_junit }} + typing_report_html: ${{ steps.getVariables.outputs.typing_report_html }} steps: - name: ⏬ Checkout repository uses: actions/checkout@v5 - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} @@ -134,58 +93,17 @@ jobs: run: | python -m pip install --disable-pip-version-check -U wheel tomli - - name: 🔁 Full package name and directory - id: getPackageName - shell: python - run: | - from os import getenv - from pathlib import Path - from textwrap import dedent - - namespace = "${{ inputs.package_namespace }}".strip() - name = "${{ inputs.package_name }}".strip() - - print(dedent(f"""\ - INPUTS: - package_namespace: {namespace} - package_name: {name} - """)) - - if namespace == "" or namespace == ".": - fullname = f"{name}" - directory = f"{name}" - mypy_prepare_command = "" - else: - fullname = f"{namespace}.{name}" - directory = f"{namespace}/{name}" - mypy_prepare_command = f"touch {namespace}/__init__.py" - - print(dedent(f"""\ - OUTPUTS: - package_fullname: {fullname} - package_directory: {directory} - mypy_prepare_command: {mypy_prepare_command} - """)) - - github_output = Path(getenv("GITHUB_OUTPUT")) - print(f"GITHUB_OUTPUT: {github_output}") - with github_output.open("a+", encoding="utf-8") as f: - f.write(dedent(f"""\ - package_fullname={fullname} - package_directory={directory} - mypy_prepare_command={mypy_prepare_command} - """)) - - name: 🔁 Extract configurations from pyproject.toml id: getVariables shell: python run: | + from json import dumps as json_dumps from os import getenv from pathlib import Path from sys import version from textwrap import dedent - print(f"Python: {version}") + print(f"Python: {version} (of default installation)") from tomli import load as tomli_load @@ -194,6 +112,9 @@ jobs: coverageXMLFile = Path("./coverage.xml") coverageJSONFile = Path("./coverage.json") coverageRC = "${{ inputs.coverage_config }}".strip() + typingCoberturaFile = Path("report/typing/cobertura.xml") + typingJUnitFile = Path("report/typing/StaticTypingSummary.xml") + typingHTMLDirectory = Path("htmlmypy") # Read output paths from 'pyproject.toml' file if coverageRC == "pyproject.toml": @@ -207,6 +128,9 @@ jobs: coverageHTMLDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"]) coverageXMLFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"]) coverageJSONFile= Path(pyProjectSettings["tool"]["coverage"]["json"]["output"]) + typingCoberturaFile = Path(pyProjectSettings["tool"]["mypy"]["cobertura_xml_report"]) / "cobertura.xml" + typingJUnitFile = Path(pyProjectSettings["tool"]["mypy"]["junit_xml"]) + typingHTMLDirectory = Path(pyProjectSettings["tool"]["mypy"]["html_report"]) else: print(f"File '{pyProjectFile}' not found.") print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.") @@ -214,6 +138,8 @@ jobs: # Read output paths from '.coveragerc' file elif len(coverageRC) > 0: + print(f"::warning title=Deprecated::Using '{coverageRCFile}' is deprecated. Please use 'pyproject.toml'.") + coverageRCFile = Path(coverageRC) if coverageRCFile.exists(): with coverageRCFile.open("rb") as file: @@ -227,24 +153,58 @@ jobs: print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.") exit(1) + unittest_report_xml = { + "fullpath": unittestXMLFile.as_posix(), + "directory": unittestXMLFile.parent.as_posix(), + "filename": unittestXMLFile.name + } + unittest_merged_report_xml = { + "fullpath": mergedUnittestXMLFile.as_posix(), + "directory": mergedUnittestXMLFile.parent.as_posix(), + "filename": mergedUnittestXMLFile.name + } + coverage_report_html = { + "fullpath": coverageHTMLDirectory.as_posix(), + "directory": coverageHTMLDirectory.as_posix() + } + coverage_report_xml = { + "fullpath": coverageXMLFile.as_posix(), + "directory": coverageXMLFile.parent.as_posix(), + "filename": coverageXMLFile.name + } + coverage_report_json = { + "fullpath": coverageJSONFile.as_posix(), + "directory": coverageJSONFile.parent.as_posix(), + "filename": coverageJSONFile.name + } + typing_report_cobertura = { + "fullpath": typingCoberturaFile.as_posix(), + "directory": typingCoberturaFile.parent.as_posix(), + "filename": typingCoberturaFile.name + } + typing_report_junit = { + "fullpath": typingJUnitFile.as_posix(), + "directory": typingJUnitFile.parent.as_posix(), + "filename": typingJUnitFile.name + } + typing_report_html = { + "fullpath": typingHTMLDirectory.as_posix(), + "directory": typingHTMLDirectory.as_posix() + } + # Write jobs to special file github_output = Path(getenv("GITHUB_OUTPUT")) print(f"GITHUB_OUTPUT: {github_output}") with github_output.open("a+", encoding="utf-8") as f: f.write(dedent(f"""\ - unittest_report_xml_directory={unittestXMLFile.parent.as_posix()} - unittest_report_xml_filename={unittestXMLFile.name} - unittest_report_xml={unittestXMLFile.as_posix()} - unittest_merged_report_xml_directory={mergedUnittestXMLFile.parent.as_posix()} - unittest_merged_report_xml_filename={mergedUnittestXMLFile.name} - unittest_merged_report_xml={mergedUnittestXMLFile.as_posix()} - coverage_report_html_directory={coverageHTMLDirectory.as_posix()} - coverage_report_xml_directory={coverageXMLFile.parent.as_posix()} - coverage_report_xml_filename={coverageXMLFile.name} - coverage_report_xml={coverageXMLFile.as_posix()} - coverage_report_json_directory={coverageJSONFile.parent.as_posix()} - coverage_report_json_filename={coverageJSONFile.name} - coverage_report_json={coverageJSONFile.as_posix()} + unittest_report_xml={json_dumps(unittest_report_xml)} + unittest_merged_report_xml={json_dumps(unittest_merged_report_xml)} + coverage_report_html={json_dumps(coverage_report_html)} + coverage_report_xml={json_dumps(coverage_report_xml)} + coverage_report_json={json_dumps(coverage_report_json)} + typing_report_cobertura={json_dumps(typing_report_cobertura)} + typing_report_junit={json_dumps(typing_report_junit)} + typing_report_html={json_dumps(typing_report_html)} """)) print(dedent(f"""\ @@ -254,4 +214,47 @@ jobs: coverage html: {coverageHTMLDirectory} coverage xml: {coverageXMLFile} coverage json: {coverageJSONFile} + typing cobertura: {typingCoberturaFile} + typing junit: {typingJUnitFile} + typing html: {typingHTMLDirectory} """)) + + - name: Debug JSON objects + run: | + printf "unittest_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.unittest_report_xml }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).filename }}" + + printf "unittest_merged_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.unittest_merged_report_xml }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).filename }}" + + printf "coverage_report_html: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_html }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_html).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_html).directory }}" + + printf "coverage_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_xml }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).filename }}" + + printf "coverage_report_json: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_json }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).filename }}" + + printf "typing_report_cobertura: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_cobertura }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).filename }}" + + printf "typing_report_junit: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_junit }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).directory }}" + printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).filename }}" + + printf "typing_report_html: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_html }}" + printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_html).fullpath }}" + printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_html).directory }}" diff --git a/.github/workflows/InstallPackage.yml b/.github/workflows/InstallPackage.yml index 1cfec35..8f60808 100644 --- a/.github/workflows/InstallPackage.yml +++ b/.github/workflows/InstallPackage.yml @@ -73,7 +73,7 @@ jobs: python-tomli:p - name: 🐍 Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 if: matrix.system != 'msys2' with: python-version: ${{ matrix.python }} @@ -84,17 +84,17 @@ jobs: python -m pip install --disable-pip-version-check -U wheel - name: 🔧 Install wheel from artifact (Ubuntu/macOS) - if: matrix.system != 'windows' + if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' ) run: | python -m pip install --disable-pip-version-check -U install/*.whl - name: 🔧 Install wheel from artifact (Windows) - if: matrix.system == 'windows' + if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' ) run: | python -m pip install -v --disable-pip-version-check (Get-Item .\install\*.whl).FullName - - name: 📦 Run application tests (Ubuntu/macOS) - if: matrix.system != 'windows' + - name: 📦 Run Package Version Check (Ubuntu/macOS) + if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' ) run: | set +e @@ -116,8 +116,8 @@ jobs: exit 1 fi - - name: 📦 Run application tests (Windows) - if: matrix.system == 'windows' + - name: 📦 Run Package Version Check (Windows) + if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' ) run: | $result=$(python -c "from ${{ inputs.package_name }} import __version__; print(f""Package version: {__version__}"")") Write-Host $result diff --git a/.github/workflows/IntermediateCleanUp.yml b/.github/workflows/IntermediateCleanUp.yml index b3ca6e7..057f756 100644 --- a/.github/workflows/IntermediateCleanUp.yml +++ b/.github/workflows/IntermediateCleanUp.yml @@ -30,12 +30,14 @@ on: default: '24.04' type: string sqlite_coverage_artifacts_prefix: - description: 'Prefix for SQLite coverage artifacts' + description: 'Prefix for SQLite coverage artifacts to be removed.' required: false + default: '' type: string xml_unittest_artifacts_prefix: - description: 'Prefix for XML unittest artifacts' + description: 'Prefix for XML unittest artifacts to be removed.' required: false + default: '' type: string jobs: diff --git a/.github/workflows/LaTeXDocumentation.yml b/.github/workflows/LaTeXDocumentation.yml index 89d844f..e524fcc 100644 --- a/.github/workflows/LaTeXDocumentation.yml +++ b/.github/workflows/LaTeXDocumentation.yml @@ -29,25 +29,35 @@ on: required: false default: '24.04' type: string + latex_artifact: + description: 'Name of the LaTeX documentation artifact.' + required: true + type: string document: description: 'LaTeX root document without *.tex extension.' required: true type: string - latex_artifact: - description: 'Name of the LaTeX documentation artifact.' + processor: + description: 'Name of the used LaTeX processor.' required: false - default: '' + default: 'xelatex' type: string pdf_artifact: description: 'Name of the PDF documentation artifact.' required: false default: '' type: string + can-fail: + description: 'Translation from LaTeX to PDF may fail.' + required: false + default: 'false' + type: string jobs: PDFDocumentation: name: 📓 Converting LaTeX Documentation to PDF runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" + continue-on-error: ${{ inputs.can-fail == 'true' }} steps: - name: 📥 Download artifacts '${{ inputs.latex_artifact }}' from 'SphinxDocumentation' job uses: pyTooling/download-artifact@v5 @@ -55,21 +65,22 @@ jobs: name: ${{ inputs.latex_artifact }} path: latex - - name: Debug - run: | - tree -pash . +# - name: Debug +# run: | +# tree -pash . - name: Build LaTeX document using 'pytooling/miktex:sphinx' uses: addnab/docker-run-action@v3 + if: inputs.pdf_artifact != '' with: image: pytooling/miktex:sphinx options: -v ${{ github.workspace }}/latex:/latex --workdir /latex run: | - which pdflatex - pwd - ls -lAh + # which ${{ inputs.processor }} + # pwd + # ls -lAh - latexmk -xelatex ${{ inputs.document }}.tex + latexmk -${{ inputs.processor }} "${{ inputs.document }}.tex" - name: 📤 Upload 'PDF Documentation' artifact uses: pyTooling/upload-artifact@v4 diff --git a/.github/workflows/NightlyRelease.yml b/.github/workflows/NightlyRelease.yml index d6d4623..88d52dd 100644 --- a/.github/workflows/NightlyRelease.yml +++ b/.github/workflows/NightlyRelease.yml @@ -100,6 +100,9 @@ jobs: # attestations: write steps: + - name: ⚠️ Deprecation Warning + run: printf "::warning title=%s::%s\n" "NightlyRelease" "'NightlyRelease.yml' template is deprecated. Please switch to 'PublishReleaseNotes.yml'. See https://pytooling.github.io/Actions/JobTemplate/Release/PublishReleaseNotes.html" + - name: ⏬ Checkout repository uses: actions/checkout@v5 with: diff --git a/.github/workflows/Package.yml b/.github/workflows/Package.yml index e772f0f..365c4ee 100644 --- a/.github/workflows/Package.yml +++ b/.github/workflows/Package.yml @@ -46,11 +46,11 @@ on: type: string jobs: - Package: name: 📦 Package in Source and Wheel Format runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" - + env: + artifact: ${{ inputs.artifact }} steps: - name: ⏬ Checkout repository uses: actions/checkout@v5 @@ -59,7 +59,7 @@ jobs: submodules: true - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} @@ -79,15 +79,15 @@ jobs: # build (not isolated) - - name: 🔧 [build] Install dependencies for packaging and release + - name: 🔧 [build - no-isolation] Install dependencies for packaging and release if: inputs.requirements == 'no-isolation' run: python -m pip install --disable-pip-version-check build - - name: 🔨 [build] Build Python package (source distribution) + - name: 🔨 [build - no-isolation] Build Python package (source distribution) if: inputs.requirements == 'no-isolation' run: python -m build --no-isolation --sdist - - name: 🔨 [build] Build Python package (binary distribution - wheel) + - name: 🔨 [build - no-isolation] Build Python package (binary distribution - wheel) if: inputs.requirements == 'no-isolation' run: python -m build --no-isolation --wheel diff --git a/.github/workflows/Parameters.yml b/.github/workflows/Parameters.yml index d3d2c0b..4489c74 100644 --- a/.github/workflows/Parameters.yml +++ b/.github/workflows/Parameters.yml @@ -58,7 +58,7 @@ on: system_list: description: 'Space separated list of systems to run tests on.' required: false - default: 'ubuntu windows macos macos-arm mingw64 ucrt64' + default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm mingw64 ucrt64' type: string include_list: description: 'Space separated list of system:python items to be included into the list of test.' @@ -68,22 +68,32 @@ on: exclude_list: description: 'Space separated list of system:python items to be excluded from the list of test.' required: false - default: '' + default: 'windows-arm:3.9 windows-arm:3.10' type: string disable_list: description: 'Space separated list of system:python items to be disabled from the list of test.' required: false - default: '' + default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11' type: string ubuntu_image: - description: 'The used GitHub Action image for Ubuntu based jobs.' + description: 'The used GitHub Action image for Ubuntu (x86-64) based jobs.' required: false default: 'ubuntu-24.04' type: string - windows_image: - description: 'The used GitHub Action image for Windows based jobs.' + ubuntu_arm_image: + description: 'The used GitHub Action image for Ubuntu (aarch64) based jobs.' required: false - default: 'windows-2022' + default: 'ubuntu-24.04-arm' + type: string + windows_image: + description: 'The used GitHub Action image for Windows Server (x86-64) based jobs.' + required: false + default: 'windows-2025' + type: string + windows_arm_image: + description: 'The used GitHub Action image for Windows (aarch64) based jobs.' + required: false + default: 'windows-11-arm' type: string macos_intel_image: description: 'The used GitHub Action image for macOS (Intel x86-64) based jobs.' @@ -93,7 +103,7 @@ on: macos_arm_image: description: 'The used GitHub Action image for macOS (ARM aarch64) based jobs.' required: false - default: 'macos-14' + default: 'macos-15' type: string pipeline-delay: description: 'Slow down this job, to delay the startup of the GitHub Action pipline.' @@ -105,25 +115,33 @@ on: python_version: description: "Default Python version for other jobs." value: ${{ jobs.Parameters.outputs.python_version }} - python_jobs: - description: "List of Python versions (and system combinations) to be used in the matrix of other jobs." - value: ${{ jobs.Parameters.outputs.python_jobs }} + package_fullname: + description: "The package's full name." + value: ${{ jobs.Parameters.outputs.package_fullname }} + package_directory: + description: "The package's directory." + value: ${{ jobs.Parameters.outputs.package_directory }} + artifact_basename: + description: "Artifact base name." + value: ${{ jobs.Parameters.outputs.artifact_basename }} artifact_names: description: "Pre-defined artifact names for other jobs." value: ${{ jobs.Parameters.outputs.artifact_names }} - params: - description: "Parameters to be used in other jobs." - value: ${{ jobs.Parameters.outputs.params }} + python_jobs: + description: "List of Python versions (and system combinations) to be used in the matrix of other jobs." + value: ${{ jobs.Parameters.outputs.python_jobs }} jobs: Parameters: name: ✎ Generate pipeline parameters runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" outputs: - python_version: ${{ steps.params.outputs.python_version }} - python_jobs: ${{ steps.params.outputs.python_jobs }} - artifact_names: ${{ steps.params.outputs.artifact_names }} - params: ${{ steps.params.outputs.params }} + python_version: ${{ steps.variables.outputs.python_version }} + package_fullname: ${{ steps.variables.outputs.package_fullname }} + package_directory: ${{ steps.variables.outputs.package_directory }} + artifact_basename: ${{ steps.variables.outputs.artifact_basename }} + artifact_names: ${{ steps.artifacts.outputs.artifact_names }} + python_jobs: ${{ steps.jobs.outputs.python_jobs }} steps: - name: Generate a startup delay of ${{ inputs.pipeline-delay }} seconds @@ -132,8 +150,98 @@ jobs: run: | sleep ${{ inputs.pipeline-delay }} - - name: Generate 'params' and 'python_jobs' - id: params + - name: Generate 'python_version' + id: variables + shell: python + run: | + from os import getenv + from pathlib import Path + from sys import exit + from textwrap import dedent + + python_version = "${{ inputs.python_version }}".strip() + package_namespace = "${{ inputs.package_namespace }}".strip() + package_name = "${{ inputs.package_name }}".strip() + name = "${{ inputs.name }}".strip() + + if package_namespace == "": # or package_namespace == ".": + package_fullname = package_name + package_directory = package_name + elif package_namespace[-2:] == ".*": + package_fullname = package_namespace[:-2] + package_directory = package_namespace[:-2] + else: + package_fullname = f"{package_namespace}.{package_name}" + package_directory = f"{package_namespace}/{package_name}" + + artifact_basename = package_fullname if name == "" else name + if artifact_basename == "" or artifact_basename == ".": + print("::error title=Parameter::artifact_basename is empty.") + exit(1) + + print("Variables:") + print(f" python_version: {python_version}") + print(f" package_fullname: {package_fullname}") + print(f" package_directory: {package_directory}") + print(f" artifact_basename: {artifact_basename}") + + # Write jobs to special file + github_output = Path(getenv("GITHUB_OUTPUT")) + print(f"GITHUB_OUTPUT: {github_output}") + with github_output.open("a+", encoding="utf-8") as f: + f.write(dedent(f"""\ + python_version={python_version} + package_fullname={package_fullname} + package_directory={package_directory} + artifact_basename={artifact_basename} + """)) + + - name: Generate 'artifact_names' + id: artifacts + shell: python + run: | + from json import dumps as json_dumps + from os import getenv + from pathlib import Path + from textwrap import dedent + + package_namespace = "${{ inputs.package_namespace }}".strip() + package_name = "${{ inputs.package_name }}".strip() + artifact_basename = "${{ steps.variables.outputs.artifact_basename }}" + + artifact_names = { + "unittesting_xml": f"{artifact_basename}-UnitTestReportSummary-XML", + "unittesting_html": f"{artifact_basename}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{artifact_basename}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{artifact_basename}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{artifact_basename}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{artifact_basename}-CodeCoverage-SQLite", + "codecoverage_xml": f"{artifact_basename}-CodeCoverage-XML", + "codecoverage_json": f"{artifact_basename}-CodeCoverage-JSON", + "codecoverage_html": f"{artifact_basename}-CodeCoverage-HTML", + "statictyping_cobertura": f"{artifact_basename}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{artifact_basename}-StaticTyping-JUnit-XML", + "statictyping_html": f"{artifact_basename}-StaticTyping-HTML", + "package_all": f"{artifact_basename}-Packages", + "documentation_html": f"{artifact_basename}-Documentation-HTML", + "documentation_latex": f"{artifact_basename}-Documentation-LaTeX", + "documentation_pdf": f"{artifact_basename}-Documentation-PDF", + } + + print("Artifacts Names ({len(artifact_names)}):") + for id, artifactName in artifact_names.items(): + print(f" {id:>24}: {artifactName}") + + # Write jobs to special file + github_output = Path(getenv("GITHUB_OUTPUT")) + print(f"GITHUB_OUTPUT: {github_output}") + with github_output.open("a+", encoding="utf-8") as f: + f.write(dedent(f"""\ + artifact_names={json_dumps(artifact_names)} + """)) + + - name: Generate 'python_jobs' + id: jobs shell: python run: | from json import dumps as json_dumps @@ -142,25 +250,17 @@ jobs: from textwrap import dedent from typing import Iterable - package_namespace = "${{ inputs.package_namespace }}".strip() - package_name = "${{ inputs.package_name }}".strip() - name = "${{ inputs.name }}".strip() - python_version = "${{ inputs.python_version }}".strip() + python_version = "${{ steps.variables.outputs.python_version }}" + name = "${{ steps.variables.outputs.artifact_basename }}" systems = "${{ inputs.system_list }}".strip() versions = "${{ inputs.python_version_list }}".strip() include_list = "${{ inputs.include_list }}".strip() exclude_list = "${{ inputs.exclude_list }}".strip() disable_list = "${{ inputs.disable_list }}".strip() - if name == "": - if package_namespace == "" or package_namespace == ".": - name = f"{package_name}" - else: - name = f"{package_namespace}.{package_name}" - currentMSYS2Version = "3.12" currentAlphaVersion = "3.14" - currentAlphaRelease = "3.14.0-alpha.1" + currentAlphaRelease = "3.14.0-rc.2" if systems == "": print("::error title=Parameter::system_list is empty.") @@ -194,7 +294,7 @@ jobs: if currentAlphaVersion in versions: print(f"::notice title=Experimental::Python {currentAlphaVersion} ({currentAlphaRelease}) is a pre-release.") for disable in disabled: - print(f"::warning title=Disabled Python Job::System '{disable}' temporarily disabled.") + print(f"::warning title=Disabled Python Job::{name}: Job combination '{disable}' temporarily disabled.") # see https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json data = { @@ -215,10 +315,12 @@ jobs: }, # Runner systems (runner images) supported by GitHub Actions "sys": { - "ubuntu": { "icon": "🐧", "runs-on": "${{ inputs.ubuntu_image }}", "shell": "bash", "name": "Linux (x86-64)" }, - "windows": { "icon": "🪟", "runs-on": "${{ inputs.windows_image }}", "shell": "pwsh", "name": "Windows (x86-64)" }, - "macos": { "icon": "🍎", "runs-on": "${{ inputs.macos_intel_image }}", "shell": "bash", "name": "macOS (x86-64)" }, - "macos-arm": { "icon": "🍏", "runs-on": "${{ inputs.macos_arm_image }}", "shell": "bash", "name": "macOS (aarch64)" }, + "ubuntu": { "icon": "🐧", "runs-on": "${{ inputs.ubuntu_image }}", "shell": "bash", "name": "Linux (x86-64)" }, + "ubuntu-arm": { "icon": "⛄", "runs-on": "${{ inputs.ubuntu_arm_image }}", "shell": "bash", "name": "Linux (aarch64)" }, + "windows": { "icon": "🪟", "runs-on": "${{ inputs.windows_image }}", "shell": "pwsh", "name": "Windows (x86-64)" }, + "windows-arm": { "icon": "🏢", "runs-on": "${{ inputs.windows_arm_image }}", "shell": "pwsh", "name": "Windows (aarch64)" }, + "macos": { "icon": "🍎", "runs-on": "${{ inputs.macos_intel_image }}", "shell": "bash", "name": "macOS (x86-64)" }, + "macos-arm": { "icon": "🍏", "runs-on": "${{ inputs.macos_arm_image }}", "shell": "bash", "name": "macOS (aarch64)" }, }, # Runtimes provided by MSYS2 "runtime": { @@ -300,7 +402,7 @@ jobs: { "sysicon": data["runtime"][runtime]["icon"], "system": "msys2", - "runs-on": "windows-latest", + "runs-on": "${{ inputs.windows_image }}", "runtime": runtime.upper(), "shell": "msys2 {0}", "pyicon": data["python"][currentMSYS2Version]["icon"], @@ -310,31 +412,11 @@ jobs: for runtime, version in combinations if runtime not in data["sys"] ] - artifact_names = { - "unittesting_xml": f"{name}-UnitTestReportSummary-XML", - "unittesting_html": f"{name}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{name}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{name}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{name}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{name}-CodeCoverage-SQLite", - "codecoverage_xml": f"{name}-CodeCoverage-XML", - "codecoverage_json": f"{name}-CodeCoverage-JSON", - "codecoverage_html": f"{name}-CodeCoverage-HTML", - "statictyping_html": f"{name}-StaticTyping-HTML", - "package_all": f"{name}-Packages", - "documentation_html": f"{name}-Documentation-HTML", - "documentation_latex": f"{name}-Documentation-LaTeX", - "documentation_pdf": f"{name}-Documentation-PDF", - } - print("Parameters:") print(f" python_version: {python_version}") print(f" python_jobs ({len(jobs)}):\n" + "".join([f" {{ " + ", ".join([f"\"{key}\": \"{value}\"" for key, value in job.items()]) + f" }},\n" for job in jobs]) ) - print(f" artifact_names ({len(artifact_names)}):") - for id, name in artifact_names.items(): - print(f" {id:>20}: {name}") # Write jobs to special file github_output = Path(getenv("GITHUB_OUTPUT")) @@ -343,13 +425,17 @@ jobs: f.write(dedent(f"""\ python_version={python_version} python_jobs={json_dumps(jobs)} - artifact_names={json_dumps(artifact_names)} """)) - name: Verify out parameters id: verify run: | - printf "python_version: %s\n" '${{ steps.params.outputs.python_version }}' - printf "python_jobs: %s\n" '${{ steps.params.outputs.python_jobs }}' - printf "artifact_names: %s\n" '${{ steps.params.outputs.artifact_names }}' - printf "params: %s\n" '${{ steps.params.outputs.params }}' + printf "python_version: %s\n" '${{ steps.variables.outputs.python_version }}' + printf "package_fullname: %s\n" '${{ steps.variables.outputs.package_fullname }}' + printf "package_directory: %s\n" '${{ steps.variables.outputs.package_directory }}' + printf "artifact_basename: %s\n" '${{ steps.variables.outputs.artifact_basename }}' + printf "====================\n" + printf "artifact_names: %s\n" '${{ steps.artifacts.outputs.artifact_names }}' + printf "====================\n" + printf "python_jobs: %s\n" '${{ steps.jobs.outputs.python_jobs }}' + printf "====================\n" diff --git a/.github/workflows/PrepareJob.yml b/.github/workflows/PrepareJob.yml index 7a3f4bd..84b334e 100644 --- a/.github/workflows/PrepareJob.yml +++ b/.github/workflows/PrepareJob.yml @@ -255,6 +255,9 @@ jobs: version=${version} EOF +# TODO: why not is_release_commit? +# TODO: how to support version branches and hotfix releases on version branches? + - name: 🔁 Find merged PullRequest from second parent of current SHA (${{ github.sha }}) id: FindPullRequest if: steps.Classify.outputs.is_merge_commit == 'true' diff --git a/.github/workflows/PublishCoverageResults.yml b/.github/workflows/PublishCoverageResults.yml index 281768d..31457df 100644 --- a/.github/workflows/PublishCoverageResults.yml +++ b/.github/workflows/PublishCoverageResults.yml @@ -38,6 +38,31 @@ on: required: false default: 'pyproject.toml' type: string + coverage_report_xml: + description: 'Directory containing the XML coverage report file.' + required: false + default: >- + { "directory": "report/coverage", + "filename": "coverage.xml", + "fullpath": "report/coverage/coverage.xml" + } + type: string + coverage_report_json: + description: 'Directory containing the JSON coverage report file.' + required: false + default: >- + { "directory": "report/coverage", + "filename": "coverage.json", + "fullpath": "report/coverage/coverage.json" + } + type: string + coverage_report_html: + description: 'HTML root directory of the generated coverage report.' + required: false + default: >- + { "directory": "report/coverage/html" + } + type: string coverage_sqlite_artifact: description: 'Name of the SQLite coverage artifact.' required: false @@ -48,41 +73,16 @@ on: required: false default: '' type: string - coverage_report_xml_directory: - description: 'Directory containing the XML coverage report file.' - required: false - default: 'report/coverage' - type: string - coverage_report_xml_filename: - description: 'Filename of the XML coverage report file.' - required: false - default: 'coverage.xml' - type: string coverage_json_artifact: description: 'Name of the JSON coverage artifact.' required: false default: '' type: string - coverage_report_json_directory: - description: 'Directory containing the JSON coverage report file.' - required: false - default: 'report/coverage' - type: string - coverage_report_json_filename: - description: 'Filename of the JSON coverage report file.' - required: false - default: 'coverage.json' - type: string coverage_html_artifact: description: 'Name of the HTML coverage artifact.' required: false default: '' type: string - coverage_report_html_directory: - description: 'HTML root directory of the generated coverage report.' - required: false - default: 'report/coverage/html' - type: string codecov: description: 'Publish merged coverage report to Codecov.' required: false @@ -138,22 +138,22 @@ jobs: run: coverage combine --data-file=.coverage coverage/ - name: Report code coverage - run: coverage report --rcfile=pyproject.toml --data-file=.coverage + run: coverage report --rcfile=${{ inputs.coverage_config }} --data-file=.coverage - name: Convert to XML format (Cobertura) if: inputs.coverage_xml_artifact != '' || inputs.codecov || inputs.codacy - run: coverage xml --data-file=.coverage + run: coverage xml --rcfile=${{ inputs.coverage_config }} --data-file=.coverage - name: Convert to JSON format if: inputs.coverage_json_artifact != '' - run: coverage json --data-file=.coverage + run: coverage json --rcfile=${{ inputs.coverage_config }} --data-file=.coverage - name: Convert to HTML format if: inputs.coverage_html_artifact != '' run: | - coverage html --data-file=.coverage -d report/coverage/html - rm report/coverage/html/.gitignore - tree -pash report/coverage/html + coverage html --rcfile=${{ inputs.coverage_config }} --data-file=.coverage + rm ${{ fromJson(inputs.coverage_report_html).directory }}/.gitignore + tree -pash ${{ fromJson(inputs.coverage_report_html).directory }} - name: 📤 Upload 'Coverage SQLite Database' artifact uses: pyTooling/upload-artifact@v4 @@ -171,8 +171,8 @@ jobs: continue-on-error: true with: name: ${{ inputs.coverage_xml_artifact }} - working-directory: ${{ inputs.coverage_report_xml_directory }} - path: ${{ inputs.coverage_report_xml_filename }} + working-directory: ${{ fromJson(inputs.coverage_report_xml).directory }} + path: ${{ fromJson(inputs.coverage_report_xml).filename }} if-no-files-found: error retention-days: 1 @@ -182,8 +182,8 @@ jobs: continue-on-error: true with: name: ${{ inputs.coverage_json_artifact }} - working-directory: ${{ inputs.coverage_report_json_directory }} - path: ${{ inputs.coverage_report_json_filename }} + working-directory: ${{ fromJson(inputs.coverage_report_json).directory }} + path: ${{ fromJson(inputs.coverage_report_json).filename }} if-no-files-found: error retention-days: 1 @@ -193,7 +193,7 @@ jobs: continue-on-error: true with: name: ${{ inputs.coverage_html_artifact }} - working-directory: ${{ inputs.coverage_report_html_directory }} + working-directory: ${{ fromJson(inputs.coverage_report_html).directory }} path: '*' if-no-files-found: error retention-days: 1 @@ -206,7 +206,7 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} disable_search: true - files: ${{ inputs.coverage_report_xml_directory }}/${{ inputs.coverage_report_xml_filename }} + files: ${{ fromJson(inputs.coverage_report_xml).fullpath }} flags: unittests env_vars: PYTHON fail_ci_if_error: true @@ -218,7 +218,7 @@ jobs: continue-on-error: true with: project-token: ${{ secrets.CODACY_TOKEN }} - coverage-reports: ${{ inputs.coverage_report_xml_directory }}/${{ inputs.coverage_report_xml_filename }} + coverage-reports: ${{ fromJson(inputs.coverage_report_xml).fullpath }} - name: Generate error messages run: | diff --git a/.github/workflows/PublishOnPyPI.yml b/.github/workflows/PublishOnPyPI.yml index 49e073c..cae9dfa 100644 --- a/.github/workflows/PublishOnPyPI.yml +++ b/.github/workflows/PublishOnPyPI.yml @@ -62,25 +62,25 @@ jobs: path: dist - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} - name: ⚙ Install dependencies for packaging and release run: python -m pip install --disable-pip-version-check ${{ inputs.requirements }} - - name: ⤴ Release Python source package to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: twine upload dist/*.tar.gz - - - name: ⤴ Release Python wheel package to PyPI + - name: ⤴ Publish Python wheel package to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: twine upload dist/*.whl + - name: ⤴ Publish Python source package to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: twine upload dist/*.tar.gz + - name: 🗑️ Delete packaging Artifacts uses: geekyeggo/delete-artifact@v5 with: diff --git a/.github/workflows/PublishReleaseNotes.yml b/.github/workflows/PublishReleaseNotes.yml index 3c92943..3a79f0a 100644 --- a/.github/workflows/PublishReleaseNotes.yml +++ b/.github/workflows/PublishReleaseNotes.yml @@ -41,8 +41,7 @@ on: type: string tag: description: 'Name of the release (tag).' - required: false - default: '' + required: true type: string title: description: 'Title of the release.' @@ -82,7 +81,7 @@ on: latest: description: 'Specify if this is the latest release.' required: false - default: false + default: true type: boolean replacements: description: 'Multi-line string containing search=replace patterns.' @@ -344,7 +343,7 @@ jobs: NOTES="${NOTES//%%gh_workflow_name%%/${{ github.workflow }}}" NOTES="${NOTES//%%gh_owner%%/${{ github.repository_owner }}}" NOTES="${NOTES//%%gh_repo%%/${repo}}" - NOTES="${NOTES//%%gh_owner_repo%%/${{ github.repository_owner }}}" + NOTES="${NOTES//%%gh_owner_repo%%/${{ github.repository }}}" #NOTES="${NOTES//%%gh_pages%%/https://${{ github.repository_owner }}.github.io/${repo}/}" NOTES="${NOTES//%%gh_runid%%/${{ github.run_id }}}" NOTES="${NOTES//%%gh_actor%%/${{ github.actor }}}" diff --git a/.github/workflows/SphinxDocumentation.yml b/.github/workflows/SphinxDocumentation.yml index fe6270d..997faed 100644 --- a/.github/workflows/SphinxDocumentation.yml +++ b/.github/workflows/SphinxDocumentation.yml @@ -44,24 +44,29 @@ on: required: false default: 'doc' type: string - coverage_report_json_directory: - description: '' - required: false - type: string coverage_json_artifact: description: 'Name of the coverage JSON artifact.' required: false default: '' type: string + coverage_report_json: + description: 'Directory where coverage JSON artifact will be extracted.' + required: false + default: >- + { "directory": "report/coverage" + } + type: string unittest_xml_artifact: description: 'Name of the unittest XML artifact.' required: false default: '' type: string - unittest_xml_directory: - description: 'Directory where unittest XML artifact is extracted.' + unittest_xml: + description: 'Directory where unittest XML artifact will be extracted.' required: false - default: 'report/unit' + default: >- + { "directory": "report/unit" + } type: string html_artifact: description: 'Name of the HTML documentation artifact.' @@ -90,7 +95,7 @@ jobs: run: sudo apt-get install -y --no-install-recommends graphviz - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} @@ -104,7 +109,7 @@ jobs: if: inputs.unittest_xml_artifact != '' with: name: ${{ inputs.unittest_xml_artifact }} - path: ${{ inputs.unittest_xml_directory }} + path: ${{ fromJson(inputs.unittest_xml).directory }} investigate: true - name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job @@ -112,7 +117,7 @@ jobs: if: inputs.coverage_json_artifact != '' with: name: ${{ inputs.coverage_json_artifact }} - path: ${{ inputs.coverage_report_json_directory }} + path: ${{ fromJson(inputs.coverage_report_json).directory }} investigate: true - name: ☑ Generate HTML documentation @@ -149,7 +154,7 @@ jobs: run: sudo apt-get install -y --no-install-recommends graphviz - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} @@ -163,7 +168,7 @@ jobs: if: inputs.unittest_xml_artifact != '' with: name: ${{ inputs.unittest_xml_artifact }} - path: ${{ inputs.unittest_xml_directory }} + path: ${{ fromJson(inputs.unittest_xml).directory }} investigate: true - name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job @@ -171,7 +176,7 @@ jobs: if: inputs.coverage_json_artifact != '' with: name: ${{ inputs.coverage_json_artifact }} - path: ${{ inputs.coverage_report_json_directory }} + path: ${{ fromJson(inputs.coverage_report_json).directory }} investigate: true - name: ☑ Generate LaTeX documentation diff --git a/.github/workflows/StaticTypeCheck.yml b/.github/workflows/StaticTypeCheck.yml index 3e417f7..431d239 100644 --- a/.github/workflows/StaticTypeCheck.yml +++ b/.github/workflows/StaticTypeCheck.yml @@ -40,32 +40,54 @@ on: required: false default: '-r tests/requirements.txt' type: string - commands: - description: 'Commands to run the static type checks.' - required: true + mypy_options: + description: 'Additional mypy options.' + required: false + default: '' + type: string + cobertura_report: + description: 'Cobertura file to upload as an artifact.' + required: false + default: >- + { "fullpath": "report/typing/cobertura.xml", + "directory": "report/typing", + "filename": "cobertura.xml" + } + type: string + junit_report: + description: 'JUnit file to upload as an artifact.' + required: false + default: >- + { "fullpath": "report/typing/StaticTypingSummary.xml", + "directory": "report/typing", + "filename": "StaticTypingSummary.xml" + } type: string html_report: description: 'Directory to upload as an artifact.' required: false - default: 'htmlmypy' + default: >- + { "directory": "report/typing/html" + } +# "fullpath": "report/typing/html", type: string - junit_report: - description: 'junit file to upload as an artifact.' + cobertura_artifact: + description: 'Name of the typing cobertura artifact (Cobertura XML).' required: false - default: 'StaticTypingSummary.xml' + default: '' + type: string + junit_artifact: + description: 'Name of the typing junit artifact (JUnit XML).' + required: false + default: '' type: string html_artifact: description: 'Name of the typing artifact (HTML report).' - required: true - type: string - junit_artifact: - description: 'Name of the typing junit artifact (junit XML).' required: false default: '' type: string jobs: - StaticTypeCheck: name: 👀 Check Static Typing using Python ${{ inputs.python_version }} runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" @@ -75,7 +97,7 @@ jobs: uses: actions/checkout@v5 - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} @@ -84,25 +106,70 @@ jobs: - name: Check Static Typing continue-on-error: true - run: ${{ inputs.commands }} + run: mypy ${{ inputs.mypy_options }} - - name: 📤 Upload 'Static Typing Report' HTML artifact + - name: Debug output directories + continue-on-error: true + run: | + # List directory contents + set +e + + ANSI_LIGHT_RED=$'\x1b[91m' + ANSI_LIGHT_GREEN=$'\x1b[92m' + ANSI_LIGHT_YELLOW=$'\x1b[93m' + ANSI_LIGHT_BLUE=$'\x1b[94m' + ANSI_NOCOLOR=$'\x1b[0m' + + if [[ "${{ fromJson(inputs.html_report).directory }}" != "" ]]; then + printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.html_report).directory }}' ..." + tree ${{ fromJson(inputs.html_report).directory }} + printf "::endgroup::\n" + fi + + if [[ "${{ fromJson(inputs.junit_report).directory }}" != "" ]]; then + printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.junit_report).directory }}' ..." + tree ${{ fromJson(inputs.junit_report).directory }} + printf "::endgroup::\n" + if [[ "${{ fromJson(inputs.cobertura_report).directory }}" != "" && "${{ fromJson(inputs.junit_report).directory }}" != "${{ fromJson(inputs.cobertura_report).directory }}" ]]; then + printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.cobertura_report).directory }}' ..." + tree ${{ fromJson(inputs.cobertura_report).directory }} + printf "::endgroup::\n" + fi + elif [[ "${{ fromJson(inputs.cobertura_report).directory }}" != "" ]]; then + printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.cobertura_report).directory }}' ..." + tree ${{ fromJson(inputs.cobertura_report).directory }} + printf "::endgroup::\n" + fi + + - name: 📤 Upload '${{ inputs.html_artifact }}' HTML artifact uses: pyTooling/upload-artifact@v4 if: ${{ inputs.html_artifact != '' }} continue-on-error: true with: name: ${{ inputs.html_artifact }} - working-directory: ${{ inputs.html_report }} + working-directory: ${{ fromJson(inputs.html_report).directory }} path: '*' if-no-files-found: error retention-days: 1 - - name: 📤 Upload 'Static Typing Report' JUnit artifact + - name: 📤 Upload '${{ inputs.junit_artifact }}' JUnit artifact uses: pyTooling/upload-artifact@v4 if: ${{ inputs.junit_artifact != '' }} continue-on-error: true with: name: ${{ inputs.junit_artifact }} - path: ${{ inputs.junit_report }} + working-directory: ${{ fromJson(inputs.junit_report).directory }} + path: ${{ fromJson(inputs.junit_report).filename }} + if-no-files-found: error + retention-days: 1 + + - name: 📤 Upload '${{ inputs.cobertura_artifact }}' Cobertura artifact + uses: pyTooling/upload-artifact@v4 + if: ${{ inputs.cobertura_artifact != '' }} + continue-on-error: true + with: + name: ${{ inputs.cobertura_artifact }} + working-directory: ${{ fromJson(inputs.cobertura_report).directory }} + path: ${{ fromJson(inputs.cobertura_report).filename }} if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/TagReleaseCommit.yml b/.github/workflows/TagReleaseCommit.yml index d110164..92ff0b1 100644 --- a/.github/workflows/TagReleaseCommit.yml +++ b/.github/workflows/TagReleaseCommit.yml @@ -54,11 +54,11 @@ jobs: permissions: contents: write # required for tag creation - actions: write # required to start a new pipeline + actions: write # required to start a new pipeline steps: - name: 🏷 Create release tag '${{ steps.FindPullRequest.outputs.version }}' - uses: actions/github-script@v7 + uses: actions/github-script@v8 id: createReleaseTag # if: inputs.auto_tag == 'true' with: @@ -71,7 +71,7 @@ jobs: }) - name: Trigger Workflow - uses: actions/github-script@v7 + uses: actions/github-script@v8 id: runReleaseTag # if: inputs.auto_tag == 'true' with: diff --git a/.github/workflows/TestReleaser.yml b/.github/workflows/TestReleaser.yml deleted file mode 100644 index 5a59486..0000000 --- a/.github/workflows/TestReleaser.yml +++ /dev/null @@ -1,176 +0,0 @@ -# ==================================================================================================================== # -# Authors: # -# Unai Martinez-Corral # -# # -# ==================================================================================================================== # -# Copyright 2020-2025 The pyTooling Authors # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -# # -# SPDX-License-Identifier: Apache-2.0 # -# ==================================================================================================================== # -name: Test Releaser - -on: - push: - tags: - - '*' - - '!tip' - - '!v*' - branches: - - '**' - - '!r*' - workflow_dispatch: - schedule: - - cron: '0 0 * * 4' - -env: - CI: true - -jobs: - - - Image: - runs-on: ubuntu-24.04 - env: - DOCKER_BUILDKIT: 1 - steps: - - uses: actions/checkout@v5 - - - name: Build container image - run: docker build -t ghcr.io/pytooling/releaser -f releaser/Dockerfile releaser - - - name: Push container image - uses: ./with-post-step - with: - main: | - echo '${{ github.token }}' | docker login ghcr.io -u GitHub-Actions --password-stdin - docker push ghcr.io/pytooling/releaser - post: docker logout ghcr.io - - - Composite: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v5 - - - run: printf "%s\n" "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt - - - name: Single - uses: ./releaser/composite - with: - rm: true - token: ${{ secrets.GITHUB_TOKEN }} - files: artifact-*.txt - - - name: List - uses: ./releaser/composite - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: | - artifact-*.txt - README.md - - - name: Add artifacts/*.txt - run: | - mkdir artifacts - printf "%s\n" "Build some tool and generate some artifacts" > artifacts/artifact.txt - touch artifacts/empty_file.txt - - - name: Single in subdir - uses: ./releaser/composite - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/artifact.txt - - - name: Add artifacts/*.md - run: | - printf "%s\n" "releaser hello" > artifacts/hello.md - printf "%s\n" "releaser world" > artifacts/world.md - - - name: Directory wildcard - uses: ./releaser/composite - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/* - - - name: Add artifacts/subdir - run: | - mkdir artifacts/subdir - printf "%s\n" "Test recursive glob" > artifacts/subdir/deep_file.txt - - - name: Directory wildcard (recursive) - uses: ./releaser/composite - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/** - - - Test: - needs: - - Image - - Composite - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v5 - - - run: printf "%s\n" "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt - - - name: Single - uses: ./releaser - with: - rm: true - token: ${{ secrets.GITHUB_TOKEN }} - files: artifact-*.txt - - - name: List - uses: ./releaser - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: | - artifact-*.txt - README.md - - - name: Add artifacts/*.txt - run: | - mkdir artifacts - printf "%s\n" "Build some tool and generate some artifacts" > artifacts/artifact.txt - touch artifacts/empty_file.txt - - - name: Single in subdir - uses: ./releaser - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/artifact.txt - - - name: Add artifacts/*.md - run: | - printf "%s\n" "releaser hello" > artifacts/hello.md - printf "%s\n" "releaser world" > artifacts/world.md - - - name: Directory wildcard - uses: ./releaser - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/* - - - name: Add artifacts/subdir - run: | - mkdir artifacts/subdir - printf "%s\n" "Test recursive glob" > artifacts/subdir/deep_file.txt - - - name: Directory wildcard (recursive) - uses: ./releaser - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: artifacts/** diff --git a/.github/workflows/UnitTesting.yml b/.github/workflows/UnitTesting.yml index 6433f76..7b7aa6a 100644 --- a/.github/workflows/UnitTesting.yml +++ b/.github/workflows/UnitTesting.yml @@ -85,34 +85,54 @@ on: default: '' type: string tests_directory: - description: 'Path to the directory containing tests (relative to root_directory).' + description: 'Path to the directory containing tests (relative from root_directory).' required: false default: 'tests' type: string unittest_directory: - description: 'Path to the directory containing unit tests (relative to tests_directory).' + description: 'Path to the directory containing unit tests (relative from tests_directory).' required: false default: 'unit' type: string - unittest_report_xml_directory: - description: 'Path where to save the unittest summary report XML.' + unittest_report_xml: + description: 'JSON object describing the path where to save the unittest summary report XML.' required: false - default: 'report/unit' - type: string - unittest_report_xml_filename: - description: 'Filename of the unittest summary report XML.' - required: false - default: 'TestReportSummary.xml' + default: >- + { "directory": "report/unit", + "filename": "TestReportSummary.xml", + "fullpath": "report/unit/TestReportSummary.xml" + } type: string coverage_config: description: 'Path to the .coveragerc file. Use pyproject.toml by default.' required: false default: 'pyproject.toml' type: string - coverage_report_html_directory: - description: '' + coverage_report_xml: + description: 'JSON object describing the path where the coverage report in XML format will be generated.' required: false - default: 'report/coverage/html' + default: >- + { "directory": "report/coverage", + "filename": "coverage.xml", + "fullpath": "report/coverage/coverage.xml" + } + type: string + coverage_report_json: + description: 'JSON object describing the path where the coverage report in JSON format will be generated.' + required: false + default: >- + { "directory": "report/coverage", + "filename": "coverage.json", + "fullpath": "report/coverage/coverage.json" + } + type: string + coverage_report_html: + description: 'JSON object describing the path where the coverage report in HTML format will be generated.' + required: false + default: >- + { "directory": "report/coverage/html", + } +# "fullpath": "report/coverage/html" type: string unittest_xml_artifact: description: "Generate unit test report with junitxml and upload results as an artifact." @@ -172,7 +192,7 @@ jobs: run: brew install ${{ inputs.brew }} - name: 🔧 Install apt dependencies on Ubuntu - if: matrix.system == 'ubuntu' && inputs.apt != '' + if: ( matrix.system == 'ubuntu' || matrix.system == 'ubuntu-arm' ) && inputs.apt != '' run: | sudo apt-get update sudo apt-get install -y --no-install-recommends ${{ inputs.apt }} @@ -293,7 +313,7 @@ jobs: ${{ inputs.pacboy }} - name: 🐍 Setup Python ${{ matrix.python }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 if: matrix.system != 'msys2' with: python-version: ${{ matrix.python }} @@ -326,10 +346,10 @@ jobs: run: ${{ inputs.macos_arm_before_script }} - name: 🐧 Ubuntu before scripts - if: matrix.system == 'ubuntu' && inputs.ubuntu_before_script != '' + if: ( matrix.system == 'ubuntu' || matrix.system == 'ubuntu-arm' ) && inputs.ubuntu_before_script != '' run: ${{ inputs.ubuntu_before_script }} - # Windows before script + # TODO: Windows before script - name: 🪟🟦 MinGW64 before scripts if: matrix.system == 'msys2' && matrix.runtime == 'MINGW64' && inputs.mingw64_before_script != '' @@ -341,16 +361,17 @@ jobs: # Run pytests + # TODO: allow configuration of pytest_args - name: ✅ Run unit tests (Ubuntu/macOS) id: pytest_bash - if: matrix.system != 'windows' + if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' ) continue-on-error: true run: | export ENVIRONMENT_NAME="${{ matrix.envname }}" export PYTHONPATH=$(pwd) cd "${{ inputs.root_directory || '.' }}" - [ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}' || unset PYTEST_ARGS + [ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=${{ fromJson(inputs.unittest_report_xml).fullpath }}' || unset PYTEST_ARGS if [ -n '${{ inputs.coverage_config }}' ]; then printf "%s\n" "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }} @@ -361,14 +382,14 @@ jobs: - name: ✅ Run unit tests (Windows) id: pytest_posh - if: matrix.system == 'windows' + if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' ) continue-on-error: true run: | $env:ENVIRONMENT_NAME = "${{ matrix.envname }}" $env:PYTHONPATH = (Get-Location).ToString() cd "${{ inputs.root_directory || '.' }}" - $PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}" } else { "" } + $PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=${{ fromJson(inputs.unittest_report_xml).fullpath }}" } else { "" } if ("${{ inputs.coverage_config }}") { Write-Host "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }} @@ -394,19 +415,19 @@ jobs: if: inputs.coverage_html_artifact != '' continue-on-error: true run: | - coverage html --data-file=.coverage -d ${{ inputs.coverage_report_html_directory }} - rm ${{ inputs.coverage_report_html_directory }}/.gitignore + coverage html --data-file=.coverage -d ${{ fromJson(inputs.coverage_report_html).directory }} + rm ${{ fromJson(inputs.coverage_report_html).directory }}/.gitignore # Upload artifacts - - name: 📤 Upload '${{ inputs.unittest_report_xml_filename }}' artifact + - name: 📤 Upload '${{ fromJson(inputs.unittest_report_xml).filename }}' artifact uses: pyTooling/upload-artifact@v4 if: inputs.unittest_xml_artifact != '' continue-on-error: true with: name: ${{ inputs.unittest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} - working-directory: ${{ inputs.unittest_report_xml_directory }} - path: ${{ inputs.unittest_report_xml_filename }} + working-directory: ${{ fromJson(inputs.unittest_report_xml).directory }} + path: ${{ fromJson(inputs.unittest_report_xml).filename }} if-no-files-found: error retention-days: 1 @@ -416,7 +437,7 @@ jobs: # uses: pyTooling/upload-artifact@v4 # with: # name: ${{ inputs.unittest_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} -# path: ${{ steps.getVariables.outputs.unittest_report_html_directory }} +# path: ${{ inputs.unittest_report_html_directory }} # if-no-files-found: error # retention-days: 1 @@ -437,7 +458,8 @@ jobs: uses: pyTooling/upload-artifact@v4 with: name: ${{ inputs.coverage_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} - path: ${{ steps.getVariables.outputs.coverage_report_xml }} + working-directory: ${{ fromJson(inputs.coverage_report_xml).directory }} + path: ${{ fromJson(inputs.coverage_report_xml).filename }} if-no-files-found: error retention-days: 1 @@ -447,7 +469,8 @@ jobs: uses: pyTooling/upload-artifact@v4 with: name: ${{ inputs.coverage_json_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} - path: ${{ steps.getVariables.outputs.coverage_report_json }} + working-directory: ${{ fromJson(inputs.coverage_report_json).directory }} + path: ${{ fromJson(inputs.coverage_report_json).filename }} if-no-files-found: error retention-days: 1 @@ -457,7 +480,7 @@ jobs: uses: pyTooling/upload-artifact@v4 with: name: ${{ inputs.coverage_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} - working-directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }} + working-directory: ${{ fromJson(inputs.coverage_report_html).directory }} path: '*' if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/VerifyDocs.yml b/.github/workflows/VerifyDocs.yml index 52bf544..637c457 100644 --- a/.github/workflows/VerifyDocs.yml +++ b/.github/workflows/VerifyDocs.yml @@ -47,7 +47,7 @@ jobs: uses: actions/checkout@v5 - name: 🐍 Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ inputs.python_version }} diff --git a/.github/workflows/_Checking_AvailableRunners.yml b/.github/workflows/_Checking_AvailableRunners.yml new file mode 100644 index 0000000..1b8b7ea --- /dev/null +++ b/.github/workflows/_Checking_AvailableRunners.yml @@ -0,0 +1,38 @@ +name: Testing available GitHub Action Images + +on: + push: + workflow_dispatch: + +jobs: + RunnerTest: + name: ${{ matrix.icon }} ${{ matrix.name }} + + runs-on: ${{ matrix.image }} + continue-on-error: ${{ matrix.can-fail }} + strategy: + fail-fast: false + matrix: + include: + - {icon: '🐧', name: 'Ubuntu 22.04 (x86-64)', image: 'ubuntu-22.04', shell: 'bash', can-fail: false} + - {icon: '🐧', name: 'Ubuntu 24.04 (x86-64)', image: 'ubuntu-24.04', shell: 'bash', can-fail: false} # latest + - {icon: '🍎', name: 'macOS-13 (x86-64)', image: 'macos-13', shell: 'bash', can-fail: false} + - {icon: '🍎', name: 'macOS-14 (x86-64)', image: 'macos-14-large', shell: 'bash', can-fail: true } # not in free plan + - {icon: '🍎', name: 'macOS-15 (x86-64)', image: 'macos-15-large', shell: 'bash', can-fail: true } # not in free plan + - {icon: '🍏', name: 'macOS-13 (aarch64)', image: 'macos-13-xlarge', shell: 'bash', can-fail: true } # not in free plan + - {icon: '🍏', name: 'macOS-14 (aarch64)', image: 'macos-14', shell: 'bash', can-fail: false} # latest + - {icon: '🍏', name: 'macOS-15 (aarch64)', image: 'macos-15', shell: 'bash', can-fail: false} + - {icon: '🪟', name: 'Windows Server 2022', image: 'windows-2022', shell: 'bash', can-fail: false} + - {icon: '🪟', name: 'Windows Server 2025', image: 'windows-2025', shell: 'bash', can-fail: false} # latest + # Third party images by ARM for aarch64 + - {icon: '⛄', name: 'Ubuntu 22.04 (aarch64)', image: 'ubuntu-22.04-arm', shell: 'bash', can-fail: false} + - {icon: '⛄', name: 'Ubuntu 24.04 (aarch64)', image: 'ubuntu-24.04-arm', shell: 'bash', can-fail: false} + - {icon: '🏢', name: 'Windows 11 (arch64)', image: 'windows-11-arm', shell: 'bash', can-fail: false} + + defaults: + run: + shell: ${{ matrix.shell }} + + steps: + - name: 'uname -a' + run: uname -a diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 8a2be41..561cdfd 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -10,28 +10,28 @@ jobs: ConfigParams: uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@main - with: - package_name: pyDummy - - InstallParams: - uses: pyTooling/Actions/.github/workflows/Parameters.yml@main - with: - package_name: pyDummy - python_version_list: '' UnitTestingParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: - name: pyDummy - python_version_list: "3.9 3.10 3.11 3.12 3.13 pypy-3.9 pypy-3.10" -# disable_list: "windows:pypy-3.10" + package_name: 'myPackage' + python_version_list: '3.9 3.10 3.11 3.12 3.13 3.14 pypy-3.10 pypy-3.11' + disable_list: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11' PlatformTestingParams: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: - name: Platform - python_version_list: "" - system_list: "ubuntu windows macos mingw64 clang64 ucrt64" + package_name: 'myPackage' + name: 'Platform' + python_version_list: '' + system_list: 'ubuntu ubuntu-arm windows windows-arm macos mingw64 clang64 ucrt64' + + InstallParams: + uses: pyTooling/Actions/.github/workflows/Parameters.yml@main + with: + package_name: 'myPackage' + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + python_version_list: '' UnitTesting: uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main @@ -39,16 +39,17 @@ jobs: - ConfigParams - UnitTestingParams with: - jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} - unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} - unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} - coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }} - unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} - unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }} - coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} -# coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} -# coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} -# coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} + jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} + unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} + coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }} + unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} + unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }} + coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} + coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} + coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} + coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} PlatformTesting: uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main @@ -56,18 +57,19 @@ jobs: - ConfigParams - PlatformTestingParams with: - jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }} + jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }} # tests_directory: "" - unittest_directory: platform - unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} - unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} - coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }} - unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }} - unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }} - coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }} - coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }} - coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }} - coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }} + unittest_directory: platform + unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} + coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }} + unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }} + unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }} + coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }} + coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }} + coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }} + coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }} StaticTypeCheck: uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@main @@ -76,11 +78,17 @@ jobs: - UnitTestingParams with: python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - commands: | - ${{ needs.ConfigParams.outputs.mypy_prepare_command }} - mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }} - html_report: 'report/typing' - html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} + html_report: ${{ needs.ConfigParams.outputs.typing_report_html }} + html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} + + CodeQuality: + uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@main + needs: + - UnitTestingParams + with: + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + package_directory: ${{ needs.UnitTestingParams.outputs.package_directory }} + artifact: CodeQuality DocCoverage: uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@main @@ -105,14 +113,13 @@ jobs: Install: uses: pyTooling/Actions/.github/workflows/InstallPackage.yml@main needs: - - ConfigParams - UnitTestingParams - InstallParams - Package with: jobs: ${{ needs.InstallParams.outputs.python_jobs }} wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} - package_name: ${{ needs.ConfigParams.outputs.package_fullname }} + package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }} PublishCoverageResults: uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@main @@ -122,17 +129,15 @@ jobs: - UnitTesting - PlatformTesting with: - coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} - coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} - coverage_report_xml_directory: ${{ needs.ConfigParams.outputs.coverage_report_xml_directory }} - coverage_report_xml_filename: ${{ needs.ConfigParams.outputs.coverage_report_xml_filename }} - coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} - coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} - coverage_report_json_filename: ${{ needs.ConfigParams.outputs.coverage_report_json_filename }} - coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} - coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }} - codecov: true - codacy: true + coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} + coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }} + coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} + coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} + coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} + coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} + codecov: true + codacy: true secrets: inherit PublishTestResults: @@ -144,7 +149,7 @@ jobs: - PlatformTesting with: additional_merge_args: '-d "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit;reduce-depth:pytest.tests.platform"' - testsuite-summary-name: ${{ needs.ConfigParams.outputs.package_fullname }} + testsuite-summary-name: ${{ needs.UnitTestingParams.outputs.package_fullname }} merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} codecov: true dorny: true @@ -166,12 +171,13 @@ jobs: - PublishCoverageResults # - VerifyDocs with: - python_version: ${{ needs.UnitTestingParams.outputs.python_version }} - coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} + python_version: ${{ needs.UnitTestingParams.outputs.python_version }} + unittest_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} + coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }} unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} - html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} - latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} + html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} + latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} IntermediateCleanUp: uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@main @@ -189,9 +195,9 @@ jobs: - UnitTestingParams - Documentation with: - document: Actions + document: 'Actions' latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} - pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }} + pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }} PublishToGitHubPages: uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main diff --git a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml index 0e2b772..90aba99 100644 --- a/.github/workflows/_Checking_NamespacePackage_Pipeline.yml +++ b/.github/workflows/_Checking_NamespacePackage_Pipeline.yml @@ -8,8 +8,8 @@ jobs: NamespacePackage: uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@main with: - package_namespace: pyExamples - package_name: Extensions + package_namespace: myFramework + package_name: Extension codecov: true codacy: true dorny: true diff --git a/.github/workflows/_Checking_Nightly.yml b/.github/workflows/_Checking_Nightly.yml index 799146a..12c9fd1 100644 --- a/.github/workflows/_Checking_Nightly.yml +++ b/.github/workflows/_Checking_Nightly.yml @@ -42,7 +42,7 @@ jobs: retention-days: 1 NightlyPage: - uses: pyTooling/Actions/.github/workflows/NightlyRelease.yml@main + uses: ./.github/workflows/PublishReleaseNotes.yml needs: - Build permissions: @@ -55,8 +55,9 @@ jobs: version=4.2.0 tool=myTool prog=program - nightly_title: "Nightly Test Release" - nightly_description: | + tag: 4.2.0 + title: "Nightly Test Release" + description: | This *nightly* release contains all latest and important artifacts created by %tool%'s CI pipeline. # %tool% %version% @@ -79,7 +80,7 @@ jobs: secrets: inherit NightlyPageWithInventory: - uses: ./.github/workflows/NightlyRelease.yml + uses: ./.github/workflows/PublishReleaseNotes.yml needs: - Build permissions: @@ -91,15 +92,15 @@ jobs: version=4.2.0 tool=myTool prog=program - nightly_name: inventory - nightly_title: "Nightly Test Release with Inventory" - nightly_description: | + tag: inventory + title: "Nightly Test Release with Inventory" + description: | This *nightly* release contains all latest and important artifacts created by %tool%'s CI pipeline. # %tool% %version% * %prog% - * iventory.json + * inventory.json inventory-json: "inventory.json" inventory-version: 4.2.5 inventory-categories: "kind1,kind2" diff --git a/.github/workflows/_Checking_Parameters.yml b/.github/workflows/_Checking_Parameters.yml index 2aadc30..d9952eb 100644 --- a/.github/workflows/_Checking_Parameters.yml +++ b/.github/workflows/_Checking_Parameters.yml @@ -14,7 +14,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: name: Example - python_version_list: "3.11 3.12 pypy-3.9 pypy-3.10" + python_version_list: "3.12 3.13 pypy-3.10 pypy-3.11" Params_Systems: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main @@ -26,25 +26,25 @@ jobs: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: name: Example - python_version_list: "3.11" + python_version_list: "3.12" system_list: "ubuntu windows macos macos-arm" - include_list: "ubuntu:3.12 ubuntu:3.13" + include_list: "ubuntu:3.13 ubuntu:3.14 ubuntu-arm:3.12" Params_Exclude: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: name: Example - python_version_list: "3.12" + python_version_list: "3.13" system_list: "ubuntu windows macos macos-arm" - exclude_list: "windows:3.12 windows:3.13" + exclude_list: "windows:3.13 windows:3.14" Params_Disable: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main with: name: Example - python_version_list: "3.12" + python_version_list: "3.13" system_list: "ubuntu windows macos macos-arm" - disable_list: "windows:3.12 windows:3.13" + disable_list: "windows:3.13 windows:3.14" Params_All: uses: pyTooling/Actions/.github/workflows/Parameters.yml@main @@ -55,15 +55,9 @@ jobs: include_list: "windows:3.10 windows:3.11 windows:3.13" exclude_list: "macos:3.12 macos:3.13" - Params_Check: + Params_Check_Default: needs: - Params_Default - - Params_PythonVersions - - Params_Systems - - Params_Include - - Params_Exclude - - Params_Disable - - Params_All runs-on: ubuntu-24.04 defaults: run: @@ -72,7 +66,7 @@ jobs: - name: Install dependencies shell: bash run: pip install --disable-pip-version-check --break-system-packages pyTooling - # Params_Default + - name: Checking results from 'Params_Default' run: | from json import loads as json_loads @@ -82,24 +76,28 @@ jobs: expectedPythonVersion = "3.13" expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"] - expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.12", "ucrt64:3.11"] + expectedSystems = ["ubuntu", "ubuntu-arm", "windows", "windows-arm", "macos", "macos-arm"] + excludedJobs = ["windows-arm:3.9", "windows-arm:3.10"] + includeJobs = ["mingw64:3.12", "ucrt64:3.12"] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_Default.outputs.python_version }}""" @@ -135,7 +133,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_PythonVersions + Params_Check_PythonVersions: + needs: + - Params_PythonVersions + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_PythonVersions' run: | from json import loads as json_loads @@ -144,25 +153,29 @@ jobs: from pyTooling.Common import zipdicts expectedPythonVersion = "3.13" - expectedPythons = ["3.11", "3.12", "pypy-3.9", "pypy-3.10"] - expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.12", "ucrt64:3.11"] + expectedPythons = ["3.12", "3.13", "pypy-3.10", "pypy-3.11"] + expectedSystems = ["ubuntu", "ubuntu-arm", "windows", "windows-arm", "macos", "macos-arm"] + excludedJobs = ["windows-arm:pypy-3.10", "windows-arm:pypy-3.11"] + includeJobs = ["mingw64:3.12", "ucrt64:3.12"] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_PythonVersions.outputs.python_version }}""" @@ -198,7 +211,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_Systems + Params_Check_Systems: + needs: + - Params_Systems + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_Systems' run: | from json import loads as json_loads @@ -209,23 +233,27 @@ jobs: expectedPythonVersion = "3.13" expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"] expectedSystems = ["windows"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw32:3.12", "mingw64:3.11"] + excludedJobs = [] + includeJobs = ["mingw64:3.12", "ucrt64:3.12"] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_Systems.outputs.python_version }}""" @@ -261,7 +289,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_Include + Params_Check_Include: + needs: + - Params_Include + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_Include' run: | from json import loads as json_loads @@ -272,23 +311,27 @@ jobs: expectedPythonVersion = "3.13" expectedPythons = ["3.12"] expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["ubuntu:3.11", "ubuntu:3.12"] + excludedJobs = [] + includeJobs = ["ubuntu:3.13", "ubuntu:3.14", "ubuntu-arm:3.12"] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_Include.outputs.python_version }}""" @@ -324,7 +367,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_Exclude + Params_Check_Exclude: + needs: + - Params_Exclude + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_Exclude' run: | from json import loads as json_loads @@ -333,25 +387,29 @@ jobs: from pyTooling.Common import zipdicts expectedPythonVersion = "3.13" - expectedPythons = ["3.12"] + expectedPythons = ["3.13"] expectedSystems = ["ubuntu", "macos", "macos-arm"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + excludedJobs = [] + includeJobs = [] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}""" @@ -387,7 +445,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_Disable + Params_Check_Disable: + needs: + - Params_Disable + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_Disable' run: | from json import loads as json_loads @@ -396,30 +465,34 @@ jobs: from pyTooling.Common import zipdicts expectedPythonVersion = "3.13" - expectedPythons = ["3.12"] - expectedSystems = ["ubuntu", "macos", "macos-arm"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + expectedPythons = ["3.13"] + expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] + excludedJobs = ["windows:3.13"] + includeJobs = [] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } - actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}""" - actualPythonJobs = json_loads("""${{ needs.Params_Exclude.outputs.python_jobs }}""".replace("'", '"')) - actualArtifactNames = json_loads("""${{ needs.Params_Exclude.outputs.artifact_names }}""".replace("'", '"')) + actualPythonVersion = """${{ needs.Params_Disable.outputs.python_version }}""" + actualPythonJobs = json_loads("""${{ needs.Params_Disable.outputs.python_jobs }}""".replace("'", '"')) + actualArtifactNames = json_loads("""${{ needs.Params_Disable.outputs.artifact_names }}""".replace("'", '"')) errors = 0 if actualPythonVersion != expectedPythonVersion: @@ -450,7 +523,18 @@ jobs: print(f"All checks PASSED.") exit(errors) - # Params_All + Params_Check_All: + needs: + - Params_All + runs-on: ubuntu-24.04 + defaults: + run: + shell: python + steps: + - name: Install dependencies + shell: bash + run: pip install --disable-pip-version-check --break-system-packages pyTooling + - name: Checking results from 'Params_All' run: | from json import loads as json_loads @@ -461,23 +545,27 @@ jobs: expectedPythonVersion = "3.13" expectedPythons = ["3.12", "3.13"] expectedSystems = ["ubuntu", "macos-arm", "windows"] - expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["windows:3.10", "windows:3.11", "windows:3.13"] + excludedJobs = [] + includeJobs = ["windows:3.10", "windows:3.11", "windows:3.13"] + expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs expectedName = "Example" expectedArtifacts = { - "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", - "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", - "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", - "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", - "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", - "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", - "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", - "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", - "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", - "statictyping_html": f"{expectedName}-StaticTyping-HTML", - "package_all": f"{expectedName}-Packages", - "documentation_html": f"{expectedName}-Documentation-HTML", - "documentation_latex": f"{expectedName}-Documentation-LaTeX", - "documentation_pdf": f"{expectedName}-Documentation-PDF", + "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", + "unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML", + "perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML", + "benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML", + "apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML", + "codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite", + "codecoverage_xml": f"{expectedName}-CodeCoverage-XML", + "codecoverage_json": f"{expectedName}-CodeCoverage-JSON", + "codecoverage_html": f"{expectedName}-CodeCoverage-HTML", + "statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML", + "statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML", + "statictyping_html": f"{expectedName}-StaticTyping-HTML", + "package_all": f"{expectedName}-Packages", + "documentation_html": f"{expectedName}-Documentation-HTML", + "documentation_latex": f"{expectedName}-Documentation-LaTeX", + "documentation_pdf": f"{expectedName}-Documentation-PDF", } actualPythonVersion = """${{ needs.Params_All.outputs.python_version }}""" diff --git a/.github/workflows/_Checking_SimplePackage_Pipeline.yml b/.github/workflows/_Checking_SimplePackage_Pipeline.yml index 5a6f1cf..e15a6db 100644 --- a/.github/workflows/_Checking_SimplePackage_Pipeline.yml +++ b/.github/workflows/_Checking_SimplePackage_Pipeline.yml @@ -8,11 +8,11 @@ jobs: SimplePackage: uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@main with: - package_name: pyDummy - codecov: true - codacy: true - dorny: true - cleanup: false + package_name: myPackage + codecov: true + codacy: true + dorny: true + cleanup: false secrets: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 09fadda..b241e00 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ coverage.xml /report/unit /tests/*.github +# bandit +/report/bandit + # setuptools /build/**/*.* /dist/**/*.* @@ -25,8 +28,8 @@ coverage.xml # Sphinx doc/_build/ -doc/pyDummy/**/*.* -!doc/pyDummy/index.rst +doc/myPackage/**/*.* +!doc/myPackage/index.rst # BuildTheDocs doc/_theme/**/*.* diff --git a/.idea/Actions.iml b/.idea/Actions.iml index d8b3f6c..b80e16a 100644 --- a/.idea/Actions.iml +++ b/.idea/Actions.iml @@ -1,7 +1,13 @@ - + + + + + + + diff --git a/doc/Action/Actions.rst b/doc/Action/Actions.rst new file mode 100644 index 0000000..98ad02e --- /dev/null +++ b/doc/Action/Actions.rst @@ -0,0 +1,15 @@ +.. grid:: 2 + + .. grid-item:: + :columns: 2 + + .. rubric:: Post-Processing + + * :ref:`ACTION/WithPostStep` + + .. grid-item:: + :columns: 2 + + .. rubric:: Deprecated + + * :ref:`ACTION/Releaser` diff --git a/doc/Action/Releaser.rst b/doc/Action/Releaser.rst index 588da9d..36b17ba 100644 --- a/doc/Action/Releaser.rst +++ b/doc/Action/Releaser.rst @@ -1,8 +1,17 @@ .. _ACTION/Releaser: +.. index:: + single: GitHub Action; Releaser Releaser ######## +.. attention:: + + The **Releaser** action is deprecated. + + Use the new GitHub Action workflow template :ref:`JOBTMPL/PublishReleaseNotes` as a replacement with lots of + additional features. + **Releaser** is a Docker GitHub Action written in Python. **Releaser** allows to keep a GitHub Release of type pre-release and its artifacts up to date with latest builds. diff --git a/doc/Action/With-post-step.rst b/doc/Action/With-post-step.rst index f384445..9f4f3c5 100644 --- a/doc/Action/With-post-step.rst +++ b/doc/Action/With-post-step.rst @@ -1,4 +1,6 @@ .. _ACTION/WithPostStep: +.. index:: + single: GitHub Action; WithPostStep with-post-step ############## diff --git a/doc/Action/index.rst b/doc/Action/index.rst index 44eef5f..8fb30a6 100644 --- a/doc/Action/index.rst +++ b/doc/Action/index.rst @@ -1,7 +1,31 @@ +.. _ACTION: +.. index:: + single: GitHub Action + Overview ######## The following 2 actions are provided by **Actions**: -* :ref:`ACTION/Releaser` -* :ref:`ACTION/WithPostStep` +.. include:: Actions.rst + + +.. _ACTION/Instantiation: +.. index:: + single: GitHub Action; Instantiation + +Instantiation +************* + +.. code-block:: yaml + + jobs: + : + steps: + - ... + + - name: + uses: ./with-post-step + with: + : + : diff --git a/doc/CodeCoverage.rst b/doc/CodeCoverage.rst new file mode 100644 index 0000000..217fd1e --- /dev/null +++ b/doc/CodeCoverage.rst @@ -0,0 +1,25 @@ +.. _CODECOV: + +Code Coverage Report +#################### + +.. grid:: 2 + + .. grid-item:: + :columns: 8 + + .. report:code-coverage:: + :reportid: src + + .. grid-item:: + :columns: 4 + + .. report:code-coverage-legend:: + :reportid: src + :style: vertical-table + +---------- + +Code coverage report generated with `pytest `__, +`Coverage.py `__ and visualized by +`sphinx-reports `__. diff --git a/doc/DocCoverage.rst b/doc/DocCoverage.rst new file mode 100644 index 0000000..d2ee388 --- /dev/null +++ b/doc/DocCoverage.rst @@ -0,0 +1,24 @@ +.. _DOCCOV: + +Documentation Coverage Report +############################# + +.. grid:: 2 + + .. grid-item:: + :columns: 5 + + .. report:doc-coverage:: + :reportid: src + + .. grid-item:: + :columns: 7 + + .. report:doc-coverage-legend:: + :reportid: src + :style: vertical-table + +---------- + +Documentation coverage generated with `"""docstr-coverage""" `__ and +visualized by `sphinx-reports `__. diff --git a/doc/Glossary.rst b/doc/Glossary.rst new file mode 100644 index 0000000..f98a866 --- /dev/null +++ b/doc/Glossary.rst @@ -0,0 +1,128 @@ +Glossary +######## + +.. glossary:: + + Bandit + Bandit is a tool designed to find common security issues in Python code. + + :Source Code: `github.com/PyCQA/bandit/ `__ + :Package: `pypi.org/project/bandit/ `__ + :Documentation: `bandit.readthedocs.io/ `__ + + build + A simple, correct Python build frontend. + + :Source Code: `github.com/pypa/build/ `__ + :Package: `pypi.org/project/build/ `__ + :Documentation: `build.pypa.io/ `__ + + Codacy + .. todo:: Add description of Codacy. + + :Cloud Service: `Codacy.com `__ + + CodeCov + .. todo:: Add description of CodeCov. + + :Cloud Service: `Codecov.io `__ + + Coverage.py + The code coverage tool for Python. + + :Source Code: `github.com/nedbat/coveragepy/ `__ + :Package: `pypi.org/project/coverage/ `__ + :Documentation: `coverage.readthedocs.io/ `__ + + delete-artifact + A GitHub Action to deletes artifacts within the workflow run. + + :Source Code: `github.com/GeekyEggo/delete-artifact/ `__ + :Marketplace: `github.com/marketplace/actions/delete-artifact/ `__ + :README: `github.com/GeekyEggo/delete-artifact ⭢ README.md `__ + + docstr_coverage + Docstring coverage analysis and rating for Python. + + :Source Code: `github.com/HunterMcGushion/docstr_coverage/ `__ + :Package: `pypi.org/project/docstr_coverage/ `__ + :Documentation: `docstr-coverage.readthedocs.io/ `__ + + gh + GitHub’s official command line tool. + + :Source Code: `github.com/cli/cli/ `__ + :Documentation: `cli.github.com/manual/ `__ + + GitHub Pages + GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files straight from a repository + on GitHub, optionally runs the files through a build process, and publishes a website. + + :Documentation: https://docs.github.com/en/pages + + interrogate + Explain yourself! Interrogate a codebase for docstring coverage. + + :Source Code: `github.com/econchick/interrogate/ `__ + :Package: `pypi.org/project/interrogate/ `__ + :Documentation: `interrogate.readthedocs.io/ `__ + + MikTeX + MiKTeX is a modern TeX distribution for Windows, Linux and macOS. + + :Source Code: `github.com/MiKTeX/miktex/ `__ + :Documentation: `miktex.org/ `__ + + mypy + Optional static typing for Python. + + :Source Code: `github.com/python/mypy/ `__ + :Package: `pypi.org/project/mypy/ `__ + :Documentation: `www.mypy-lang.org/ `__ + + pyEDAA.Reports + A collection of various (EDA tool-specific) report data formats. + + :Source Code: `github.com/edaa-org/pyEDAA.Reports/ `__ + :Package: `pypi.org/project/pyEDAA.Reports/ `__ + :Documentation: `edaa-org.github.io/pyEDAA.Reports/ `__ + + pip + The Python package installer. + + :Source Code: `github.com/pypa/pip/ `__ + :Package: `pypi.org/project/pip/ `__ + :Documentation: `pip.pypa.io/ `__ + + PyPI + Find, install and publish Python packages with the Python Package Index. + + :Cloud Service: `PyPI.org `__ + + pytest + The pytest framework makes it easy to write small tests, yet scales to support complex functional testing. + + :Source Code: `github.com/pytest-dev/pytest/ `__ + :Package: `pypi.org/project/pytest/ `__ + :Documentation: `pytest.org/ `__ + + Sphinx + The Sphinx documentation generator. + + :Source Code: `github.com/sphinx-doc/sphinx/ `__ + :Package: `pypi.org/project/sphinx/ `__ + :Documentation: `www.sphinx-doc.org/ `__ + + Test Reporter + Displays test results from popular testing frameworks directly in GitHub. + + :Source Code: `github.com/dorny/test-reporter/ `__ + :Marketplace: `github.com/marketplace/actions/test-reporter/ `__ + :README: `github.com/dorny/test-reporter ⭢ README.md `__ + + twine + Utilities for interacting with PyPI. + + :Source Code: `github.com/pypa/twine/ `__ + :Package: `pypi.org/project/twine/ `__ + :Documentation: `twine.readthedocs.io/ `__ diff --git a/doc/Instantiation.rst b/doc/Instantiation.rst index 3b18faf..7d6c5f7 100644 --- a/doc/Instantiation.rst +++ b/doc/Instantiation.rst @@ -36,10 +36,13 @@ to handover input parameters to the template. on: push: workflow_dispatch: + schedule: + # Every Friday at 22:00 - rerun pipeline to check for dependency-based issues + - cron: '0 22 * * 5' jobs: : - uses: //.github/workflows/