From 1f3d12ef95e034b423777f4632ccd65ccb4df304 Mon Sep 17 00:00:00 2001 From: umarcor Date: Tue, 21 Dec 2021 01:38:52 +0100 Subject: [PATCH 01/18] README: add 'Container Step' to Context; add References --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d57a14b..120ed78 100644 --- a/README.md +++ b/README.md @@ -7,24 +7,30 @@ language for writing reusable CI code. However, Python being equally popular and capable, usage of JS/TS might be bypassed, with some caveats. This repository gathers reusable CI tooling for testing, packaging and distributing Python projects and documentation. + ## Context -GitHub Actions supports four types of reusable code: +GitHub Actions supports five procedures to reuse code: -- JavaScript Action. +- JavaScript Action: - [docs.github.com: actions/creating-actions/creating-a-javascript-action](https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action) -- Container Action. +- Container Action: - [docs.github.com: actions/creating-actions/creating-a-docker-container-action](https://docs.github.com/en/actions/creating-actions/creating-a-docker-container-action) -- Composite Action. +- Container Step: + - [docs.github.com: actions/learn-github-actions/workflow-syntax-for-github-actions#example-using-a-docker-public-registry-action](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#example-using-a-docker-public-registry-action) + - [docs.github.com: actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswithargs](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswithargs) +- Composite Action: - [docs.github.com: actions/creating-actions/creating-a-composite-action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action) - [github.blog/changelog: 2020-08-07-github-actions-composite-run-steps](https://github.blog/changelog/2020-08-07-github-actions-composite-run-steps/) - [github.blog/changelog: 2021-08-25-github-actions-reduce-duplication-with-action-compositio](https://github.blog/changelog/2021-08-25-github-actions-reduce-duplication-with-action-composition/) -- Reusable Workflows. +- Reusable Workflow: - [docs.github.com: actions/learn-github-actions/reusing-workflows](https://docs.github.com/en/actions/learn-github-actions/reusing-workflows) - [github.blog/changelog: 2021-10-05-github-actions-dry-your-github-actions-configuration-by-reusing-workflows](https://github.blog/changelog/2021-10-05-github-actions-dry-your-github-actions-configuration-by-reusing-workflows/) -Leaving JavaScript and Container Actions aside, the main differences between Composite Actions and Reusable Workflows -are the following: +Container Actions and Container Steps are almost equivalent: Actions use a configuration file (`action.yml`), while +Steps do not. +Leaving JavaScript and Container Actions and Steps aside, the main differences between Composite Actions and Reusable +Workflows are the following: - Composite Actions can be executed from a remote/external path or from the checked out branch, and from any location. However, Reusable Workflows can only be used through a remote/external path (`{owner}/{repo}/{path}/{filename}@{ref}`), @@ -74,6 +80,7 @@ It allows using the `post` feature with scripts written in bash, python or any o the environment. See: [actions/runner#1478](https://github.com/actions/runner/issues/1478). + ## Reusable workflows This repository provides 10+ Reusable Workflows based on the CI pipelines of the repos in this organisation, @@ -150,19 +157,23 @@ Find further usage cases in the following list of projects: - [VHDL/pyVHDLModel](https://github.com/VHDL/pyVHDLModel/tree/main/.github/workflows) +## References + +- [hdl/containers#48](https://github.com/hdl/containers/issues/48) + + ## Contributors * [Patrick Lehmann](https://GitHub.com/Paebbels) * [Unai Martinez-Corral](https://GitHub.com/umarcor) (Maintainer) -* [and more...](https://GitHub.com/pyTooling/Actions/graphs/contributors) +* [and more...](https://GitHub.com/pyTooling/Actions/graphs/contributors) ## License -This Python package (source code) licensed under [Apache License 2.0](LICENSE.md). +This Python package (source code) licensed under [Apache License 2.0](LICENSE.md). The accompanying documentation is licensed under [Creative Commons - Attribution 4.0 (CC-BY 4.0)](doc/Doc-License.rst). +--- -------------------------- - -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 From 615aafc0b46d150f296db3f738791fedfff8f842 Mon Sep 17 00:00:00 2001 From: umarcor Date: Thu, 2 Dec 2021 16:36:20 +0100 Subject: [PATCH 02/18] releaser: add DEVELOPMENT.md --- releaser/DEVELOPMENT.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 releaser/DEVELOPMENT.md diff --git a/releaser/DEVELOPMENT.md b/releaser/DEVELOPMENT.md new file mode 100644 index 0000000..6d0c6d5 --- /dev/null +++ b/releaser/DEVELOPMENT.md @@ -0,0 +1,24 @@ +# Releaser Development + +- The connection issues explained in "Troubleshooting" might be related to some problem deep inside the Python libraries. + Some users tried [cli/cli](https://github.com/cli/cli), which is written in golang, as an alternative to **Releaser** + (see [msys2/msys2-installer#36](https://github.com/msys2/msys2-installer/pull/36)). + In fact, when the Python version of **Releaser** was written, `cli` was evaluated as an alternative to PyGitHub. + `gh release` was (and still is) not flexible enough to update the reference of a release, without deleting and + recreating it (see [cli.github.com: manual/gh_release_create](https://cli.github.com/manual/gh_release_create)). + Deletion and recreation is unfortunate, because it notifies all the watchers of a repository + (see [eine/tip#111](https://github.com/eine/tip/issues/111)). + Nevertheless, it might be desirable to evaluate using `gh release upload` (see [cli.github.com: manual/gh_release_upload](https://cli.github.com/manual/gh_release_upload)) in **Releaser**. + - Login through SSH is not supported by `cli` (see [cli/cli#3715](https://github.com/cli/cli/issues/3715)); however, + on GitHub Actions a token is available `${{ github.token }}` and `cli` is installed by default. + +- In order to avoid **Releaser** and the dependencies being installed at runtime, we should add a workflow to build a + container image and push it to the GitHub Container Registry (say `ghcr.io/pyTooling/Releaser`). + Then, update `action.yml` to use that image instead of the `Dockerfile`. + That would remove the performance penalty of having additional dependencies (such as + [pyTooling/pyAttributes](https://github.com/pyTooling/pyAttributes) or + [willmcgugan/rich](https://github.com/willmcgugan/rich)). + +- It might be desirable to have pyTooling.Version.SemVersion handle the regular expression from + [semver.org](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string), and use + proper Python classes in **Releaser**. From 250cceb80de03e393c0d8091365272f80ba2729c Mon Sep 17 00:00:00 2001 From: umarcor Date: Mon, 20 Dec 2021 23:49:42 +0100 Subject: [PATCH 03/18] releaser: update README.md and DEVELOPMENT.md --- releaser/DEVELOPMENT.md | 20 ++------------------ releaser/README.md | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/releaser/DEVELOPMENT.md b/releaser/DEVELOPMENT.md index 6d0c6d5..3fdc386 100644 --- a/releaser/DEVELOPMENT.md +++ b/releaser/DEVELOPMENT.md @@ -1,23 +1,7 @@ # Releaser Development -- The connection issues explained in "Troubleshooting" might be related to some problem deep inside the Python libraries. - Some users tried [cli/cli](https://github.com/cli/cli), which is written in golang, as an alternative to **Releaser** - (see [msys2/msys2-installer#36](https://github.com/msys2/msys2-installer/pull/36)). - In fact, when the Python version of **Releaser** was written, `cli` was evaluated as an alternative to PyGitHub. - `gh release` was (and still is) not flexible enough to update the reference of a release, without deleting and - recreating it (see [cli.github.com: manual/gh_release_create](https://cli.github.com/manual/gh_release_create)). - Deletion and recreation is unfortunate, because it notifies all the watchers of a repository - (see [eine/tip#111](https://github.com/eine/tip/issues/111)). - Nevertheless, it might be desirable to evaluate using `gh release upload` (see [cli.github.com: manual/gh_release_upload](https://cli.github.com/manual/gh_release_upload)) in **Releaser**. - - Login through SSH is not supported by `cli` (see [cli/cli#3715](https://github.com/cli/cli/issues/3715)); however, - on GitHub Actions a token is available `${{ github.token }}` and `cli` is installed by default. - -- In order to avoid **Releaser** and the dependencies being installed at runtime, we should add a workflow to build a - container image and push it to the GitHub Container Registry (say `ghcr.io/pyTooling/Releaser`). - Then, update `action.yml` to use that image instead of the `Dockerfile`. - That would remove the performance penalty of having additional dependencies (such as - [pyTooling/pyAttributes](https://github.com/pyTooling/pyAttributes) or - [willmcgugan/rich](https://github.com/willmcgugan/rich)). +- [pyTooling/pyAttributes](https://github.com/pyTooling/pyAttributes) or + [willmcgugan/rich](https://github.com/willmcgugan/rich) might be used to enhance the UX. - It might be desirable to have pyTooling.Version.SemVersion handle the regular expression from [semver.org](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string), and use diff --git a/releaser/README.md b/releaser/README.md index 4949b53..16cf0a2 100644 --- a/releaser/README.md +++ b/releaser/README.md @@ -48,6 +48,20 @@ as assets. In this context, one of the main use cases of **Releaser** is pushing artifacts as release assets. Thus, the name of the Action. +GitHub provides an official CLI tool, written in golang: [cli/cli](https://github.com/cli/cli). +When the Python version of **Releaser** was written, `cli` was evaluated as an alternative to *PyGitHub*. +`gh release` was (and still is) not flexible enough to update the reference of a release, without deleting and +recreating it (see [cli.github.com: manual/gh_release_create](https://cli.github.com/manual/gh_release_create)). +Deletion and recreation is unfortunate, because it notifies all the watchers of a repository +(see [eine/tip#111](https://github.com/eine/tip/issues/111)). +However, [cli.github.com: manual/gh_release_upload](https://cli.github.com/manual/gh_release_upload) handles uploading +artifacts as assets faster and with better stability for larger files than *PyGitHub* +(see [msys2/msys2-installer#36](https://github.com/msys2/msys2-installer/pull/36)). +Furthermore, the GitHub CLI is installed on GitHub Actions' default virtual environments. +Although `gh` does not support login through SSH (see [cli/cli#3715](https://github.com/cli/cli/issues/3715)), on GitHub +Actions a token is available `${{ github.token }}`. +Therefore, **Releaser** uses `gh release upload` internally. + ## Usage The following block shows a minimal YAML workflow file: From bb855d572d81158ebd9f0a14ba17f963dce17cc4 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 21 Dec 2021 19:00:11 +0100 Subject: [PATCH 04/18] Pytest using pyproject.toml. --- .github/workflows/CoverageCollection.yml | 21 ++++++++++++++++----- .github/workflows/UnitTesting.yml | 7 ++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index ff95257..7c0d61b 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -35,6 +35,16 @@ on: required: false default: '-r tests/requirements.txt' type: string + unittest_directory: + description: 'Path to the directory containing unit tests.' + required: false + default: 'tests/unit' + type: string + coverage_config: + description: 'Path to the .coveragerc file. Use pyproject.toml if empty.' + required: false + default: '' + type: string artifact: description: 'Name of the coverage artifact.' required: true @@ -67,7 +77,8 @@ jobs: - name: Collect coverage continue-on-error: true run: | - python -m pytest -rA --cov=.. --cov-config=tests/.coveragerc tests/unit --color=yes + [ 'x${{ inputs.coverage_config }}' != 'x' ] && PYCOV_ARGS='--cov-config=${{ inputs.coverage_config }}' || unset PYCOV_ARGS + python -m pytest -rA --cov=.. $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes - name: Convert to cobertura format run: coverage xml @@ -75,14 +86,14 @@ jobs: - name: Convert to HTML format run: | coverage html - rm htmlcov/.gitignore + rm report/coverage/html/.gitignore - name: 📤 Upload 'Coverage Report' artifact continue-on-error: true uses: actions/upload-artifact@v2 with: name: ${{ inputs.artifact }} - path: htmlcov + path: report/coverage/html if-no-files-found: error retention-days: 1 @@ -90,7 +101,7 @@ jobs: continue-on-error: true uses: codecov/codecov-action@v1 with: - file: ./coverage.xml + file: report/coverage/coverage.xml flags: unittests env_vars: PYTHON @@ -99,4 +110,4 @@ jobs: uses: codacy/codacy-coverage-reporter-action@master with: project-token: ${{ secrets.codacy_token }} - coverage-reports: ./coverage.xml + coverage-reports: report/coverage/coverage.xml diff --git a/.github/workflows/UnitTesting.yml b/.github/workflows/UnitTesting.yml index 4155898..ada82b4 100644 --- a/.github/workflows/UnitTesting.yml +++ b/.github/workflows/UnitTesting.yml @@ -34,6 +34,11 @@ on: required: false default: '-r tests/requirements.txt' type: string + unittest_directory: + description: 'Path to the directory containing unit tests.' + required: false + default: 'tests/unit' + type: string artifact: description: "Generate unit test report with junitxml and upload results as an artifact." required: false @@ -68,7 +73,7 @@ jobs: - name: ☑ Run unit tests run: | [ 'x${{ inputs.artifact }}' != 'x' ] && PYTEST_ARGS='--junitxml=TestReport.xml' || unset PYTEST_ARGS - python -m pytest -rA tests/unit $PYTEST_ARGS --color=yes + python -m pytest -rA ${{ inputs.unittest_directory }} $PYTEST_ARGS --color=yes - name: 📤 Upload 'TestReport.xml' artifact if: inputs.artifact != '' From 6ad23eabf5585aac3f30aabfa964a643e20ad754 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 21 Dec 2021 22:48:44 +0100 Subject: [PATCH 05/18] Extract information from TOML file. --- .github/workflows/CoverageCollection.yml | 28 ++++++++++++++++++++---- .github/workflows/Parameters.yml | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 7c0d61b..8d0a0e8 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -64,6 +64,26 @@ jobs: - name: ⏬ Checkout repository uses: actions/checkout@v2 + - name: 🔁 Extract configurations from pyproject.toml + id: getVariables + run: | + function ReadToml() { + state=0; + RegExp="$2 = \"(.*)\"" + while IFS=$'\r\n' read -r Line; do + if [[ $state -eq 0 && "$Line" == "[$1]" ]]; then + state=1; + elif [[ $state -eq 1 && "$Line" =~ $RegExp ]]; then + echo "${BASH_REMATCH[1]}"; + break; + fi + done < <(cat "pyproject.toml") + } + + # write to step outputs + echo ::set-output name=coverage_report_html_directory::$(ReadToml "tool.coverage.html" "directory") + echo ::set-output name=coverage_report_xml::$(ReadToml "tool.coverage.xml" "output") + - name: 🐍 Setup Python ${{ inputs.python_version }} uses: actions/setup-python@v2 with: @@ -86,14 +106,14 @@ jobs: - name: Convert to HTML format run: | coverage html - rm report/coverage/html/.gitignore + rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore - name: 📤 Upload 'Coverage Report' artifact continue-on-error: true uses: actions/upload-artifact@v2 with: name: ${{ inputs.artifact }} - path: report/coverage/html + path: ${{ steps.getVariables.outputs.coverage_report_html_directory }} if-no-files-found: error retention-days: 1 @@ -101,7 +121,7 @@ jobs: continue-on-error: true uses: codecov/codecov-action@v1 with: - file: report/coverage/coverage.xml + file: ${{ steps.getVariables.outputs.coverage_report_xml }} flags: unittests env_vars: PYTHON @@ -110,4 +130,4 @@ jobs: uses: codacy/codacy-coverage-reporter-action@master with: project-token: ${{ secrets.codacy_token }} - coverage-reports: report/coverage/coverage.xml + coverage-reports: ${{ steps.getVariables.outputs.coverage_report_xml }} diff --git a/.github/workflows/Parameters.yml b/.github/workflows/Parameters.yml index f7b217d..6892cd9 100644 --- a/.github/workflows/Parameters.yml +++ b/.github/workflows/Parameters.yml @@ -76,7 +76,7 @@ jobs: print(params) data = { - '3.6': { 'icon': '🔴', 'until': '23.12.2021' }, + '3.6': { 'icon': '🔴', 'until': '23.12.2021' }, # Black circle ⚫ for EOL versions. '3.7': { 'icon': '🟠', 'until': '27.06.2023' }, '3.8': { 'icon': '🟡', 'until': 'Oct. 2024' }, '3.9': { 'icon': '🟢', 'until': 'Oct. 2025' }, From 09f7504de4d56770cbe6772eb7bbd8872a984448 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 21 Dec 2021 23:17:33 +0100 Subject: [PATCH 06/18] Fixed path to project root. --- .github/workflows/CoverageCollection.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 8d0a0e8..09ee31a 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -98,7 +98,7 @@ jobs: continue-on-error: true run: | [ 'x${{ inputs.coverage_config }}' != 'x' ] && PYCOV_ARGS='--cov-config=${{ inputs.coverage_config }}' || unset PYCOV_ARGS - python -m pytest -rA --cov=.. $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes + python -m pytest -rA --cov=. $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes - name: Convert to cobertura format run: coverage xml From 9dfafd588e906a04729797d4f4ff464ed188b6d0 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Wed, 22 Dec 2021 14:29:09 +0100 Subject: [PATCH 07/18] Changed scripting from bash to Python. Also use .coveragerc as fallback. --- .github/workflows/CoverageCollection.yml | 47 ++++++++++++++++-------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 09ee31a..6cdf29a 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -66,23 +66,40 @@ jobs: - name: 🔁 Extract configurations from pyproject.toml id: getVariables + shell: python run: | - function ReadToml() { - state=0; - RegExp="$2 = \"(.*)\"" - while IFS=$'\r\n' read -r Line; do - if [[ $state -eq 0 && "$Line" == "[$1]" ]]; then - state=1; - elif [[ $state -eq 1 && "$Line" =~ $RegExp ]]; then - echo "${BASH_REMATCH[1]}"; - break; - fi - done < <(cat "pyproject.toml") - } + from pathlib import Path + from tomli import load as tomli_load - # write to step outputs - echo ::set-output name=coverage_report_html_directory::$(ReadToml "tool.coverage.html" "directory") - echo ::set-output name=coverage_report_xml::$(ReadToml "tool.coverage.xml" "output") + coverageRC = "${{ inputs.coverage_config }}".strip() + + # Read output paths from 'pyproject.toml' file + if coverageRC == "": + pyProjectFile = Path("pyproject.toml") + if pyProjectFile.exists(): + with pyProjectFile.open("rb") as file: + pyProjectSettings = tomli_load(file) + + htmlDirectory = pyProjectSettings["tool.coverage.html"]["directory"] + xmlFile = pyProjectSettings["tool.coverage.xml"]["output"] + else: + print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.") + + # Read output paths from '.coveragerc' file + else: + coverageRCFile = Path(coverageRC) + if coverageRCFile.exists(): + with coverageRCFile.open("rb") as file: + coverageRCSettings = tomli_load(file) + + htmlDirectory = pyProjectSettings["html"]["directory"] + xmlFile = pyProjectSettings["xml"]["output"] + else: + print(f"File '{coverageRCFile}' not found.") + + print(f"::set-output name=coverage_report_html_directory::{htmlDirectory}") + print(f"::set-output name=coverage_report_xml::{xmlFile}") + print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}") - name: 🐍 Setup Python ${{ inputs.python_version }} uses: actions/setup-python@v2 From fa10ed076ca7cb2985b827a78fc23e0a95678170 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Fri, 24 Dec 2021 12:51:38 +0100 Subject: [PATCH 08/18] Install dependency `tomli` before script execution. --- .github/workflows/CoverageCollection.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 6cdf29a..71dabf5 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -64,6 +64,17 @@ jobs: - name: ⏬ Checkout repository uses: actions/checkout@v2 + - name: 🐍 Setup Python ${{ inputs.python_version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ inputs.python_version }} + + - name: 🗂 Install dependencies + run: | + python -m pip install -U pip + python -m pip install tomli + python -m pip install ${{ inputs.requirements }} + - name: 🔁 Extract configurations from pyproject.toml id: getVariables shell: python @@ -101,16 +112,6 @@ jobs: print(f"::set-output name=coverage_report_xml::{xmlFile}") print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}") - - name: 🐍 Setup Python ${{ inputs.python_version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ inputs.python_version }} - - - name: 🗂 Install dependencies - run: | - python -m pip install -U pip - python -m pip install ${{ inputs.requirements }} - - name: Collect coverage continue-on-error: true run: | From d7c765ba7919d0fa9f411b3f616bcb4bbee4a09a Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Fri, 24 Dec 2021 13:04:31 +0100 Subject: [PATCH 09/18] Fixed how to access complex nested key-value pairs. --- .github/workflows/CoverageCollection.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 71dabf5..fa72e8c 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -91,8 +91,8 @@ jobs: with pyProjectFile.open("rb") as file: pyProjectSettings = tomli_load(file) - htmlDirectory = pyProjectSettings["tool.coverage.html"]["directory"] - xmlFile = pyProjectSettings["tool.coverage.xml"]["output"] + htmlDirectory = pyProjectSettings["tool"]["coverage"]["html"]["directory"] + xmlFile = pyProjectSettings["tool"]["coverage"]["xml"]["output"] else: print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.") From dcd0a4b6176ca2ec0fa9a0b813ce31714db5210b Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 13:52:30 +0100 Subject: [PATCH 10/18] Parameters: mark Python 3.6 black, update others, warn about unsupported versions --- .github/workflows/Parameters.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Parameters.yml b/.github/workflows/Parameters.yml index f7b217d..085dc13 100644 --- a/.github/workflows/Parameters.yml +++ b/.github/workflows/Parameters.yml @@ -75,16 +75,19 @@ jobs: print("Parameters:") print(params) + versions = '${{ inputs.python_version_list }}'.split(' ') + if '3.6' in versions: + print('WARNING: support for Python 3.6 ended in 2021.12.23') data = { - '3.6': { 'icon': '🔴', 'until': '23.12.2021' }, - '3.7': { 'icon': '🟠', 'until': '27.06.2023' }, - '3.8': { 'icon': '🟡', 'until': 'Oct. 2024' }, - '3.9': { 'icon': '🟢', 'until': 'Oct. 2025' }, - '3.10': { 'icon': '🟢', 'until': 'Oct. 2026' }, + '3.6': { 'icon': '⚫', 'until': '2021.12.23' }, + '3.7': { 'icon': '🔴', 'until': '2023.06.27' }, + '3.8': { 'icon': '🟠', 'until': '2024.10' }, + '3.9': { 'icon': '🟡', 'until': '2025.10' }, + '3.10': { 'icon': '🟢', 'until': '2026.10' }, } jobs = [ {'python': version, 'icon': data[version]['icon']} - for version in '${{ inputs.python_version_list }}'.split(' ') + for version in versions ] print(f'::set-output name=python_jobs::{jobs!s}') print("Python jobs:") From 66c7b4b619465aea9f3a6337c3db2244ab608fd6 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 14:11:37 +0100 Subject: [PATCH 11/18] ExamplePipeline/ArtifactCleanup: needs PublishTestResults instead of UnitTesting --- ExamplePipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExamplePipeline.yml b/ExamplePipeline.yml index 9be2d54..8e4af52 100644 --- a/ExamplePipeline.yml +++ b/ExamplePipeline.yml @@ -148,7 +148,7 @@ jobs: uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main needs: - Params - - UnitTesting + - PublishTestResults - Coverage - StaticTypeCheck - BuildTheDocs From 925b44a8a8f8ed07cb04304da1bce5513173ff75 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 15:54:09 +0100 Subject: [PATCH 12/18] CoverageCollection: fix variable name --- .github/workflows/CoverageCollection.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index fa72e8c..37be0b2 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -81,7 +81,7 @@ jobs: run: | from pathlib import Path from tomli import load as tomli_load - + coverageRC = "${{ inputs.coverage_config }}".strip() # Read output paths from 'pyproject.toml' file @@ -90,24 +90,24 @@ jobs: if pyProjectFile.exists(): with pyProjectFile.open("rb") as file: pyProjectSettings = tomli_load(file) - + htmlDirectory = pyProjectSettings["tool"]["coverage"]["html"]["directory"] xmlFile = pyProjectSettings["tool"]["coverage"]["xml"]["output"] else: print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.") - + # Read output paths from '.coveragerc' file else: coverageRCFile = Path(coverageRC) if coverageRCFile.exists(): with coverageRCFile.open("rb") as file: coverageRCSettings = tomli_load(file) - - htmlDirectory = pyProjectSettings["html"]["directory"] - xmlFile = pyProjectSettings["xml"]["output"] + + htmlDirectory = coverageRCSettings["html"]["directory"] + xmlFile = coverageRCSettings["xml"]["output"] else: print(f"File '{coverageRCFile}' not found.") - + print(f"::set-output name=coverage_report_html_directory::{htmlDirectory}") print(f"::set-output name=coverage_report_xml::{xmlFile}") print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}") From 9bd8004dfb5112e61616e119de5cd52374c2dd86 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 15:59:23 +0100 Subject: [PATCH 13/18] CoverageCollection: pass output directory to coverage html --- .github/workflows/CoverageCollection.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 37be0b2..9946179 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -123,7 +123,7 @@ jobs: - name: Convert to HTML format run: | - coverage html + coverage html -d ${{ steps.getVariables.outputs.coverage_report_html_directory }} rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore - name: 📤 Upload 'Coverage Report' artifact From 62cd2d1d0f499b1ba5a11ba2d4f9ed5db033f8b3 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 16:22:11 +0100 Subject: [PATCH 14/18] CoverageCollection: htmlDirectory defaults to 'htmlcov' --- .github/workflows/CoverageCollection.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 9946179..3dacfe4 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -82,6 +82,7 @@ jobs: from pathlib import Path from tomli import load as tomli_load + htmlDirectory = 'htmlcov' coverageRC = "${{ inputs.coverage_config }}".strip() # Read output paths from 'pyproject.toml' file From b8564eb389e4c86a8c0cbb11bd240dde202b94e6 Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 16:24:55 +0100 Subject: [PATCH 15/18] CoverageCollection: xmlFile defaults to './coverage.xml' --- .github/workflows/CoverageCollection.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index 3dacfe4..d42621d 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -83,6 +83,7 @@ jobs: from tomli import load as tomli_load htmlDirectory = 'htmlcov' + xmlFile = './coverage.xml' coverageRC = "${{ inputs.coverage_config }}".strip() # Read output paths from 'pyproject.toml' file From 9846c9e60c4f8d3b1edf29eaebbb2e220ee0af5c Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 16:31:47 +0100 Subject: [PATCH 16/18] CoverageCollection: use 'pyproject.toml' by default --- .github/workflows/CoverageCollection.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index d42621d..cfccc79 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -41,9 +41,9 @@ on: default: 'tests/unit' type: string coverage_config: - description: 'Path to the .coveragerc file. Use pyproject.toml if empty.' + description: 'Path to the .coveragerc file. Use pyproject.toml by default.' required: false - default: '' + default: 'pyproject.toml' type: string artifact: description: 'Name of the coverage artifact.' @@ -87,7 +87,7 @@ jobs: coverageRC = "${{ inputs.coverage_config }}".strip() # Read output paths from 'pyproject.toml' file - if coverageRC == "": + if coverageRC == "pyproject.toml": pyProjectFile = Path("pyproject.toml") if pyProjectFile.exists(): with pyProjectFile.open("rb") as file: From 1fbeef36d6b993772691898bfa10f29bc6ea85ac Mon Sep 17 00:00:00 2001 From: umarcor Date: Fri, 24 Dec 2021 16:34:56 +0100 Subject: [PATCH 17/18] CoverageCollection: skip config file if empty --- .github/workflows/CoverageCollection.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CoverageCollection.yml b/.github/workflows/CoverageCollection.yml index cfccc79..f5bde28 100644 --- a/.github/workflows/CoverageCollection.yml +++ b/.github/workflows/CoverageCollection.yml @@ -99,7 +99,7 @@ jobs: print(f"File '{pyProjectFile}' not found and no ' .coveragerc' file specified.") # Read output paths from '.coveragerc' file - else: + elif len(coverageRC) > 0: coverageRCFile = Path(coverageRC) if coverageRCFile.exists(): with coverageRCFile.open("rb") as file: From 78b225195f34f56c482dc9dac8407570af6e1beb Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Fri, 24 Dec 2021 21:05:04 +0100 Subject: [PATCH 18/18] Updated README according to latest changes. --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 120ed78..495acfc 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,16 @@ As shown in the screenshot above, the expected order is: - Global: - [Parameters](.github/workflows/Parameters.yml): a workaround for the limitations to handle global variables in GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)). - It generates outputs with artifact names and job matrices to be used in other jobs. + It generates outputs with artifact names and job matrices to be used in later running jobs. - Code testing/analysis: - [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and - optionally upload results as XML reports. - - [CoverageCollection](.github/workflows/CoverageCollection.yml): collect coverage data with `pytest` using a single - version of Python, generate HTML and Cobertura (XML) reports, upload the HTML report as an artifact, and upload the - results to Codecov and Codacy. + optionally upload results as XML reports. Configuration options to `pytest` should be given via section + `[tool.pytest.ini_options]` in a `pyproject.toml` file. + - [CoverageCollection](.github/workflows/CoverageCollection.yml): collect code coverage data (incl. branch coverage) + with `pytest`/`pytest-cov`/`coverage.py` using a single version of Python (latest). It generates HTML and Cobertura + (XML)reports, upload the HTML report as an artifact, and upload the test results to Codecov and Codacy. Configuration + options to `pytest` and `coverage.py` should be given via section `[tool.pytest.ini_options]` and `[tool.coverage.*]` + in a `pyproject.toml` file. - [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and optionally upload results as an HTML report. Example `commands`: @@ -128,7 +131,7 @@ As shown in the screenshot above, the expected order is: mypy --html-report ../htmlmypy -p ToolName ``` - - [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test. + - [VerifyDocs](.github/workflows/VerifyDocs.yml): extract code examples from the README and test these code snippets. - Packaging and releasing: - [Release](.github/workflows/Release.yml): publish GitHub Release. - [Package](.github/workflows/Package.yml): generate source and wheel packages, and upload them as an artifact.