Compare commits

...

133 Commits
v2.1.0 ... r5

Author SHA1 Message Date
Patrick Lehmann
6fb3389c4f Updating r5 from main@v5.4.0 2025-08-30 12:36:17 +02:00
Patrick Lehmann
d6342484cd v5.4.0 2025-08-29 22:14:18 +02:00
Patrick Lehmann
ba3d82668e Allow configuration of JUnit dialects. 2025-08-29 21:57:36 +02:00
Patrick Lehmann
b1eff9ce92 Updating r5 from main@v5.2.0 and main@v5.3.0 2025-08-29 08:19:12 +02:00
Patrick Lehmann
4b0b103c5f v5.3.0 2025-08-29 07:49:24 +02:00
Patrick Lehmann
ba6f55dd57 Support cross-repository pull request runs from forks. 2025-08-28 22:36:35 +02:00
Patrick Lehmann
d48de1d02d v5.2.0 2025-08-17 13:21:18 +02:00
Patrick Lehmann
744f08b9bf Tag release only if not a scheduled CI run. 2025-08-17 10:44:31 +02:00
Patrick Lehmann
fb546453ae Bumped dependencies. 2025-08-13 01:09:26 +02:00
Patrick Lehmann
1a3ba03626 Check pytest outcome. 2025-07-27 22:32:42 +02:00
Patrick Lehmann
4d2d4c47fc Upload merged unit tests as artifact even if some test cases failed. 2025-07-16 08:26:01 +02:00
Patrick Lehmann
962827936c Build Sphinx documentation, even if some testcases have failed. 2025-07-15 22:35:13 +02:00
Patrick Lehmann
13530435df Updating r5 from main@v5.1.0 2025-06-20 02:26:47 +02:00
Patrick Lehmann
2307b526df v5.1.0 2025-06-20 02:09:09 +02:00
Patrick Lehmann
f3e2e42ae3 Handle replacement placeholders, if no substitution exists. 2025-06-20 02:02:46 +02:00
Patrick Lehmann
2f7fa88c36 Compute and verify SHA256 checksums. 2025-06-19 17:44:31 +02:00
Patrick Lehmann
f0d8a24973 Updating r5 from main@v5.0.0. 2025-06-17 09:03:56 +02:00
Patrick Lehmann
2ee14e88a2 v5.0.0 2025-06-16 15:14:28 +02:00
Patrick Lehmann
68f708d79c Bumped dependencies. 2025-06-16 12:16:45 +02:00
Patrick Lehmann
034d27d4a3 Added pyaml and MarkupSafe to list of preinstalled pacman packages. 2025-05-28 09:56:51 +02:00
Patrick Lehmann
5891636ec9 Install Python package and readout embedded package version. 2025-05-06 00:52:38 +02:00
Patrick Lehmann
330a21e6c3 Apply Dependabot reviewers vs. GitHub CODEOWNERS change requested by Dependabot. 2025-05-05 18:53:05 +02:00
Patrick Lehmann
bdf8239a73 Don't use "secrets: inherit"! 2025-05-04 11:21:05 +02:00
Patrick Lehmann
68357fddd8 Converted boolean parameters to string. 2025-05-03 18:31:39 +02:00
Patrick Lehmann
4addce16fa Fixed here-docs. 2025-05-01 22:44:01 +02:00
Patrick Lehmann
2f4ccf2af4 Generate error messages if codecov or codacy fail. 2025-05-01 12:59:16 +02:00
Patrick Lehmann
0b6462f4d2 Fixed secret name. 2025-04-27 09:12:58 +02:00
Patrick Lehmann
b0198fa459 Run intermediate cleanup parallel to documentation, because only pre-merge artifacts (unittest and coverage) are cleaned. 2025-04-26 21:37:19 +02:00
Patrick Lehmann
05808d4058 Do not create application test parameters, if application testing isn't integrated yet. 2025-04-26 21:33:46 +02:00
Patrick Lehmann
7a57747fcd Removed release description text, so PR text is used. 2025-04-26 21:29:50 +02:00
Patrick Lehmann
3f7b0c221b Use == 'true'. 2025-04-26 20:07:08 +02:00
Patrick Lehmann
4ebf262921 Fix documentation coverage search directory 2025-04-26 19:13:37 +02:00
Patrick Lehmann
f015e1c3a0 Use is_release_tag. 2025-04-26 18:37:44 +02:00
Patrick Lehmann
9e6138b5a9 Provide is_nightly_tag and is_release_tag. 2025-04-26 18:36:57 +02:00
Patrick Lehmann
39ae93c200 Use secrets: inherit. 2025-04-26 18:35:58 +02:00
Patrick Lehmann
1b6454ba91 Allow no assets. 2025-04-26 16:43:35 +02:00
Patrick Lehmann
12389ac0f0 Test new job templates. 2025-04-26 15:00:18 +02:00
Patrick Lehmann
c1f0e4a16b Reworked Release job template. 2025-04-26 10:38:30 +02:00
Patrick Lehmann
8d0c46d6b5 Added new Prepare job template. 2025-04-26 00:20:10 +02:00
Patrick Lehmann
70f5fe1fc8 Added default values to secrets in case a service demanding a secret isn't used. 2025-04-26 00:19:49 +02:00
Patrick Lehmann
a1309f9f42 Improved printf usage. 2025-04-26 00:18:51 +02:00
Patrick Lehmann
8e94b774da Bumped copyright year. 2025-04-26 00:17:59 +02:00
Patrick Lehmann
aaf283515b Fixed typos. 2025-04-21 13:09:12 +02:00
Patrick Lehmann
f10daa2e2c Allow setting a pipeline startup delay. 2025-04-20 19:22:08 +02:00
Patrick Lehmann
27d45b9766 Bumped version of pyDummy. 2025-04-18 13:06:36 +02:00
Patrick Lehmann
a1509493ae Updating r4 from main@v4.3.0 2025-04-18 12:47:20 +02:00
Patrick Lehmann
33edd82e6f v4.3.0 2025-04-18 10:39:54 +02:00
Patrick Lehmann
d08f312904 Allow control where to publish reports (Codecov, Codacy, Dorny Test-Reporter). 2025-04-18 10:27:33 +02:00
Patrick Lehmann
731d0d2369 Provide secrets for testing. 2025-04-18 01:02:45 +02:00
Patrick Lehmann
de400ae2db Fixed publishing code coverage results. 2025-04-18 00:35:25 +02:00
Patrick Lehmann
cb3c338df6 [Dependabot]: Bump dorny/test-reporter from v1 to v2. 2025-04-17 01:35:48 +02:00
dependabot[bot]
79c8526437 [Dependabot]: Bump dorny/test-reporter from 1 to 2
Bumps [dorny/test-reporter](https://github.com/dorny/test-reporter) from 1 to 2.
- [Release notes](https://github.com/dorny/test-reporter/releases)
- [Changelog](https://github.com/dorny/test-reporter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dorny/test-reporter/compare/v1...v2)

---
updated-dependencies:
- dependency-name: dorny/test-reporter
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 23:02:45 +00:00
Patrick Lehmann
0f7d062c38 Replaced getVariables by ExtractConfiguration and package coverage XML/JSON correctly. 2025-04-16 14:55:57 +02:00
Patrick Lehmann
d5635a8842 Allow disabling cleanups. 2025-04-16 13:35:11 +02:00
Patrick Lehmann
6aa6af95ee Activated more investigations after artifact-download. 2025-04-16 13:34:37 +02:00
Patrick Lehmann
15bf375fe6 Bumped versions. Updated python version to 3.13. 2025-04-16 13:33:45 +02:00
Patrick Lehmann
a1b03cfe2a Extract pyedaa-reports settings for merged unittesting results. 2025-03-24 08:41:16 +01:00
Patrick Lehmann
79620e267d Collect results even when tests (partially) failed. 2025-03-19 19:01:14 +01:00
Patrick Lehmann
fffef5c814 Used merged unittest XML in Sphinx documentation. 2025-03-19 07:53:06 +01:00
Patrick Lehmann
1c22a8805e Updating r4 from main@v4.2.2 2025-03-07 21:15:35 +01:00
Patrick Lehmann
42e17fae05 v4.2.2 2025-03-07 21:12:53 +01:00
Patrick Lehmann
9b7032a585 Use doc_directory instead of hard-coded paths for workarounds. 2025-03-07 21:02:00 +01:00
Patrick Lehmann
9110c85738 Do not require 'coverage_report_json_directory' in SphinxDocumentation.yml. 2025-03-07 21:01:29 +01:00
Patrick Lehmann
c55ff4d83f Updating r4 from main@v4.2.1 2025-03-02 15:58:26 +01:00
Patrick Lehmann
c81d139080 v4.2.1 2025-03-02 15:55:14 +01:00
Patrick Lehmann
c64e054bcd Allow comments in the list of assets. 2025-03-02 11:30:32 +01:00
Patrick Lehmann
a2f2a6c0d4 Updating r4 from main@v4.2.0 2025-02-28 23:08:47 +01:00
Patrick Lehmann
78fdb584aa v4.2.0 2025-02-28 22:59:27 +01:00
Patrick Lehmann
a456635686 Increment on errors. 2025-02-28 22:43:49 +01:00
Patrick Lehmann
befc59f22d Fixed check for previous errors. 2025-02-28 22:40:25 +01:00
Patrick Lehmann
d6fc0efd47 Improved indentation for error messages. 2025-02-28 22:39:47 +01:00
Patrick Lehmann
c018acc3c1 Bumped dependencies and fixed Bash coding style. 2025-02-26 18:19:37 +01:00
Patrick Lehmann
d74c610bb4 Fixed Bash syntax. 2025-02-21 00:10:27 +01:00
Patrick Lehmann
edc4ab3e86 Bumped copyright year to 2025. 2025-02-21 00:10:10 +01:00
Patrick Lehmann
0a338ae8b7 Fixed badge URLS. 2025-01-25 21:59:55 +01:00
Patrick Lehmann
4069da0a74 Bumped dependencies. 2025-01-25 21:59:34 +01:00
Patrick Lehmann
9f4321b7e7 v4.1.0 2025-01-16 21:51:02 +01:00
Patrick Lehmann
679ec24c80 v4.1.0 2025-01-16 21:50:03 +01:00
Patrick Lehmann
3a13486ea6 Add only filename to the inventory. 2025-01-16 19:04:47 +01:00
Patrick Lehmann
34fb9c9869 Coding style fixes. 2025-01-15 23:25:32 +01:00
Patrick Lehmann
7523c4adca Search only for xml files. 2025-01-15 16:09:19 +01:00
Patrick Lehmann
530ad7a4a1 Added timezone. 2025-01-15 16:09:04 +01:00
Patrick Lehmann
bd3f2afaf3 Group output from zip. 2025-01-15 13:08:08 +01:00
Patrick Lehmann
0db52d7abc v4.0.1 2025-01-14 21:19:59 +01:00
Patrick Lehmann
b1e4cb961f v4.0.1 2025-01-14 21:16:37 +01:00
Patrick Lehmann
1e6b71e87b Fixed delimiter sign for category splitting at ','. 2025-01-14 21:07:41 +01:00
Patrick Lehmann
5128522ede v4.0.0 2025-01-14 17:39:43 +01:00
Patrick Lehmann
5d8a608893 v4.0.0 2025-01-14 17:03:38 +01:00
Patrick Lehmann
c3b7b3ca64 Take inventory categories from input parameter. 2025-01-14 16:58:21 +01:00
Patrick Lehmann
f61b77ee72 Adding support for a JSON inventory. 2025-01-14 12:17:50 +01:00
Patrick Lehmann
5f18024dd4 Updated pipeline image. 2024-12-27 00:00:02 +01:00
Patrick Lehmann
d0f07e1af4 Fixed printf. 2024-12-26 21:56:04 +01:00
Patrick Lehmann
2b5a9bdeff Updated README. 2024-12-26 19:00:45 +01:00
Patrick Lehmann
d324bdacee Improved handling of true values from GitHub YAML. 2024-12-26 18:12:47 +01:00
Patrick Lehmann
9296bd6e7d Workaround 1 - missing file extensions. 2024-12-24 18:19:14 +01:00
Patrick Lehmann
c37f727e2d Workaround 2 - % sign in a filename. 2024-12-24 13:09:02 +01:00
Patrick Lehmann
a4559e8e63 Build documentation using pyTooling/MikTeX. 2024-12-21 23:51:14 +01:00
Patrick Lehmann
bc94fba95e Fixed broken pipeline dependency. 2024-12-19 00:31:17 +01:00
Patrick Lehmann
d6ec94fea7 Handover coverage_report_html_directory. 2024-12-19 00:23:40 +01:00
Patrick Lehmann
4bc4ec5cf4 Generate merged unit test report. 2024-12-19 00:03:06 +01:00
Patrick Lehmann
e21f89670a Improved usage of tree command. 2024-12-18 23:45:28 +01:00
Patrick Lehmann
c13dd2521c Install interrogate with png extras to avoid error. 2024-12-18 22:50:25 +01:00
Patrick Lehmann
cf2e89a622 Updated Python version for MSYS2 to 3.12. 2024-12-18 22:50:02 +01:00
Patrick Lehmann
a9a3c400fd Create unittest XML and coverage sqlite artifacts in job template test. 2024-12-18 22:49:28 +01:00
Patrick Lehmann
970b4ae021 Reoved commented out Coverage job. 2024-12-18 22:48:45 +01:00
Patrick Lehmann
1807741b0a Fixed TOML key. 2024-12-18 22:29:00 +01:00
Patrick Lehmann
cf095afe77 Fixed usage of true. 2024-12-18 22:24:10 +01:00
Patrick Lehmann
12991ee38a Implemented 'unittest_report_xml_directory' and 'unittest_report_xml_filename'. 2024-12-18 22:23:43 +01:00
Patrick Lehmann
70134b31c3 Exchanged 'echo' with 'printf'.
co-authored-by: Sven Köhler <sven.koehler@gmail.com>
2024-12-16 23:55:20 +01:00
Patrick Lehmann
38514a9005 Create tar files with userid=0 and groupid=0. 2024-12-11 01:15:01 +01:00
Patrick Lehmann
899a5f53bc v3.1.1 2024-12-08 23:46:42 +01:00
Patrick Lehmann
9ceefdbf5d v3.1.1 2024-12-08 23:44:56 +01:00
Patrick Lehmann
b1bc6e50a3 Remove temporary tarball. 2024-12-08 23:41:26 +01:00
Patrick Lehmann
5925101578 v3.1.0 2024-12-08 21:30:48 +01:00
Patrick Lehmann
fdee9e011f v3.1.0 2024-12-08 21:27:59 +01:00
Patrick Lehmann
3e50c2ed5b Support embedded tarballs for nightly release's artifacts. 2024-12-08 21:23:55 +01:00
Patrick Lehmann
1b0acf206c v3.0.0 2024-12-08 11:03:28 +01:00
Patrick Lehmann
9e0b1c69f1 v3.0.0 2024-12-08 09:43:32 +01:00
Patrick Lehmann
f084e02f01 Use pattern to reduce the number of downloaded artifacts. 2024-12-07 23:41:16 +01:00
Patrick Lehmann
c34d4e240e [Dependabot]: Bump codecov/codecov-action from 3 to 5 2024-12-07 15:03:03 +01:00
Patrick Lehmann
6d04009bd6 Use new pyTooling/upload-artifact pyTooling/download-artifact composite actions. 2024-12-07 10:20:00 +01:00
Patrick Lehmann
50d32d1950 Replace also in description text. 2024-12-02 08:27:34 +01:00
Patrick Lehmann
7733e8998f Supporting replacements in NightlyReleases. 2024-12-01 22:06:18 +01:00
Patrick Lehmann
4c28b9d003 Added support for ZStandard. [skip ci] 2024-11-29 01:51:56 +01:00
Patrick Lehmann
bafea7d082 Trim leading whitespace. 2024-11-27 19:45:24 +01:00
Patrick Lehmann
9ca7b04f37 Added tar/gz compression as tgz files. 2024-11-27 19:34:44 +01:00
Patrick Lehmann
7a0ee75fd5 Added nightly release job template. 2024-11-27 08:03:57 +01:00
Patrick Lehmann
bc876f7171 Allow disabling the publishing of test reports via Dorny Test Reporter. 2024-11-22 22:23:18 +01:00
Patrick Lehmann
edca070047 Also checkout submodules. 2024-11-17 01:59:51 +01:00
dependabot[bot]
21c2f48dad [Dependabot]: Bump codecov/codecov-action from 3 to 5
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-15 03:35:36 +00:00
Patrick Lehmann
a0c016bf79 v2.1.1 2024-11-10 21:17:21 +01:00
Patrick Lehmann
9338fbd106 v2.0.1 2024-11-10 21:15:14 +01:00
Patrick Lehmann
6869d0f666 Forward secrets to pipeline template. 2024-11-10 21:11:14 +01:00
74 changed files with 3178 additions and 879 deletions

3
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,3 @@
* @Paebbels
/.github/ @Paebbels

View File

@@ -10,9 +10,5 @@ updates:
- Dependencies - Dependencies
assignees: assignees:
- Paebbels - Paebbels
- umarcor
reviewers:
- Paebbels
- umarcor
schedule: schedule:
interval: "daily" # Checks on Monday trough Friday. interval: "daily" # Checks on Monday trough Friday.

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -86,10 +86,10 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 📥 Download artifacts '${{ inputs.wheel }}' from 'Package' job - name: 📥 Download artifacts '${{ inputs.wheel }}' from 'Package' job
uses: actions/download-artifact@v4 uses: pyTooling/download-artifact@v5
with: with:
name: ${{ inputs.wheel }} name: ${{ inputs.wheel }}
path: install path: install
@@ -134,20 +134,21 @@ jobs:
packages = { packages = {
"coverage": "python-coverage:p", "coverage": "python-coverage:p",
"docstr_coverage": "python-pyyaml:p", "docstr_coverage": "python-pyyaml:p python-types-pyyaml:p",
"igraph": "igraph:p", "igraph": "igraph:p",
"jinja2": "python-markupsafe:p", "jinja2": "python-markupsafe:p",
"lxml": "python-lxml:p", "lxml": "python-lxml:p",
"numpy": "python-numpy:p", "numpy": "python-numpy:p",
"markupsafe": "python-markupsafe:p", "markupsafe": "python-markupsafe:p",
"pip": "python-pip:p", "pip": "python-pip:p",
"pyyaml": "python-pyyaml:p", "pyyaml": "python-pyyaml:p python-types-pyyaml:p",
"ruamel.yaml": "python-ruamel-yaml:p python-ruamel.yaml.clib:p", "ruamel.yaml": "python-ruamel-yaml:p python-ruamel.yaml.clib:p",
"sphinx": "python-markupsafe:p", "sphinx": "python-markupsafe:p",
"tomli": "python-tomli:p", "tomli": "python-tomli:p",
"wheel": "python-wheel:p", "wheel": "python-wheel:p",
"pyEDAA.ProjectModel": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p", "pyEDAA.ProjectModel": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p",
"pyEDAA.Reports": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p", "pyEDAA.Reports": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p",
"sphinx-reports": "python-markupsafe:p python-pyaml:p python-types-pyyaml:p",
} }
subPackages = { subPackages = {
"pytooling": { "pytooling": {
@@ -188,8 +189,8 @@ jobs:
f.write(f"pacboy_packages={' '.join(pacboyPackages)}\n") f.write(f"pacboy_packages={' '.join(pacboyPackages)}\n")
- name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}' - name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}'
if: matrix.system == 'msys2'
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
if: matrix.system == 'msys2'
with: with:
msystem: ${{ matrix.runtime }} msystem: ${{ matrix.runtime }}
update: true update: true
@@ -198,8 +199,8 @@ jobs:
${{ inputs.pacboy }} ${{ inputs.pacboy }}
- name: 🐍 Setup Python ${{ matrix.python }} - name: 🐍 Setup Python ${{ matrix.python }}
if: matrix.system != 'msys2'
uses: actions/setup-python@v5 uses: actions/setup-python@v5
if: matrix.system != 'msys2'
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
@@ -231,10 +232,10 @@ jobs:
cd "${{ inputs.root_directory || '.' }}" cd "${{ inputs.root_directory || '.' }}"
[ -n '${{ inputs.apptest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS [ -n '${{ inputs.apptest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS
if [ -n '${{ inputs.coverage_config }}' ]; then if [ -n '${{ inputs.coverage_config }}' ]; then
echo "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}" printf "%s\n" "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}"
coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }} coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}
else else
echo "python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}" printf "%s\n" "python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}"
python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }} python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.apptest_directory }}
fi fi
@@ -255,9 +256,10 @@ jobs:
- name: 📤 Upload 'TestReportSummary.xml' artifact - name: 📤 Upload 'TestReportSummary.xml' artifact
if: inputs.apptest_xml_artifact != '' if: inputs.apptest_xml_artifact != ''
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.apptest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.apptest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: report/unit/TestReportSummary.xml working-directory: report/unit
path: TestReportSummary.xml
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -47,13 +47,13 @@ jobs:
steps: steps:
- name: 🗑️ Delete package Artifacts - name: 🗑️ Delete package Artifacts
if: ${{ ! startsWith(github.ref, 'refs/tags') }}
uses: geekyeggo/delete-artifact@v5 uses: geekyeggo/delete-artifact@v5
if: ${{ ! startsWith(github.ref, 'refs/tags') }}
with: with:
name: ${{ inputs.package }} name: ${{ inputs.package }}
- name: 🗑️ Delete remaining Artifacts - name: 🗑️ Delete remaining Artifacts
if: ${{ inputs.remaining != '' }}
uses: geekyeggo/delete-artifact@v5 uses: geekyeggo/delete-artifact@v5
if: ${{ inputs.remaining != '' }}
with: with:
name: ${{ inputs.remaining }} name: ${{ inputs.remaining }}

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -38,10 +38,10 @@ jobs:
steps: steps:
- name: '❗ Deprecation message' - name: '❗ Deprecation message'
run: echo "::warning title=Deprecated::'BuildTheDocs.yml' is not maintained anymore. Please switch to 'SphinxDocumentation.yml', 'LaTeXDocumentation.yml' and 'ExtractConfiguration.yml'." 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: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 🛳️ Build documentation - name: 🛳️ Build documentation
uses: buildthedocs/btd@v0 uses: buildthedocs/btd@v0
@@ -49,11 +49,12 @@ jobs:
skip-deploy: true skip-deploy: true
- name: 📤 Upload 'documentation' artifacts - name: 📤 Upload 'documentation' artifacts
uses: pyTooling/upload-artifact@v4
if: inputs.artifact != '' if: inputs.artifact != ''
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.artifact }} name: ${{ inputs.artifact }}
path: doc/_build/html working-directory: doc/_build/html
path: '*'
retention-days: 1 retention-days: 1
- name: '📓 Publish site to GitHub Pages' - name: '📓 Publish site to GitHub Pages'

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -32,7 +32,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
directory: directory:
description: 'Source code directory to check.' description: 'Source code directory to check.'
@@ -50,7 +50,7 @@ jobs:
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -59,14 +59,25 @@ jobs:
- name: 🔧 Install wheel,tomli and pip dependencies (native) - name: 🔧 Install wheel,tomli and pip dependencies (native)
run: | run: |
python -m pip install --disable-pip-version-check -U docstr_coverage interrogate python -m pip install --disable-pip-version-check -U docstr_coverage interrogate[png]
- name: Run 'interrogate' Documentation Coverage Check - name: Run 'interrogate' Documentation Coverage Check
continue-on-error: true continue-on-error: true
run: | run: |
interrogate -c pyproject.toml --fail-under=${{ inputs.fail_under }} && echo "::error title=interrogate::Insufficient documentation quality (goal: ${{ inputs.fail_under }})" set +e
interrogate -c pyproject.toml --fail-under=${{ inputs.fail_under }} ${{ inputs.directory }}
if [[ $? -ne 0 ]]; then
printf "::error title=%s::%s\n" "interrogate" "Insufficient documentation quality (goal: ${{ inputs.fail_under }})"
fi
- name: Run 'docstr_coverage' Documentation Coverage Check - name: Run 'docstr_coverage' Documentation Coverage Check
continue-on-error: true continue-on-error: true
run: | run: |
docstr-coverage -v 2 --fail-under=${{ inputs.fail_under }} ${{ inputs.directory }} && echo "::error title=docstr-coverage::Insufficient documentation quality (goal: ${{ inputs.fail_under }})" set +e
docstr-coverage --fail-under=${{ inputs.fail_under }} ${{ inputs.directory }}
if [[ $? -ne 0 ]]; then
printf "%s\n" "::error title=docstr-coverage::Insufficient documentation quality (goal: ${{ inputs.fail_under }})"
fi

View File

@@ -1,3 +1,24 @@
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# 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: Namespace Package name: Namespace Package
on: on:
@@ -72,51 +93,100 @@ on:
required: false required: false
default: '' default: ''
type: string type: string
codecov:
description: 'Publish merged coverage and unittest reports to Codecov.'
required: false
default: 'false'
type: string
codacy:
description: 'Publish merged coverage report to Codacy.'
required: false
default: 'false'
type: string
dorny:
description: 'Publish merged unittest report via Dorny Test-Reporter.'
required: false
default: 'false'
type: string
cleanup:
description: 'Cleanup artifacts afterwards.'
required: false
default: 'true'
type: string
secrets:
PYPI_TOKEN:
description: "Token for pushing releases to PyPI."
required: false
CODECOV_TOKEN:
description: "Token for pushing coverage and unittest results to Codecov."
required: false
CODACY_TOKEN:
description: "Token for pushing coverage results to Codacy."
required: false
jobs: jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r5
ConfigParams: ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r2 uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r5
with: with:
package_namespace: ${{ inputs.package_namespace }} package_namespace: ${{ inputs.package_namespace }}
package_name: ${{ inputs.package_name }} package_name: ${{ inputs.package_name }}
UnitTestingParams: UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
package_namespace: ${{ inputs.package_namespace }} package_namespace: ${{ inputs.package_namespace }}
package_name: ${{ inputs.package_name }} package_name: ${{ inputs.package_name }}
python_version: ${{ inputs.unittest_python_version }} python_version: ${{ inputs.unittest_python_version }}
python_version_list: ${{ inputs.unittest_python_version_list }} python_version_list: ${{ inputs.unittest_python_version_list }}
system_list: ${{ inputs.unittest_system_list }} system_list: ${{ inputs.unittest_system_list }}
include_list: ${{ inputs.unittest_include_list }} include_list: ${{ inputs.unittest_include_list }}
exclude_list: ${{ inputs.unittest_exclude_list }} exclude_list: ${{ inputs.unittest_exclude_list }}
disable_list: ${{ inputs.unittest_disable_list }} disable_list: ${{ inputs.unittest_disable_list }}
AppTestingParams: # AppTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 # uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
# with:
# package_namespace: ${{ inputs.package_namespace }}
# package_name: ${{ inputs.package_name }}
# python_version: ${{ inputs.apptest_python_version }}
# python_version_list: ${{ inputs.apptest_python_version_list }}
# system_list: ${{ inputs.apptest_system_list }}
# include_list: ${{ inputs.apptest_include_list }}
# exclude_list: ${{ inputs.apptest_exclude_list }}
# disable_list: ${{ inputs.apptest_disable_list }}
InstallParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
package_namespace: ${{ inputs.package_namespace }} package_namespace: ${{ inputs.package_namespace }}
package_name: ${{ inputs.package_name }} package_name: ${{ inputs.package_name }}
python_version: ${{ inputs.apptest_python_version }} python_version: ${{ inputs.unittest_python_version }}
python_version_list: ${{ inputs.apptest_python_version_list }} python_version_list: ''
system_list: ${{ inputs.apptest_system_list }} system_list: ${{ inputs.unittest_system_list }}
include_list: ${{ inputs.apptest_include_list }} include_list: ${{ inputs.unittest_include_list }}
exclude_list: ${{ inputs.apptest_exclude_list }} exclude_list: ${{ inputs.unittest_exclude_list }}
disable_list: ${{ inputs.apptest_disable_list }} disable_list: ${{ inputs.unittest_disable_list }}
UnitTesting: UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r2 uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
with: with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
requirements: "-r tests/unit/requirements.txt" requirements: "-r tests/unit/requirements.txt"
# pacboy: "msys/git python-lxml:p" # pacboy: "msys/git python-lxml:p"
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} 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 }}
StaticTypeCheck: StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r2 uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r5
needs: needs:
- ConfigParams - ConfigParams
- UnitTestingParams - UnitTestingParams
@@ -125,29 +195,41 @@ jobs:
commands: | commands: |
${{ needs.ConfigParams.outputs.mypy_prepare_command }} ${{ needs.ConfigParams.outputs.mypy_prepare_command }}
mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }} mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }}
html_report: 'report/typing' html_report: 'report/typing'
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
DocCoverage: DocCoverage:
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r2 uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
directory: ${{ inputs.package_namespace }}/${{ inputs.package_name }} directory: ${{ needs.ConfigParams.outputs.package_directory }}
# fail_below: 70
Package: Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r2 uses: pyTooling/Actions/.github/workflows/Package.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- UnitTesting # - UnitTesting
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
Install:
uses: pyTooling/Actions/.github/workflows/InstallPackage.yml@r5
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 }}
# AppTesting: # AppTesting:
# uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@r2 # uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@r5
# needs: # needs:
# - AppTestingParams # - AppTestingParams
# - UnitTestingParams # - UnitTestingParams
@@ -158,62 +240,81 @@ jobs:
# apptest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).apptesting_xml }} # apptest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).apptesting_xml }}
PublishCoverageResults: PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
- UnitTesting - UnitTesting
if: success() || failure()
with: with:
# coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} # coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
# coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} # coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} # coverage_report_xml_directory: ${{ needs.ConfigParams.outputs.coverage_report_xml_directory }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} # 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 }}
secrets: secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}
PublishTestResults: PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
- UnitTesting - UnitTesting
if: success() || failure()
with: with:
merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} testsuite-summary-name: ${{ needs.ConfigParams.outputs.package_fullname }}
merged_junit_filename: ${{ 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 }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# VerifyDocs: # VerifyDocs:
# uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r2 # uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r5
# needs: # needs:
# - UnitTestingParams # - UnitTestingParams
# with: # with:
# python_version: ${{ needs.UnitTestingParams.outputs.python_version }} # python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
Documentation: Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r2 uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r5
needs: needs:
- UnitTestingParams
- ConfigParams - ConfigParams
- UnitTestingParams
- PublishTestResults - PublishTestResults
- PublishCoverageResults - PublishCoverageResults
# - VerifyDocs # - VerifyDocs
if: success() || failure()
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-ubuntu-native-3.12 unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
IntermediateCleanUp: IntermediateCleanUp:
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r2 uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- PublishCoverageResults - PublishCoverageResults
- PublishTestResults - PublishTestResults
- Documentation if: ( success() || failure() ) && inputs.cleanup == 'true'
with: with:
sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}- sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}-
xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}- xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-
# PDFDocumentation: # PDFDocumentation:
# uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@r2 # uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@r5
# needs: # needs:
# - UnitTestingParams # - UnitTestingParams
# - Documentation # - Documentation
@@ -223,7 +324,7 @@ jobs:
# pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }} # pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
PublishToGitHubPages: PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- Documentation - Documentation
@@ -231,33 +332,64 @@ jobs:
- PublishCoverageResults - PublishCoverageResults
- StaticTypeCheck - StaticTypeCheck
with: with:
doc: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} doc: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
coverage: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} coverage: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
TriggerTaggedRelease:
uses: pyTooling/Actions/.github/workflows/TagReleaseCommit.yml@r5
needs:
- Prepare
- UnitTesting
- Install
# - AppTesting
# - StaticTypeCheck
- Package
- PublishToGitHubPages
if: needs.Prepare.outputs.is_release_commit && github.event_name != 'schedule'
permissions:
contents: write # required for create tag
actions: write # required for trigger workflow
with:
version: ${{ needs.Prepare.outputs.version }}
auto_tag: ${{ needs.Prepare.outputs.is_release_commit }}
secrets: inherit
ReleasePage: ReleasePage:
uses: pyTooling/Actions/.github/workflows/Release.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@r5
if: startsWith(github.ref, 'refs/tags')
needs: needs:
- Package - Prepare
- UnitTesting
- Install
# - AppTesting # - AppTesting
# - StaticTypeCheck
- Package
- PublishToGitHubPages - PublishToGitHubPages
if: needs.Prepare.outputs.is_release_tag == 'true'
permissions:
contents: write
actions: write
with:
tag: ${{ needs.Prepare.outputs.version }}
secrets: inherit
PublishOnPyPI: PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r5
if: startsWith(github.ref, 'refs/tags')
needs: needs:
- Prepare
- UnitTestingParams - UnitTestingParams
- Package
- ReleasePage - ReleasePage
if: needs.Prepare.outputs.is_release_tag == 'true'
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
requirements: -r dist/requirements.txt requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
secrets: secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
ArtifactCleanUp: ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r2 uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- UnitTesting - UnitTesting
@@ -269,6 +401,7 @@ jobs:
- PublishToGitHubPages - PublishToGitHubPages
# - PublishOnPyPI # - PublishOnPyPI
- IntermediateCleanUp - IntermediateCleanUp
if: inputs.cleanup == 'true'
with: with:
package: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} package: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
remaining: | remaining: |

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -72,10 +72,13 @@ jobs:
steps: steps:
- name: '❗ Deprecation message' - name: '❗ Deprecation message'
run: echo "::warning title=Deprecated::'CoverageCollection.yml' is not maintained anymore. Please switch to 'UnitTesting.yml', 'PublishCoverageResults.yml' and 'PublishTestResults.yml'." 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: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -147,7 +150,7 @@ jobs:
ABSDIR=$(pwd) ABSDIR=$(pwd)
cd "${{ inputs.tests_directory || '.' }}" cd "${{ inputs.tests_directory || '.' }}"
[ -n '${{ inputs.coverage_config }}' ] && PYCOV_ARGS="--cov-config=${ABSDIR}/${{ inputs.coverage_config }}" || unset PYCOV_ARGS [ -n '${{ inputs.coverage_config }}' ] && PYCOV_ARGS="--cov-config=${ABSDIR}/${{ inputs.coverage_config }}" || unset PYCOV_ARGS
echo "python -m pytest -rA --cov=${ABSDIR} ${PYCOV_ARGS} ${{ inputs.unittest_directory }} --color=yes" printf "%s\n" "python -m pytest -rA --cov=${ABSDIR} ${PYCOV_ARGS} ${{ inputs.unittest_directory }} --color=yes"
python -m pytest -rA --cov=${ABSDIR} $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes python -m pytest -rA --cov=${ABSDIR} $PYCOV_ARGS ${{ inputs.unittest_directory }} --color=yes
- name: Convert to cobertura format - name: Convert to cobertura format
@@ -160,16 +163,17 @@ jobs:
- name: 📤 Upload 'Coverage Report' artifact - name: 📤 Upload 'Coverage Report' artifact
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.artifact }} name: ${{ inputs.artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_html_directory }} working-directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: 📊 Publish coverage at CodeCov - name: 📊 Publish coverage at CodeCov
continue-on-error: true continue-on-error: true
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v5
with: with:
files: ${{ steps.getVariables.outputs.coverage_report_xml }} files: ${{ steps.getVariables.outputs.coverage_report_xml }}
flags: unittests flags: unittests

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -32,7 +32,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
package_namespace: package_namespace:
description: 'Name of the tool''s namespace.' description: 'Name of the tool''s namespace.'
@@ -59,18 +59,42 @@ on:
mypy_prepare_command: mypy_prepare_command:
description: "" description: ""
value: ${{ jobs.Extract.outputs.mypy_prepare_command }} 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_directory:
description: "" description: ""
value: ${{ jobs.Extract.outputs.coverage_report_html_directory }} value: ${{ jobs.Extract.outputs.coverage_report_html_directory }}
coverage_report_xml_directory: coverage_report_xml_directory:
description: "" description: ""
value: ${{ jobs.Extract.outputs.coverage_report_xml_directory }} value: ${{ jobs.Extract.outputs.coverage_report_xml_directory }}
coverage_report_xml_filename:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_xml_filename }}
coverage_report_xml: coverage_report_xml:
description: "" description: ""
value: ${{ jobs.Extract.outputs.coverage_report_xml }} value: ${{ jobs.Extract.outputs.coverage_report_xml }}
coverage_report_json_directory: coverage_report_json_directory:
description: "" description: ""
value: ${{ jobs.Extract.outputs.coverage_report_json_directory }} value: ${{ jobs.Extract.outputs.coverage_report_json_directory }}
coverage_report_json_filename:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_json_filename }}
coverage_report_json: coverage_report_json:
description: "" description: ""
value: ${{ jobs.Extract.outputs.coverage_report_json }} value: ${{ jobs.Extract.outputs.coverage_report_json }}
@@ -80,18 +104,26 @@ jobs:
name: 📓 Extract configurations from pyproject.toml name: 📓 Extract configurations from pyproject.toml
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
outputs: outputs:
package_fullname: ${{ steps.getPackageName.outputs.package_fullname }} package_fullname: ${{ steps.getPackageName.outputs.package_fullname }}
package_directory: ${{ steps.getPackageName.outputs.package_directory }} package_directory: ${{ steps.getPackageName.outputs.package_directory }}
mypy_prepare_command: ${{ steps.getPackageName.outputs.mypy_prepare_command }} mypy_prepare_command: ${{ steps.getPackageName.outputs.mypy_prepare_command }}
coverage_report_html_directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }} unittest_report_xml_directory: ${{ steps.getVariables.outputs.unittest_report_xml_directory }}
coverage_report_xml_directory: ${{ steps.getVariables.outputs.coverage_report_xml_directory }} unittest_report_xml_filename: ${{ steps.getVariables.outputs.unittest_report_xml_filename }}
coverage_report_xml: ${{ steps.getVariables.outputs.coverage_report_xml }} unittest_report_xml: ${{ steps.getVariables.outputs.unittest_report_xml }}
coverage_report_json_directory: ${{ steps.getVariables.outputs.coverage_report_json_directory }} unittest_merged_report_xml_directory: ${{ steps.getVariables.outputs.unittest_merged_report_xml_directory }}
coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }} 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 }}
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -113,6 +145,12 @@ jobs:
namespace = "${{ inputs.package_namespace }}".strip() namespace = "${{ inputs.package_namespace }}".strip()
name = "${{ inputs.package_name }}".strip() name = "${{ inputs.package_name }}".strip()
print(dedent(f"""\
INPUTS:
package_namespace: {namespace}
package_name: {name}
"""))
if namespace == "" or namespace == ".": if namespace == "" or namespace == ".":
fullname = f"{name}" fullname = f"{name}"
directory = f"{name}" directory = f"{name}"
@@ -122,6 +160,13 @@ jobs:
directory = f"{namespace}/{name}" directory = f"{namespace}/{name}"
mypy_prepare_command = f"touch {namespace}/__init__.py" 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")) github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}") print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f: with github_output.open("a+", encoding="utf-8") as f:
@@ -144,10 +189,11 @@ jobs:
from tomli import load as tomli_load from tomli import load as tomli_load
htmlDirectory = Path("htmlcov") unittestXMLFile = Path("./unittest.xml")
xmlFile = Path("./coverage.xml") coverageHTMLDirectory = Path("htmlcov")
jsonFile = Path("./coverage.json") coverageXMLFile = Path("./coverage.xml")
coverageRC = "${{ inputs.coverage_config }}".strip() coverageJSONFile = Path("./coverage.json")
coverageRC = "${{ inputs.coverage_config }}".strip()
# Read output paths from 'pyproject.toml' file # Read output paths from 'pyproject.toml' file
if coverageRC == "pyproject.toml": if coverageRC == "pyproject.toml":
@@ -156,9 +202,11 @@ jobs:
with pyProjectFile.open("rb") as file: with pyProjectFile.open("rb") as file:
pyProjectSettings = tomli_load(file) pyProjectSettings = tomli_load(file)
htmlDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"]) unittestXMLFile = Path(pyProjectSettings["tool"]["pytest"]["junit_xml"])
xmlFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"]) mergedUnittestXMLFile = Path(pyProjectSettings["tool"]["pyedaa-reports"]["junit_xml"])
jsonFile = Path(pyProjectSettings["tool"]["coverage"]["json"]["output"]) coverageHTMLDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"])
coverageXMLFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"])
coverageJSONFile= Path(pyProjectSettings["tool"]["coverage"]["json"]["output"])
else: else:
print(f"File '{pyProjectFile}' not found.") print(f"File '{pyProjectFile}' not found.")
print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.") print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.")
@@ -171,9 +219,9 @@ jobs:
with coverageRCFile.open("rb") as file: with coverageRCFile.open("rb") as file:
coverageRCSettings = tomli_load(file) coverageRCSettings = tomli_load(file)
htmlDirectory = Path(coverageRCSettings["html"]["directory"]) coverageHTMLDirectory = Path(coverageRCSettings["html"]["directory"])
xmlFile = Path(coverageRCSettings["xml"]["output"]) coverageXMLFile = Path(coverageRCSettings["xml"]["output"])
jsonFile = Path(coverageRCSettings["json"]["output"]) coverageJSONFile = Path(coverageRCSettings["json"]["output"])
else: else:
print(f"File '{coverageRCFile}' not found.") print(f"File '{coverageRCFile}' not found.")
print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.") print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.")
@@ -184,11 +232,26 @@ jobs:
print(f"GITHUB_OUTPUT: {github_output}") print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f: with github_output.open("a+", encoding="utf-8") as f:
f.write(dedent(f"""\ f.write(dedent(f"""\
coverage_report_html_directory={htmlDirectory.as_posix()} unittest_report_xml_directory={unittestXMLFile.parent.as_posix()}
coverage_report_xml_directory={xmlFile.parent.as_posix()} unittest_report_xml_filename={unittestXMLFile.name}
coverage_report_xml={xmlFile.as_posix()} unittest_report_xml={unittestXMLFile.as_posix()}
coverage_report_json_directory={jsonFile.parent.as_posix()} unittest_merged_report_xml_directory={mergedUnittestXMLFile.parent.as_posix()}
coverage_report_json={jsonFile.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()}
""")) """))
print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}\n json={jsonFile}") print(dedent(f"""\
DEBUG:
unittest xml: {unittestXMLFile}
merged unittest xml: {mergedUnittestXMLFile}
coverage html: {coverageHTMLDirectory}
coverage xml: {coverageXMLFile}
coverage json: {coverageJSONFile}
"""))

130
.github/workflows/InstallPackage.yml vendored Normal file
View File

@@ -0,0 +1,130 @@
# ==================================================================================================================== #
# 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: Install Package
on:
workflow_call:
inputs:
jobs:
description: 'JSON list with environment fields, telling the system and Python versions to run tests with.'
required: true
type: string
wheel:
description: "Wheel package as input artifact."
required: true
type: string
package_name:
description: "Name of the Python package."
required: true
type: string
jobs:
PackageInstallation:
name: ${{ matrix.sysicon }} ${{ matrix.pyicon }} Package installation using Python ${{ matrix.python }}
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(inputs.jobs) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: 📥 Download artifacts '${{ inputs.wheel }}' from 'Package' job
uses: pyTooling/download-artifact@v5
with:
name: ${{ inputs.wheel }}
path: install
- name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}'
uses: msys2/setup-msys2@v2
if: matrix.system == 'msys2'
with:
msystem: ${{ matrix.runtime }}
update: true
pacboy: >-
python-pip:p python-wheel:p
python-lxml:p
python-markupsafe:p
python-pyaml:p python-types-pyyaml:p
python-ruamel-yaml:p python-ruamel.yaml.clib:p
python-tomli:p
- name: 🐍 Setup Python ${{ matrix.python }}
uses: actions/setup-python@v5
if: matrix.system != 'msys2'
with:
python-version: ${{ matrix.python }}
- name: 🔧 Install wheel and pip dependencies (native)
if: matrix.system != 'msys2'
run: |
python -m pip install --disable-pip-version-check -U wheel
- name: 🔧 Install wheel from artifact (Ubuntu/macOS)
if: matrix.system != 'windows'
run: |
python -m pip install --disable-pip-version-check -U install/*.whl
- name: 🔧 Install wheel from artifact (Windows)
if: matrix.system == 'windows'
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'
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_NOCOLOR=$'\x1b[0m'
printf "Import package and checking package version ...\n "
python3 - << EOF | tee ImportTest.log | grep -E "^Package version:\s+[0-9]+\.[0-9]+\.[0-9]+"
from ${{ inputs.package_name }} import __version__
print(f"Package version: {__version__}")
EOF
if [[ $? -eq 0 ]]; then
printf " ${ANSI_LIGHT_GREEN}[PASSED]${ANSI_NOCOLOR}\n"
else
printf " ${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "::error title=%s::%s\n" "InstallPackage" "Couldn't check package version of '${{ inputs.package_name }}'."
exit 1
fi
- name: 📦 Run application tests (Windows)
if: matrix.system == 'windows'
run: |
$result=$(python -c "from ${{ inputs.package_name }} import __version__; print(f""Package version: {__version__}"")")
Write-Host $result
if ($result -match "Package version:\s+\d+\.\d+\.\d+") {
Write-Host -ForegroundColor Green "[PASSED]"
} else {
Write-Host -ForegroundColor Red "[FAILED]"
Write-Host ("::error title={0}::{1}" -f "InstallPackage", "Couldn't check package version of '${{ inputs.package_name }}'.")
exit 1
}

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -50,22 +50,33 @@ jobs:
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps: steps:
- name: 📥 Download artifacts '${{ inputs.latex_artifact }}' from 'SphinxDocumentation' job - name: 📥 Download artifacts '${{ inputs.latex_artifact }}' from 'SphinxDocumentation' job
uses: actions/download-artifact@v4 uses: pyTooling/download-artifact@v5
with: with:
name: ${{ inputs.latex_artifact }} name: ${{ inputs.latex_artifact }}
path: latex path: latex
- name: Compile LaTeX document - name: Debug
uses: xu-cheng/latex-action@master run: |
tree -pash .
- name: Build LaTeX document using 'pytooling/miktex:sphinx'
uses: addnab/docker-run-action@v3
with: with:
working_directory: latex image: pytooling/miktex:sphinx
root_file: ${{ inputs.document }}.tex options: -v ${{ github.workspace }}/latex:/latex --workdir /latex
run: |
which pdflatex
pwd
ls -lAh
latexmk -xelatex ${{ inputs.document }}.tex
- name: 📤 Upload 'PDF Documentation' artifact - name: 📤 Upload 'PDF Documentation' artifact
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
if: inputs.pdf_artifact != '' if: inputs.pdf_artifact != ''
with: with:
name: ${{ inputs.pdf_artifact }} name: ${{ inputs.pdf_artifact }}
working-directory: latex
path: ${{ inputs.document }}.pdf path: ${{ inputs.document }}.pdf
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1

530
.github/workflows/NightlyRelease.yml vendored Normal file
View File

@@ -0,0 +1,530 @@
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# 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: Nightly
on:
workflow_call:
inputs:
ubuntu_image:
description: 'Name of the Ubuntu image.'
required: false
default: 'ubuntu-24.04'
type: string
nightly_name:
description: 'Name of the nightly release.'
required: false
default: 'nightly'
type: string
nightly_title:
description: 'Title of the nightly release.'
required: false
default: ''
type: string
nightly_description:
description: 'Description of the nightly release.'
required: false
default: 'Release of artifacts from latest CI pipeline.'
type: string
draft:
description: 'Specify if this is a draft.'
required: false
default: false
type: boolean
prerelease:
description: 'Specify if this is a pre-release.'
required: false
default: false
type: boolean
latest:
description: 'Specify if this is the latest release.'
required: false
default: false
type: boolean
replacements:
description: 'Multi-line string containing search=replace patterns.'
required: false
default: ''
type: string
assets:
description: 'Multi-line string containing artifact:file:title asset descriptions.'
required: true
type: string
inventory-json:
type: string
required: false
default: ''
inventory-version:
type: string
required: false
default: ''
inventory-categories:
type: string
required: false
default: ''
tarball-name:
type: string
required: false
default: '__pyTooling_upload_artifact__.tar'
can-fail:
type: boolean
required: false
default: false
jobs:
Release:
name: 📝 Update 'Nightly Page' on GitHub
runs-on: ${{ inputs.ubuntu_image }}
continue-on-error: ${{ inputs.can-fail }}
permissions:
contents: write
actions: write
# attestations: write
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
# The command 'git describe' (used for version) needs the history.
fetch-depth: 0
- name: 🔧 Install zstd
run: sudo apt-get install -y --no-install-recommends zstd
- name: 📑 Delete (old) Release Page
id: deleteReleasePage
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_LIGHT_YELLOW=$'\x1b[93m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
printf "%s" "Deleting release '${{ inputs.nightly_name }}' ... "
message="$(gh release delete ${{ inputs.nightly_name }} --yes 2>&1)"
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
elif [[ "${message}" == "release not found" ]]; then
printf "%s\n" "${ANSI_LIGHT_YELLOW}[NOT FOUND]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "InternalError" "Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'."
exit 1
fi
- name: 📑 (Re)create (new) Release Page
id: createReleasePage
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
addDraft="--draft"
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
addPreRelease="--prerelease"
fi
if [[ "${{ inputs.latest }}" == "false" ]]; then
addLatest="--latest=false"
fi
if [[ "${{ inputs.nightly_title }}" != "" ]]; then
addTitle=("--title" "${{ inputs.nightly_title }}")
fi
cat <<'EOF' > __NoTeS__.md
${{ inputs.nightly_description }}
EOF
if [[ -s __NoTeS__.md ]]; then
addNotes=("--notes-file" "__NoTeS__.md")
fi
# Apply replacements
while IFS=$'\r\n' read -r patternLine; do
# skip empty lines
[[ "$patternLine" == "" ]] && continue
pattern="${patternLine%%=*}"
replacement="${patternLine#*=}"
sed -i -e "s/%$pattern%/$replacement/g" "__NoTeS__.md"
done <<<'${{ inputs.replacements }}'
# Add footer line
cat <<EOF >> __NoTeS__.md
--------
Published from [${{ github.workflow }}](https://github.com/Paebbels/ghdl/actions/runs/${{ github.run_id }}) workflow triggered by @${{ github.actor }} on $(date '+%Y-%m-%d %H:%M:%S %Z').
EOF
printf "%s\n" "Creating release '${{ inputs.nightly_name }}' ... "
message="$(gh release create "${{ inputs.nightly_name }}" --verify-tag $addDraft $addPreRelease $addLatest "${addTitle[@]}" "${addNotes[@]}" 2>&1)"
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "InternalError" "Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'."
exit 1
fi
- name: 📥 Download artifacts and upload as assets
id: uploadAssets
run: |
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'
export GH_TOKEN=${{ github.token }}
Replace() {
line="$1"
while IFS=$'\r\n' read -r patternLine; do
# skip empty lines
[[ "$patternLine" == "" ]] && continue
pattern="${patternLine%%=*}"
replacement="${patternLine#*=}"
line="${line//"%$pattern%"/"$replacement"}"
done <<<'${{ inputs.replacements }}'
printf "%s\n" "$line"
}
# Create JSON inventory
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
VERSION="1.0"
# Split categories by ',' into a Bash array.
# See https://stackoverflow.com/a/45201229/3719459
if [[ "${{ inputs.inventory-categories }}" != "" ]]; then
readarray -td, inventoryCategories <<<"${{ inputs.inventory-categories }},"
unset 'inventoryCategories[-1]'
declare -p inventoryCategories
else
inventoryCategories=""
fi
jsonInventory=$(jq -c -n \
--arg version "${VERSION}" \
--arg date "$(date +"%Y-%m-%dT%H-%M-%S%:z")" \
--argjson jsonMeta "$(jq -c -n \
--arg tag "${{ inputs.nightly_name }}" \
--arg version "${{ inputs.inventory-version }}" \
--arg hash "${{ github.sha }}" \
--arg repo "${{ github.server_url }}/${{ github.repository }}" \
--arg release "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ inputs.nightly_name }}" \
--argjson categories "$(jq -c -n \
'$ARGS.positional' \
--args "${inventoryCategories[@]}" \
)" \
'{"tag": $tag, "version": $version, "git-hash": $hash, "repository-url": $repo, "release-url": $release, "categories": $categories}' \
)" \
'{"version": 1.0, "timestamp": $date, "meta": $jsonMeta, "files": {}}'
)
fi
ERRORS=0
# A dictionary of 0/1 to avoid duplicate downloads
declare -A downloadedArtifacts
# A dictionary to check for duplicate asset files in release
declare -A assetFilenames
while IFS=$'\r\n' read -r assetLine; do
if [[ "${assetLine}" == "" || "${assetLine:0:1}" == "#" ]]; then
continue
fi
# split assetLine colon separated triple: artifact:asset:title
artifact="${assetLine%%:*}"
assetLine="${assetLine#*:}"
asset="${assetLine%%:*}"
assetLine="${assetLine#*:}"
if [[ "${{ inputs.inventory-json }}" == "" ]]; then
categories=""
title="${assetLine##*:}"
else
categories="${assetLine%%:*}"
title="${assetLine##*:}"
fi
# remove leading whitespace
asset="${asset#"${asset%%[![:space:]]*}"}"
categories="${categories#"${categories%%[![:space:]]*}"}"
title="${title#"${title%%[![:space:]]*}"}"
# apply replacements
asset="$(Replace "${asset}")"
title="$(Replace "${title}")"
printf "%s\n" "Publish asset '${asset}' from artifact '${artifact}' with title '${title}'"
printf " %s" "Checked asset for duplicates ... "
if [[ -n "${assetFilenames[$asset]}" ]]; then
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "DuplicateAsset" "Asset '${asset}' from artifact '${artifact}' was already uploaded to release '${{ inputs.nightly_name }}'."
ERRORS=$((ERRORS + 1))
continue
else
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
assetFilenames[$asset]=1
fi
# Download artifact by artifact name
if [[ -n "${downloadedArtifacts[$artifact]}" ]]; then
printf " %s\n" "downloading '${artifact}' ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
else
echo " downloading '${artifact}' ... "
printf " %s" "gh run download $GITHUB_RUN_ID --dir \"${artifact}\" --name \"${artifact}\" "
gh run download $GITHUB_RUN_ID --dir "${artifact}" --name "${artifact}"
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't download artifact '${artifact}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "ArtifactNotFound" "Couldn't download artifact '${artifact}'."
ERRORS=$((ERRORS + 1))
continue
fi
downloadedArtifacts[$artifact]=1
printf " %s" "Checking for embedded tarball ... "
if [[ -f "${artifact}/${{ inputs.tarball-name }}" ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[FOUND]${ANSI_NOCOLOR}"
pushd "${artifact}" > /dev/null
printf " %s" "Extracting embedded tarball ... "
tar -xf "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
fi
printf " %s" "Removing temporary tarball ... "
rm -f "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
fi
popd > /dev/null
else
printf "%s\n" "${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
fi
fi
# Check if artifact should be compressed (zip, tgz) or if asset was part of the downloaded artifact.
printf " %s" "checking asset '${artifact}/${asset}' ... "
if [[ "${asset}" == !*.zip ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[ZIP]${ANSI_NOCOLOR}"
asset="${asset##*!}"
printf "::group:: %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
zip -r "../${asset}" *
)
retCode=$?
printf "::endgroup::\n"
if [[ $retCode -eq 0 ]]; then
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
printf " %s\n" "Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zip file '${asset}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to zip file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ "${asset}" == !*.tgz || "${asset}" == !*.tar.gz || "${asset}" == \$*.tgz || "${asset}" == \$*.tar.gz ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[TAR/GZ]${ANSI_NOCOLOR}"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
tar -c --gzip --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
retCode=$?
else
asset="${asset##*!}"
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
tar -c --gzip --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
printf " %s\n" "Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to tgz file '${asset}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to tgz file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ "${asset}" == !*.tzst || "${asset}" == !*.tar.zst || "${asset}" == \$*.tzst || "${asset}" == \$*.tar.zst ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[ZST]${ANSI_NOCOLOR}"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
tar -c --zstd --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
retCode=$?
else
asset="${asset##*!}"
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
tar -c --zstd --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
printf " %s\n" "Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zst file '${asset}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to zst file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ -e "${artifact}/${asset}" ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${artifact}/${asset}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't find asset '${asset}' in artifact '${artifact}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "FileNotFound" "Couldn't find asset '${asset}' in artifact '${artifact}'."
ERRORS=$((ERRORS + 1))
continue
fi
# Add asset to JSON inventory
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
if [[ "${categories}" != "${title}" ]]; then
printf " %s\n" "adding file '${uploadFile#*/}' with '${categories//;/ → }' to JSON inventory ..."
category=""
jsonEntry=$(jq -c -n \
--arg title "${title}" \
--arg file "${uploadFile#*/}" \
'{"file": $file, "title": $title}' \
)
while [[ "${categories}" != "${category}" ]]; do
category="${categories##*,}"
categories="${categories%,*}"
jsonEntry=$(jq -c -n --arg cat "${category}" --argjson value "${jsonEntry}" '{$cat: $value}')
done
jsonInventory=$(jq -c -n \
--argjson inventory "${jsonInventory}" \
--argjson file "${jsonEntry}" \
'$inventory * {"files": $file}' \
)
else
printf " %s\n" "adding file '${uploadFile#*/}' to JSON inventory ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
fi
fi
# Upload asset to existing release page
printf " %s" "uploading asset '${asset}' from '${uploadFile}' with title '${title}' ... "
gh release upload ${{ inputs.nightly_name }} "${uploadFile}#${title}" --clobber
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "UploadError" "Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'."
ERRORS=$((ERRORS + 1))
continue
fi
done <<<'${{ inputs.assets }}'
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
inventoryTitle="Release Inventory (JSON)"
printf "%s\n" "Publish asset '${{ inputs.inventory-json }}' with title '${inventoryTitle}'"
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Writing JSON inventory to '${{ inputs.inventory-json }}' ...."
printf "%s\n" "$(jq -n --argjson inventory "${jsonInventory}" '$inventory')" > "${{ inputs.inventory-json }}"
cat "${{ inputs.inventory-json }}"
printf "::endgroup::\n"
# Upload inventory asset to existing release page
printf " %s" "uploading asset '${{ inputs.inventory-json }}' title '${inventoryTitle}' ... "
gh release upload ${{ inputs.nightly_name }} "${{ inputs.inventory-json }}#${inventoryTitle}" --clobber
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't upload asset '${{ inputs.inventory-json }}' to release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "UploadError" "Couldn't upload asset '${{ inputs.inventory-json }}' to release '${{ inputs.nightly_name }}'."
ERRORS=$((ERRORS + 1))
continue
fi
fi
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Inspecting downloaded artifacts ..."
tree -pash -L 3 .
printf "::endgroup::\n"
if [[ $ERRORS -ne 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_RED}${ERRORS} errors detected in previous steps.${ANSI_NOCOLOR}"
exit 1
fi
- name: 📑 Remove draft state from Release Page
if: ${{ ! inputs.draft }}
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
# Remove draft-state from release page
printf "%s" "Remove draft-state from release '${title}' ... "
gh release edit --draft=false "${{ inputs.nightly_name }}"
if [[ $? -eq 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
printf "%s\n" "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_LIGHT_RED}Couldn't remove draft-state from release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "ReleasePage" "Couldn't remove draft-state from release '${{ inputs.nightly_name }}'."
fi

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -33,7 +33,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
requirements: requirements:
description: 'Python dependencies to be installed through pip; if empty, use pyproject.toml through build.' description: 'Python dependencies to be installed through pip; if empty, use pyproject.toml through build.'
@@ -53,7 +53,10 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -103,9 +106,10 @@ jobs:
run: python setup.py bdist_wheel run: python setup.py bdist_wheel
- name: 📤 Upload wheel artifact - name: 📤 Upload wheel artifact
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.artifact }} name: ${{ inputs.artifact }}
path: dist/ working-directory: dist
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -83,7 +83,7 @@ on:
windows_image: windows_image:
description: 'The used GitHub Action image for Windows based jobs.' description: 'The used GitHub Action image for Windows based jobs.'
required: false required: false
default: 'windows-latest' default: 'windows-2022'
type: string type: string
macos_intel_image: macos_intel_image:
description: 'The used GitHub Action image for macOS (Intel x86-64) based jobs.' description: 'The used GitHub Action image for macOS (Intel x86-64) based jobs.'
@@ -93,8 +93,13 @@ on:
macos_arm_image: macos_arm_image:
description: 'The used GitHub Action image for macOS (ARM aarch64) based jobs.' description: 'The used GitHub Action image for macOS (ARM aarch64) based jobs.'
required: false required: false
default: 'macos-latest' default: 'macos-14'
type: string type: string
pipeline-delay:
description: 'Slow down this job, to delay the startup of the GitHub Action pipline.'
required: false
default: 0
type: number
outputs: outputs:
python_version: python_version:
@@ -121,6 +126,12 @@ jobs:
params: ${{ steps.params.outputs.params }} params: ${{ steps.params.outputs.params }}
steps: steps:
- name: Generate a startup delay of ${{ inputs.pipeline-delay }} seconds
id: delay
if: inputs.pipeline-delay >= 0
run: |
sleep ${{ inputs.pipeline-delay }}
- name: Generate 'params' and 'python_jobs' - name: Generate 'params' and 'python_jobs'
id: params id: params
shell: python shell: python
@@ -147,7 +158,7 @@ jobs:
else: else:
name = f"{package_namespace}.{package_name}" name = f"{package_namespace}.{package_name}"
currentMSYS2Version = "3.11" currentMSYS2Version = "3.12"
currentAlphaVersion = "3.14" currentAlphaVersion = "3.14"
currentAlphaRelease = "3.14.0-alpha.1" currentAlphaRelease = "3.14.0-alpha.1"
@@ -197,9 +208,10 @@ jobs:
"3.13": { "icon": "🟢", "until": "2029.10" }, "3.13": { "icon": "🟢", "until": "2029.10" },
"3.14": { "icon": "🟣", "until": "2030.10" }, "3.14": { "icon": "🟣", "until": "2030.10" },
"pypy-3.7": { "icon": "⟲⚫", "until": "????.??" }, "pypy-3.7": { "icon": "⟲⚫", "until": "????.??" },
"pypy-3.8": { "icon": "⟲🔴", "until": "????.??" }, "pypy-3.8": { "icon": "⟲", "until": "????.??" },
"pypy-3.9": { "icon": "⟲🟠", "until": "????.??" }, "pypy-3.9": { "icon": "⟲🔴", "until": "????.??" },
"pypy-3.10": { "icon": "⟲🟡", "until": "????.??" }, "pypy-3.10": { "icon": "⟲🟠", "until": "????.??" },
"pypy-3.11": { "icon": "⟲🟡", "until": "????.??" },
}, },
# Runner systems (runner images) supported by GitHub Actions # Runner systems (runner images) supported by GitHub Actions
"sys": { "sys": {
@@ -337,7 +349,7 @@ jobs:
- name: Verify out parameters - name: Verify out parameters
id: verify id: verify
run: | run: |
echo 'python_version: ${{ steps.params.outputs.python_version }}' printf "python_version: %s\n" '${{ steps.params.outputs.python_version }}'
echo 'python_jobs: ${{ steps.params.outputs.python_jobs }}' printf "python_jobs: %s\n" '${{ steps.params.outputs.python_jobs }}'
echo 'artifact_names: ${{ steps.params.outputs.artifact_names }}' printf "artifact_names: %s\n" '${{ steps.params.outputs.artifact_names }}'
echo 'params: ${{ steps.params.outputs.params }}' printf "params: %s\n" '${{ steps.params.outputs.params }}'

345
.github/workflows/PrepareJob.yml vendored Normal file
View File

@@ -0,0 +1,345 @@
name: Prepare Variables
on:
workflow_call:
inputs:
ubuntu_image:
description: 'Name of the Ubuntu image.'
required: false
default: 'ubuntu-24.04'
type: string
main_branch:
description: 'Name of the branch containing releases.'
required: false
default: 'main'
type: string
development_branch:
description: 'Name of the development branch containing features.'
required: false
default: 'dev'
type: string
release_branch:
description: 'Name of the branch containing releases and nightly builds.'
required: false
default: 'main'
type: string
nightly_tag_pattern:
description: 'Pattern for nightly tags on the release branch.'
required: false
default: 'nightly'
type: string
release_tag_pattern:
description: 'Pattern for release tags on the release branch. Usually: vXX.YY.ZZ'
required: false
default: '(v|r)?[0-9]+(\.[0-9]+){0,2}(-(dev|alpha|beta|rc)([0-9]*))?'
type: string
outputs:
on_main_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_main_branch }}
on_dev_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_dev_branch }}
on_release_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_release_branch }}
is_regular_commit:
description: ""
value: ${{ jobs.Prepare.outputs.is_regular_commit }}
is_merge_commit:
description: ""
value: ${{ jobs.Prepare.outputs.is_merge_commit }}
is_release_commit:
description: ""
value: ${{ jobs.Prepare.outputs.is_release_commit }}
is_nightly_tag:
description: ""
value: ${{ jobs.Prepare.outputs.is_nightly_tag }}
is_release_tag:
description: ""
value: ${{ jobs.Prepare.outputs.is_release_tag }}
ref_kind:
description: ""
value: ${{ jobs.Prepare.outputs.ref_kind }}
branch:
description: ""
value: ${{ jobs.Prepare.outputs.branch }}
tag:
description: ""
value: ${{ jobs.Prepare.outputs.tag }}
version:
description: ""
value: ${{ jobs.Prepare.outputs.version }}
pr_title:
description: ""
value: ${{ jobs.Prepare.outputs.pr_title }}
pr_number:
description: ""
value: ${{ jobs.Prepare.outputs.pr_number }}
# pr_mergedby:
# description: ""
# value: ${{ jobs.Prepare.outputs.pr_mergedby }}
# pr_mergedat:
# description: ""
# value: ${{ jobs.Prepare.outputs.pr_mergedat }}
jobs:
Prepare:
name: Extract Information
runs-on: ubuntu-24.04
outputs:
on_main_branch: ${{ steps.Classify.outputs.on_main_branch }}
on_dev_branch: ${{ steps.Classify.outputs.on_dev_branch }}
on_release_branch: ${{ steps.Classify.outputs.on_release_branch }}
is_regular_commit: ${{ steps.Classify.outputs.is_regular_commit }}
is_merge_commit: ${{ steps.Classify.outputs.is_merge_commit }}
is_release_commit: ${{ steps.Classify.outputs.is_release_commit }}
is_nightly_tag: ${{ steps.Classify.outputs.is_nightly_tag }}
is_release_tag: ${{ steps.Classify.outputs.is_release_tag }}
ref_kind: ${{ steps.Classify.outputs.ref_kind }}
branch: ${{ steps.Classify.outputs.branch }}
tag: ${{ steps.Classify.outputs.tag }}
version: ${{ steps.Classify.outputs.version || steps.FindPullRequest.outputs.pr_version }}
# release_version: ${{ steps.FindPullRequest.outputs.release_version }}
pr_title: ${{ steps.FindPullRequest.outputs.pr_title }}
pr_number: ${{ steps.Classify.outputs.pr_number || steps.FindPullRequest.outputs.pr_number }}
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
# The command 'git describe' (used for version) needs the history.
fetch-depth: 0
- name: 🖉 GitHub context information
run: |
printf "%s\n" "github.event_name: ${{ github.event_name }}"
printf "%s\n" "github.actor: ${{ github.actor }}"
printf "%s\n" "github.ref: ${{ github.ref }}"
printf "%s\n" "github.base_ref: ${{ github.base_ref }}"
printf "%s\n" "github.head_ref: ${{ github.head_ref }}"
printf "%s\n" "github.sha: ${{ github.sha }}"
- name: 🖉 Classify commit
id: Classify
run: |
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'
ref="${{ github.ref }}"
on_main_branch="false"
on_dev_branch="false"
on_release_branch="false"
is_regular_commit="false"
is_merge_commit="false"
is_release_commit="false"
is_nightly_tag="false"
is_release_tag="false"
ref_kind="unknown"
branch=""
tag=""
pr_number=""
version=""
if [[ "${ref:0:11}" == "refs/heads/" ]]; then
ref_kind="branch"
branch="${ref:11}"
printf "Commit check:\n"
if [[ "${branch}" == "${{ inputs.main_branch }}" ]]; then
on_main_branch="true"
if [[ -z "$(git rev-list -1 --merges ${{ github.sha }}~1..${{ github.sha }})" ]]; then
is_regular_commit="true"
printf " ${ANSI_LIGHT_YELLOW}regular "
else
is_merge_commit="true"
printf " ${ANSI_LIGHT_GREEN}merge "
fi
printf "commit${ANSI_NOCOLOR} on main branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.main_branch }}"
fi
if [[ "${branch}" == "${{ inputs.development_branch }}" ]]; then
on_dev_branch="true"
if [[ -z "$(git rev-list -1 --merges ${{ github.sha }}~1..${{ github.sha }})" ]]; then
is_regular_commit="true"
printf " ${ANSI_LIGHT_YELLOW}regular "
else
is_merge_commit="true"
printf " ${ANSI_LIGHT_GREEN}merge "
fi
printf "commit${ANSI_NOCOLOR} on development branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.development_branch }}"
fi
if [[ "${branch}" == "${{ inputs.release_branch }}" ]]; then
on_release_branch="true"
if [[ -z "$(git rev-list -1 --merges ${{ github.sha }}~1..${{ github.sha }})" ]]; then
is_regular_commit="true"
printf " ${ANSI_LIGHT_YELLOW}regular "
else
is_release_commit="true"
printf " ${ANSI_LIGHT_GREEN}release "
fi
printf "commit${ANSI_NOCOLOR} on release branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}"
fi
elif [[ "${ref:0:10}" == "refs/tags/" ]]; then
ref_kind="tag"
tag="${ref:10}"
printf "Tag check:\n"
printf " Check if tag is on release branch '%s' ... " "${{ inputs.release_branch }}"
git branch --remotes --contains $(git rev-parse --verify "tags/${tag}~0") | grep "origin/${{ inputs.release_branch }}" > /dev/null
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Tag '%s' isn't on branch '%s'.${ANSI_NOCOLOR}\n" "${tag}" "${{ inputs.release_branch }}"
printf "::error title=TagCheck::Tag '%s' isn't on branch '%s'.\n" "${tag}" "${{ inputs.release_branch }}"
exit 1
fi
NIGHTLY_TAG_PATTERN='^${{ inputs.nightly_tag_pattern }}$'
RELEASE_TAG_PATTERN='^${{ inputs.release_tag_pattern }}$'
printf " Check tag name against regexp '%s' ... " "${RELEASE_TAG_PATTERN}"
if [[ "${tag}" =~ NIGHTLY_TAG_PATTERN ]]; then
printf "${ANSI_LIGHT_GREEN}[NIGHTLY]${ANSI_NOCOLOR}\n"
is_nightly_tag="true"
elif [[ "${tag}" =~ $RELEASE_TAG_PATTERN ]]; then
printf "${ANSI_LIGHT_GREEN}[RELEASE]${ANSI_NOCOLOR}\n"
version="${tag}"
is_release_tag="true"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Tag name '%s' doesn't conform to regexp${ANSI_NOCOLOR}\n" "${tag}"
printf " ${ANSI_LIGHT_RED}nightly tag: %s${ANSI_NOCOLOR}\n" "${NIGHTLY_TAG_PATTERN}"
printf " ${ANSI_LIGHT_RED}release tag: %s${ANSI_NOCOLOR}\n" "${RELEASE_TAG_PATTERN}"
printf "::error title=RexExpCheck::Tag name '%s' doesn't conform to regexp '%s' nor '%s'.\n" "${tag}" "${NIGHTLY_TAG_PATTERN}" "${RELEASE_TAG_PATTERN}"
exit 1
fi
elif [[ "${ref:0:10}" == "refs/pull/" ]]; then
ref_kind="pullrequest"
pr_number=${ref:11}
pr_number=${pr_number%%/*}
printf "Pull Request check:\n"
printf " Number: %s\n" "${pr_number}"
else
printf "${ANSI_LIGHT_RED}Unknown Git reference '%s'.${ANSI_NOCOLOR}\n" "${{ github.ref }}"
printf "::error title=Classify Commit::Unknown Git reference '%s'.\n" "${{ github.ref }}"
exit 1
fi
tee --append "${GITHUB_OUTPUT}" <<EOF
on_main_branch=${on_main_branch}
on_dev_branch=${on_dev_branch}
on_release_branch=${on_release_branch}
is_regular_commit=${is_regular_commit}
is_merge_commit=${is_merge_commit}
is_release_commit=${is_release_commit}
is_nightly_tag=${is_nightly_tag}
is_release_tag=${is_release_tag}
ref_kind=${ref_kind}
branch=${branch}
tag=${tag}
pr_number=${pr_number}
version=${version}
EOF
- name: 🔁 Find merged PullRequest from second parent of current SHA (${{ github.sha }})
id: FindPullRequest
if: steps.Classify.outputs.is_merge_commit == 'true'
run: |
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'
export GH_TOKEN=${{ github.token }}
printf "Read second parent of current SHA (%s) ... " "${{ github.ref }}"
FATHER_SHA=$(git rev-parse ${{ github.ref }}^2)
if [[ $? -ne 0 || "{FATHER_SHA}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't read second parent (father) of '%s'.${ANSI_NOCOLOR}\n" "${{ github.ref }}^2"
printf "::error title=GitCommitHistoryError::Couldn't read second parent (father) of '%s'. -> %s\n" "${{ github.ref }}^2" "${FATHER_SHA}"
exit 1
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
fi
printf "Search Pull Request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${FATHER_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${FATHER_SHA}" --state "merged" --json "title,number,mergedBy,mergedAt")
if [[ $? -ne 0 || "${PULL_REQUESTS}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't find a merged Pull Request to '%s'. -> %s${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
printf "::error title=PullRequest::Couldn't find a merged Pull Request to '%s'. -> %s\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
exit 1
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
PR_TITLE="$( printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].title")"
PR_NUMBER="$( printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].number")"
PR_MERGED_BY="$(printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].mergedBy.login")"
PR_MERGED_AT="$(printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].mergedAt")"
printf "${ANSI_LIGHT_BLUE}Found Pull Request:${ANSI_NOCOLOR}\n"
printf " %s\n" "Title: ${PR_TITLE}"
printf " %s\n" "Number: ${PR_NUMBER}"
printf " %s\n" "MergedBy: ${PR_MERGED_BY}"
printf " %s\n" "MergedAt: ${PR_MERGED_AT} ($(date -d"${PR_MERGED_AT}" '+%d.%m.%Y - %H:%M:%S'))"
fi
RELEASE_TAG_PATTERN='^${{ inputs.release_tag_pattern }}$'
printf "Check Pull Request title against regexp '%s' ... " "${RELEASE_TAG_PATTERN}"
if [[ "${PR_TITLE}" =~ $RELEASE_TAG_PATTERN ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
RELEASE_VERSION="${PR_TITLE}"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Pull Request title '%s' doesn't conform to regexp '%s'.${ANSI_NOCOLOR}\n" "${PR_TITLE}" "${RELEASE_TAG_PATTERN}"
printf "::error title=RexExpCheck::Pull Request title '%s' doesn't conform to regexp '%s'.\n" "${PR_TITLE}" "${RELEASE_TAG_PATTERN}"
exit 1
fi
printf "Release tag: ${ANSI_LIGHT_GREEN}%s${ANSI_NOCOLOR}\n" "${RELEASE_VERSION}"
tee --append "${GITHUB_OUTPUT}" <<EOF
pr_version=${RELEASE_VERSION}
pr_title=${PR_TITLE}
pr_number=${PR_NUMBER}
pr_mergedby=${PR_MERGED_BY}
pr_mergedat=${PR_MERGED_AT}
EOF
- name: Debug
run: |
printf "on_main_branch: %s\n" "${{ steps.Classify.outputs.on_main_branch }}"
printf "on_dev_branch: %s\n" "${{ steps.Classify.outputs.on_dev_branch }}"
printf "on_release_branch: %s\n" "${{ steps.Classify.outputs.on_release_branch }}"
printf "is_regular_commit: %s\n" "${{ steps.Classify.outputs.is_regular_commit }}"
printf "is_merge_commit: %s\n" "${{ steps.Classify.outputs.is_merge_commit }}"
printf "is_release_commit: %s\n" "${{ steps.Classify.outputs.is_release_commit }}"
printf "is_nightly_tag: %s\n" "${{ steps.Classify.outputs.is_nightly_tag }}"
printf "is_release_tag: %s\n" "${{ steps.Classify.outputs.is_release_tag }}"
printf "ref_kind: %s\n" "${{ steps.Classify.outputs.ref_kind }}"
printf "branch: %s\n" "${{ steps.Classify.outputs.branch }}"
printf "tag: %s\n" "${{ steps.Classify.outputs.tag }}"
printf "version: %s\n" "${{ steps.Classify.outputs.version || steps.FindPullRequest.outputs.pr_version }}"
printf " from tag: %s\n" "${{ steps.Classify.outputs.version }}"
printf " from pr: %s\n" "${{ steps.FindPullRequest.outputs.pr_version }}"
printf "pr title: %s\n" "${{ steps.FindPullRequest.outputs.pr_title }}"
printf "pr number: %s\n" "${{ steps.Classify.outputs.pr_number || steps.FindPullRequest.outputs.pr_number }}"
printf " from merge: %s\n" "${{ steps.Classify.outputs.pr_number }}"
printf " from pr: %s\n" "${{ steps.FindPullRequest.outputs.pr_number }}"

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -29,6 +29,10 @@ on:
required: false required: false
default: '24.04' default: '24.04'
type: string type: string
coverage_artifacts_pattern:
required: false
default: '*-CodeCoverage-SQLite-*'
type: string
coverage_config: coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.' description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
required: false required: false
@@ -44,20 +48,58 @@ on:
required: false required: false
default: '' default: ''
type: string 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: coverage_json_artifact:
description: 'Name of the JSON coverage artifact.' description: 'Name of the JSON coverage artifact.'
required: false required: false
default: '' default: ''
type: string 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: coverage_html_artifact:
description: 'Name of the HTML coverage artifact.' description: 'Name of the HTML coverage artifact.'
required: false required: false
default: '' default: ''
type: string 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
default: 'false'
type: string
codacy:
description: 'Publish merged coverage report to Codacy.'
required: false
default: 'false'
type: string
secrets: secrets:
codacy_token: CODECOV_TOKEN:
description: 'Token to push result to codacy.' description: 'Token to push result to Codecov.'
required: true required: false
CODACY_TOKEN:
description: 'Token to push result to Codacy.'
required: false
jobs: jobs:
PublishCoverageResults: PublishCoverageResults:
@@ -67,84 +109,30 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Download Artifacts
uses: actions/download-artifact@v4
with: with:
lfs: true
submodules: true
- name: 📥 Download Artifacts
uses: pyTooling/download-artifact@v5
with:
pattern: ${{ inputs.coverage_artifacts_pattern }}
path: artifacts path: artifacts
- name: 🔎 Inspect extracted artifact (tarball)
run: |
tree -pash artifacts
- name: 🔧 Install coverage and tomli - name: 🔧 Install coverage and tomli
run: | run: |
python -m pip install -U --disable-pip-version-check --break-system-packages coverage[toml] tomli python -m pip install -U --disable-pip-version-check --break-system-packages coverage[toml] tomli
- name: 🔁 Extract configurations from pyproject.toml - name: Rename .coverage files and move them all into 'coverage/'
id: getVariables
shell: python
run: | run: |
from os import getenv
from pathlib import Path
from sys import version
from textwrap import dedent
print(f"Python: {version}")
from tomli import load as tomli_load
htmlDirectory = Path("htmlcov")
xmlFile = Path("./coverage.xml")
jsonFile = Path("./coverage.json")
coverageRC = "${{ inputs.coverage_config }}".strip()
# Read output paths from 'pyproject.toml' file
if coverageRC == "pyproject.toml":
pyProjectFile = Path("pyproject.toml")
if pyProjectFile.exists():
with pyProjectFile.open("rb") as file:
pyProjectSettings = tomli_load(file)
htmlDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"])
xmlFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"])
jsonFile = Path(pyProjectSettings["tool"]["coverage"]["json"]["output"])
else:
print(f"File '{pyProjectFile}' not found.")
print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.")
exit(1)
# Read output paths from '.coveragerc' file
elif len(coverageRC) > 0:
coverageRCFile = Path(coverageRC)
if coverageRCFile.exists():
with coverageRCFile.open("rb") as file:
coverageRCSettings = tomli_load(file)
htmlDirectory = Path(coverageRCSettings["html"]["directory"])
xmlFile = Path(coverageRCSettings["xml"]["output"])
jsonFile = Path(coverageRCSettings["json"]["output"])
else:
print(f"File '{coverageRCFile}' not found.")
print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.")
exit(1)
# 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"""\
coverage_report_html_directory={htmlDirectory.as_posix()}
coverage_report_xml={xmlFile}
coverage_report_json={jsonFile}
"""))
print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}\n json={jsonFile}")
- name: Rename .coverage files and collect them all to coverage/
run: |
ls -lAh artifacts/
ls -lAh artifacts/*/.coverage
mkdir -p coverage mkdir -p coverage
find artifacts/ -type f -path "*SQLite*.coverage" -exec sh -c 'cp -v $0 "coverage/$(basename $0).$(basename $(dirname $0))"' {} ';' find artifacts/ -type f -path "*SQLite*.coverage" -exec sh -c 'cp -v $0 "coverage/$(basename $0).$(basename $(dirname $0))"' {} ';'
tree -a coverage tree -pash coverage
- name: Combine SQLite files (using Coverage.py) - name: Combine SQLite files (using Coverage.py)
run: coverage combine --data-file=.coverage coverage/ run: coverage combine --data-file=.coverage coverage/
@@ -153,7 +141,7 @@ jobs:
run: coverage report --rcfile=pyproject.toml --data-file=.coverage run: coverage report --rcfile=pyproject.toml --data-file=.coverage
- name: Convert to XML format (Cobertura) - name: Convert to XML format (Cobertura)
if: inputs.coverage_xml_artifact != '' if: inputs.coverage_xml_artifact != '' || inputs.codecov || inputs.codacy
run: coverage xml --data-file=.coverage run: coverage xml --data-file=.coverage
- name: Convert to JSON format - name: Convert to JSON format
@@ -165,12 +153,12 @@ jobs:
run: | run: |
coverage html --data-file=.coverage -d report/coverage/html coverage html --data-file=.coverage -d report/coverage/html
rm report/coverage/html/.gitignore rm report/coverage/html/.gitignore
tree -a report/coverage/html tree -pash report/coverage/html
- name: 📤 Upload 'Coverage SQLite Database' artifact - name: 📤 Upload 'Coverage SQLite Database' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_sqlite_artifact != '' if: inputs.coverage_sqlite_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_sqlite_artifact }} name: ${{ inputs.coverage_sqlite_artifact }}
path: .coverage path: .coverage
@@ -178,48 +166,69 @@ jobs:
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage XML Report' artifact - name: 📤 Upload 'Coverage XML Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_xml_artifact != '' if: inputs.coverage_xml_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_xml_artifact }} name: ${{ inputs.coverage_xml_artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_xml }} working-directory: ${{ inputs.coverage_report_xml_directory }}
path: ${{ inputs.coverage_report_xml_filename }}
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage JSON Report' artifact - name: 📤 Upload 'Coverage JSON Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_json_artifact != '' if: inputs.coverage_json_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_json_artifact }} name: ${{ inputs.coverage_json_artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_json }} working-directory: ${{ inputs.coverage_report_json_directory }}
path: ${{ inputs.coverage_report_json_filename }}
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage HTML Report' artifact - name: 📤 Upload 'Coverage HTML Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_html_artifact != '' if: inputs.coverage_html_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_html_artifact }} name: ${{ inputs.coverage_html_artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_html_directory }} working-directory: ${{ inputs.coverage_report_html_directory }}
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: 📊 Publish code coverage at CodeCov - name: 📊 Publish code coverage at CodeCov
if: inputs.CodeCov == true uses: codecov/codecov-action@v5
id: codecov
if: inputs.codecov == 'true'
continue-on-error: true continue-on-error: true
uses: codecov/codecov-action@v4
with: with:
files: ${{ steps.getVariables.outputs.coverage_report_xml }} token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
files: ${{ inputs.coverage_report_xml_directory }}/${{ inputs.coverage_report_xml_filename }}
flags: unittests flags: unittests
env_vars: PYTHON env_vars: PYTHON
fail_ci_if_error: true
- name: 📉 Publish code coverage at Codacy - name: 📉 Publish code coverage at Codacy
if: inputs.Codacy == true
continue-on-error: true
uses: codacy/codacy-coverage-reporter-action@v1 uses: codacy/codacy-coverage-reporter-action@v1
id: codacy
if: inputs.codacy == 'true'
continue-on-error: true
with: with:
project-token: ${{ secrets.codacy_token }} project-token: ${{ secrets.CODACY_TOKEN }}
coverage-reports: ${{ steps.getVariables.outputs.coverage_report_xml }} coverage-reports: ${{ inputs.coverage_report_xml_directory }}/${{ inputs.coverage_report_xml_filename }}
- name: Generate error messages
run: |
if [[ "${{ steps.codecov.outcome }}" == "failure" ]]; then
printf "::error title=%s::%s\n" "Publish Code Coverage Results / Codecov" "Failed to publish code coverage results."
else
printf "Codecov: No errors to report.\n"
fi
if [[ "${{ steps.codacy.outcome }}" == "failure" ]]; then
printf "::error title=%s::%s\n" "Publish Code Coverage Results / Codacy" "Failed to publish code coverage results."
else
printf "Codacy: No errors to report.\n"
fi

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -33,7 +33,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
requirements: requirements:
description: 'Python dependencies to be installed through pip.' description: 'Python dependencies to be installed through pip.'
@@ -50,17 +50,16 @@ on:
required: false required: false
jobs: jobs:
PublishOnPyPI: PublishOnPyPI:
name: 🚀 Publish to PyPI name: 🚀 Publish to PyPI
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps: steps:
- name: 📥 Download artifacts '${{ inputs.artifact }}' from 'Package' job - name: 📥 Download artifacts '${{ inputs.artifact }}' from 'Package' job
uses: actions/download-artifact@v4 uses: pyTooling/download-artifact@v5
with: with:
name: ${{ inputs.artifact }} name: ${{ inputs.artifact }}
path: dist/ path: dist
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5

View File

@@ -0,0 +1,831 @@
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# 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: Release
on:
workflow_call:
inputs:
ubuntu_image:
description: 'Name of the Ubuntu image.'
required: false
default: 'ubuntu-24.04'
type: string
release_branch:
description: 'Name of the branch containing releases.'
required: false
default: 'main'
type: string
mode:
description: 'Release mode: nightly or release.'
required: false
default: 'release'
type: string
tag:
description: 'Name of the release (tag).'
required: false
default: ''
type: string
title:
description: 'Title of the release.'
required: false
default: ''
type: string
description:
description: 'Multi-line description of the release.'
required: false
default: ''
type: string
description_file:
description: 'Description of the release from a Markdown file.'
required: false
default: ''
type: string
description_footer:
description: 'Footer line(s) in every release.'
required: false
default: |
--------
Published from [%%gh_workflow_name%%](%%gh_server%%/%%gh_owner_repo%%/actions/runs/%%gh_runid%%) workflow triggered by %%gh_actor%% on %%datetime%%.
This automatic release was created by [pyTooling/Actions](http://github.com/pyTooling/Actions)::Release.yml
type: string
draft:
description: 'Specify if this is a draft.'
required: false
default: false
type: boolean
prerelease:
description: 'Specify if this is a pre-release.'
required: false
default: false
type: boolean
latest:
description: 'Specify if this is the latest release.'
required: false
default: false
type: boolean
replacements:
description: 'Multi-line string containing search=replace patterns.'
required: false
default: ''
type: string
assets:
description: 'Multi-line string containing artifact:file:title asset descriptions.'
required: false
type: string
default: ''
inventory-json:
type: string
required: false
default: ''
inventory-version:
type: string
required: false
default: ''
inventory-categories:
type: string
required: false
default: ''
tarball-name:
type: string
required: false
default: '__pyTooling_upload_artifact__.tar'
can-fail:
type: boolean
required: false
default: false
outputs:
release-page:
description: "URL to the release page."
value: ${{ jobs.Release.outputs.release-page }}
jobs:
Release:
name: 📝 Create or Update Release Page on GitHub
runs-on: ${{ inputs.ubuntu_image }}
continue-on-error: ${{ inputs.can-fail }}
permissions:
contents: write
actions: write
# attestations: write
outputs:
release-page: ${{ steps.removeDraft.outputs.release_page }}
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
# The command 'git describe' (used for version) needs the history.
fetch-depth: 0
- name: 🔧 Install zstd
run: sudo apt-get install -y --no-install-recommends zstd
- name: 📑 Prepare
id: prepare
run: |
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'
printf "Release mode: ${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "${{ inputs.mode }}"
case "${{ inputs.mode }}" in
"release")
;;
"nightly")
printf "→ Allow deletion and recreation of existing release pages for rolling releases (nightly releases)\n"
;;
*)
printf "Unknown mode '%s'\n" "${{ inputs.mode }}"
printf "::error title=%s::%s\n" "InternalError" "Unknown mode '${{ inputs.mode }}'."
exit 1
esac
- name: 📑 Delete (old) Release Page
id: deleteReleasePage
if: inputs.mode == 'nightly'
run: |
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'
export GH_TOKEN=${{ github.token }}
printf "Deleting release '%s' ... " "${{ inputs.tag }}"
message="$(gh release delete ${{ inputs.tag }} --yes 2>&1)"
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
elif [[ "${message}" == "release not found" ]]; then
printf "${ANSI_LIGHT_YELLOW}[NOT FOUND]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't delete release '%s' -> Error: '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.tag }}" "${message}"
printf "::error title=%s::%s\n" "InternalError" "Couldn't delete release '${{ inputs.tag }}' -> Error: '${message}'."
exit 1
fi
- name: 📑 Assemble Release Notes
id: createReleaseNotes
run: |
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'
export GH_TOKEN=${{ github.token }}
# Save release description (from parameter in a file)
head -c -1 <<'EOF' > __DESCRIPTION__.md
${{ inputs.description }}
EOF
# Save release footer (from parameter in a file)
head -c -1 <<'EOF' > __FOOTER__.md
${{ inputs.description_footer }}
EOF
# Download Markdown from PullRequest
# Readout second parent's SHA
# Search PR with that SHA
# Load description of that PR
printf "Read second parent of current SHA (%s) ... " "${{ github.ref }}"
FATHER_SHA=$(git rev-parse ${{ github.ref }}^2 -- 2> /dev/null)
if [[ $? -ne 0 || "{FATHER_SHA}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "→ ${ANSI_LIGHT_YELLOW}Skipped readout of pull request description. This is not a merge commit.${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf "Search Pull Request to '%s' and branch containing SHA %s ... " "${{ inputs.release_branch }}" "${FATHER_SHA}"
PULL_REQUESTS=$(gh pr list --base "${{ inputs.release_branch }}" --search "${FATHER_SHA}" --state "merged" --json "title,number,mergedBy,mergedAt,body")
if [[ $? -ne 0 || "${PULL_REQUESTS}" == "" ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf "${ANSI_LIGHT_RED}Couldn't find a merged Pull Request to '%s'. -> %s${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
printf "::error title=PullRequest::Couldn't find a merged Pull Request to '%s'. -> %s\n" "${{ inputs.release_branch }}" "${PULL_REQUESTS}"
exit 1
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
PR_TITLE="$( printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].title")"
PR_NUMBER="$( printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].number")"
PR_BODY="$( printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].body")"
PR_MERGED_BY="$(printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].mergedBy.login")"
PR_MERGED_AT="$(printf "%s\n" "${PULL_REQUESTS}" | jq --raw-output ".[0].mergedAt")"
printf "Found Pull Request:\n"
printf " %s\n" "Title: ${PR_TITLE}"
printf " %s\n" "Number: ${PR_NUMBER}"
printf " %s\n" "MergedBy: ${PR_MERGED_BY}"
printf " %s\n" "MergedAt: ${PR_MERGED_AT} ($(date -d"${PR_MERGED_AT}" '+%d.%m.%Y - %H:%M:%S'))"
fi
echo "${PR_BODY}" > __PULLREQUEST__.md
fi
# Check if a release description file should be used and exists.
if [[ "${{ inputs.description_file }}" != "" ]]; then
if [[ ! -f "${{ inputs.description_file }}" ]]; then
printf "${ANSI_LIGHT_RED}Release description file '%s' not found.${ANSI_NOCOLOR}\n" "${{ inputs.description_file }}"
printf "::error title=%s::%s\n" "FileNotFound" "Release description file '${{ inputs.description_file }}' not found."
exit 1
elif [[ -s "${{ inputs.description_file }}" ]]; then
printf "Use '%s' as main release description.\n" "${{ inputs.description_file }}"
cp -v "${{ inputs.description_file }}" __NOTES__.md
else
printf "${ANSI_LIGHT_RED}Release description file '%s' is empty.${ANSI_NOCOLOR}\n" "${{ inputs.description_file }}"
printf "::error title=%s::%s\n" "FileNotFound" "Release description file '${{ inputs.description_file }}' is empty."
exit 1
fi
# Check if the main release description is provided by a template parameter
elif [[ -s __DESCRIPTION__.md ]]; then
printf "Use '__DESCRIPTION__.md' as main release description.\n"
mv -v __DESCRIPTION__.md __NOTES__.md
# Check if the pull request serves as the main release description text.
elif [[ -s __PULLREQUEST__.md ]]; then
printf "Use '__PULLREQUEST__.md' as main release description.\n"
mv -v __PULLREQUEST__.md __NOTES__.md
printf "Append '%%%%FOOTER%%%%' to '__NOTES__.md'.\n"
printf "\n%%%%FOOTER%%%%\n" >> __NOTES__.md
else
printf "${ANSI_LIGHT_RED}No release description specified (file, parameter, PR text).${ANSI_NOCOLOR}\n"
printf "::error title=%s::%s\n" "MissingDescription" "No release description specified (file, parameter, PR text)."
exit 1
fi
# Read release notes main file for placeholder substitution
NOTES=$(<__NOTES__.md)
# Inline description
if [[ -s __DESCRIPTION__.md ]]; then
NOTES="${NOTES//%%DESCRIPTION%%/$(<__DESCRIPTION__.md)}"
else
NOTES="${NOTES//%%DESCRIPTION%%/}"
fi
# Inline PullRequest and increase headline levels
if [[ -s __PULLREQUEST__.md ]]; then
while [[ "${NOTES}" =~ %%(PULLREQUEST(\+[0-3])?)%% ]]; do
case "${BASH_REMATCH[1]}" in
"PULLREQUEST+0" | "PULLREQUEST")
NOTES="${NOTES//${BASH_REMATCH[0]}/$(<__PULLREQUEST__.md)}"
;;
"PULLREQUEST+1")
NOTES="${NOTES//${BASH_REMATCH[0]}/$(cat __PULLREQUEST__.md | sed -E 's/^(#+) /\1# /gm;t')}"
;;
"PULLREQUEST+2")
NOTES="${NOTES//${BASH_REMATCH[0]}/$(cat __PULLREQUEST__.md | sed -E 's/^(#+) /\1### /gm;t')}"
;;
"PULLREQUEST+3")
NOTES="${NOTES//${BASH_REMATCH[0]}/$(cat __PULLREQUEST__.md | sed -E 's/^(#+) /\1### /gm;t')}"
;;
esac
done
else
while [[ "${NOTES}" =~ %%(PULLREQUEST(\+[0-3])?)%% ]]; do
NOTES="${NOTES//${BASH_REMATCH[0]}/}"
done
fi
# inline Footer
if [[ -s __FOOTER__.md ]]; then
NOTES="${NOTES//%%FOOTER%%/$(<__FOOTER__.md)}"
else
NOTES="${NOTES//%%FOOTER%%/}"
fi
# Apply replacements
while IFS=$'\r\n' read -r patternLine; do
# skip empty lines
[[ "$patternLine" == "" ]] && continue
pattern="%${patternLine%%=*}%"
replacement="${patternLine#*=}"
NOTES="${NOTES//$pattern/$replacement}"
done <<<'${{ inputs.replacements }}'
# Workarounds for stupid GitHub variables
owner_repo="${{ github.repository }}"
repo=${owner_repo##*/}
# Replace special identifiers
NOTES="${NOTES//%%gh_server%%/${{ github.server_url }}}"
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_pages%%/https://${{ github.repository_owner }}.github.io/${repo}/}"
NOTES="${NOTES//%%gh_runid%%/${{ github.run_id }}}"
NOTES="${NOTES//%%gh_actor%%/${{ github.actor }}}"
NOTES="${NOTES//%%gh_sha%%/${{ github.sha }}}"
NOTES="${NOTES//%%date%%/$(date '+%Y-%m-%d')}"
NOTES="${NOTES//%%time%%/$(date '+%H:%M:%S %Z')}"
NOTES="${NOTES//%%datetime%%/$(date '+%Y-%m-%d %H:%M:%S %Z')}"
# Write final release notes to file
echo "${NOTES}" > __NOTES__.md
# Display partial contents for debugging
if [[ -s __DESCRIPTION__.md ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '__DESCRIPTION__.md' ($(stat --printf="%s" "__DESCRIPTION__.md") B) ...."
cat __DESCRIPTION__.md
printf "::endgroup::\n"
else
printf "${ANSI_LIGHT_YELLOW}No '__DESCRIPTION__.md' found.${ANSI_NOCOLOR}\n"
fi
if [[ -s __PULLREQUEST__.md ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '__PULLREQUEST__.md' ($(stat --printf="%s" "__PULLREQUEST__.md") B) ...."
cat __PULLREQUEST__.md
printf "::endgroup::\n"
else
printf "${ANSI_LIGHT_YELLOW}No '__PULLREQUEST__.md' found.${ANSI_NOCOLOR}\n"
fi
if [[ -s __FOOTER__.md ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '__FOOTER__.md' ($(stat --printf="%s" "__FOOTER__.md") B) ...."
cat __FOOTER__.md
printf "::endgroup::\n"
else
printf "${ANSI_LIGHT_YELLOW}No '__FOOTER__.md' found.${ANSI_NOCOLOR}\n"
fi
# Print final release notes
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '__NOTES__.md' ($(stat --printf="%s" "__NOTES__.md") B) ...."
cat __NOTES__.md
printf "::endgroup::\n"
- name: 📑 Create new Release Page
id: createReleasePage
if: inputs.mode == 'release'
run: |
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'
export GH_TOKEN=${{ github.token }}
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
addPreRelease="--prerelease"
fi
if [[ "${{ inputs.latest }}" == "false" ]]; then
addLatest="--latest=false"
fi
if [[ "${{ inputs.title }}" != "" ]]; then
addTitle=("--title" "${{ inputs.title }}")
fi
if [[ -s __NOTES__.md ]]; then
addNotes=("--notes-file" "__NOTES__.md")
fi
printf "Creating release '%s' ... " "${{ inputs.tag }}"
message="$(gh release create "${{ inputs.tag }}" --verify-tag --draft $addPreRelease $addLatest "${addTitle[@]}" "${addNotes[@]}" 2>&1)"
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf " Release page: %s\n" "${message}"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't create release '%s' -> Error: '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.tag }}" "${message}"
printf "::error title=%s::%s\n" "InternalError" "Couldn't create release '${{ inputs.tag }}' -> Error: '${message}'."
exit 1
fi
- name: 📑 Recreate Release Page
id: recreateReleasePage
if: inputs.mode == 'nightly'
run: |
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'
export GH_TOKEN=${{ github.token }}
addDraft="--draft"
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
addPreRelease="--prerelease"
fi
if [[ "${{ inputs.latest }}" == "false" ]]; then
addLatest="--latest=false"
fi
if [[ "${{ inputs.title }}" != "" ]]; then
addTitle=("--title" "${{ inputs.title }}")
fi
if [[ -s __NOTES__.md ]]; then
addNotes=("--notes-file" "__NOTES__.md")
fi
printf "Creating release '%s' ... " "${{ inputs.tag }}"
message="$(gh release create "${{ inputs.tag }}" --verify-tag --draft $addPreRelease $addLatest "${addTitle[@]}" "${addNotes[@]}" 2>&1)"
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf " Release page: %s\n" "${message}"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't recreate release '%s' -> Error: '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.tag }}" "${message}"
printf "::error title=%s::%s\n" "InternalError" "Couldn't recreate release '${{ inputs.tag }}' -> Error: '${message}'."
exit 1
fi
- name: 📥 Download artifacts and upload as assets
id: uploadAssets
run: |
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'
export GH_TOKEN=${{ github.token }}
Replace() {
line="$1"
while IFS=$'\r\n' read -r patternLine; do
# skip empty lines
[[ "$patternLine" == "" ]] && continue
pattern="${patternLine%%=*}"
replacement="${patternLine#*=}"
line="${line//"%$pattern%"/"$replacement"}"
done <<<'${{ inputs.replacements }}'
printf "%s\n" "$line"
}
# Create JSON inventory
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
VERSION="1.0"
# Split categories by ',' into a Bash array.
# See https://stackoverflow.com/a/45201229/3719459
if [[ "${{ inputs.inventory-categories }}" != "" ]]; then
readarray -td, inventoryCategories <<<"${{ inputs.inventory-categories }},"
unset 'inventoryCategories[-1]'
declare -p inventoryCategories
else
inventoryCategories=""
fi
jsonInventory=$(jq -c -n \
--arg version "${VERSION}" \
--arg date "$(date +"%Y-%m-%dT%H-%M-%S%:z")" \
--argjson jsonMeta "$(jq -c -n \
--arg tag "${{ inputs.tag }}" \
--arg version "${{ inputs.inventory-version }}" \
--arg hash "${{ github.sha }}" \
--arg repo "${{ github.server_url }}/${{ github.repository }}" \
--arg release "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ inputs.tag }}" \
--argjson categories "$(jq -c -n \
'$ARGS.positional' \
--args "${inventoryCategories[@]}" \
)" \
'{"tag": $tag, "version": $version, "git-hash": $hash, "repository-url": $repo, "release-url": $release, "categories": $categories}' \
)" \
'{"version": 1.0, "timestamp": $date, "meta": $jsonMeta, "files": {}}'
)
fi
ERRORS=0
# A dictionary of 0/1 to avoid duplicate downloads
declare -A downloadedArtifacts
# A dictionary to check for duplicate asset files in release
declare -A assetFilenames
# A dictionary of SHA256 checksums
declare -A sha256Checksums
while IFS=$'\r\n' read -r assetLine; do
if [[ "${assetLine}" == "" || "${assetLine:0:1}" == "#" ]]; then
continue
fi
# split assetLine colon separated triple: artifact:asset:title
artifact="${assetLine%%:*}"
assetLine="${assetLine#*:}"
asset="${assetLine%%:*}"
assetLine="${assetLine#*:}"
if [[ "${{ inputs.inventory-json }}" == "" ]]; then
categories=""
title="${assetLine##*:}"
else
categories="${assetLine%%:*}"
title="${assetLine##*:}"
fi
# remove leading whitespace
asset="${asset#"${asset%%[![:space:]]*}"}"
categories="${categories#"${categories%%[![:space:]]*}"}"
title="${title#"${title%%[![:space:]]*}"}"
# apply replacements
asset="$(Replace "${asset}")"
title="$(Replace "${title}")"
printf "${ANSI_LIGHT_BLUE}Publish asset '%s' from artifact '%s' with title '%s'${ANSI_NOCOLOR}\n" "${asset}" "${artifact}" "${title}"
printf " Checked asset for duplicates ... "
if [[ -n "${assetFilenames[$asset]}" ]]; then
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf "::error title=%s::%s\n" "DuplicateAsset" "Asset '${asset}' from artifact '${artifact}' was already uploaded to release '${{ inputs.tag }}'."
ERRORS=$((ERRORS + 1))
continue
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
assetFilenames[$asset]=1
fi
# Download artifact by artifact name
if [[ -n "${downloadedArtifacts[$artifact]}" ]]; then
printf " downloading artifact '%s' ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}\n" "${artifact}"
else
echo " downloading '${artifact}' ... "
printf " gh run download $GITHUB_RUN_ID --dir \"%s\" --name \"%s\" " "${artifact}" "${artifact}"
gh run download $GITHUB_RUN_ID --dir "${artifact}" --name "${artifact}"
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't download artifact '%s'.${ANSI_NOCOLOR}\n" "${artifact}"
printf "::error title=%s::%s\n" "ArtifactNotFound" "Couldn't download artifact '${artifact}'."
ERRORS=$((ERRORS + 1))
continue
fi
downloadedArtifacts[$artifact]=1
printf " Checking for embedded tarball ... "
if [[ -f "${artifact}/${{ inputs.tarball-name }}" ]]; then
printf "${ANSI_LIGHT_GREEN}[FOUND]${ANSI_NOCOLOR}\n"
pushd "${artifact}" > /dev/null
printf " Extracting embedded tarball ... "
tar -xf "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
fi
printf " Removing temporary tarball ... "
rm -f "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
fi
popd > /dev/null
else
printf "${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}\n"
fi
fi
# Check if artifact should be compressed (zip, tgz) or if asset was part of the downloaded artifact.
printf " checking asset '%s' ... " "${artifact}/${asset}"
if [[ "${asset}" == !*.zip ]]; then
printf "${ANSI_LIGHT_GREEN}[ZIP]${ANSI_NOCOLOR}\n"
asset="${asset##*!}"
printf "::group:: %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
zip -r "../${asset}" *
)
retCode=$?
printf "::endgroup::\n"
if [[ $retCode -eq 0 ]]; then
printf " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
uploadFile="${asset}"
else
printf " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't compress '%s' to zip file '%s'.${ANSI_NOCOLOR}\n" "${artifact}" "${asset}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to zip file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ "${asset}" == !*.tgz || "${asset}" == !*.tar.gz || "${asset}" == \$*.tgz || "${asset}" == \$*.tar.gz ]]; then
printf "${ANSI_LIGHT_GREEN}[TAR/GZ]${ANSI_NOCOLOR}\n"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
printf " Compressing artifact '%s' to '%s' ...\n" "${artifact}" "${asset}"
tar -c --gzip --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
retCode=$?
else
asset="${asset##*!}"
printf " Compressing artifact '%s' to '%s' ...\n" "${artifact}" "${asset}"
(
cd "${artifact}" && \
tar -c --gzip --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
printf " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
uploadFile="${asset}"
else
printf " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't compress '%s' to tgz file '%s'.${ANSI_NOCOLOR}\n" "${artifact}" "${asset}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to tgz file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ "${asset}" == !*.tzst || "${asset}" == !*.tar.zst || "${asset}" == \$*.tzst || "${asset}" == \$*.tar.zst ]]; then
printf "${ANSI_LIGHT_GREEN}[ZST]${ANSI_NOCOLOR}\n"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
printf " Compressing artifact '%s' to '%s' ...\n" "${artifact}" "${asset}"
tar -c --zstd --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
retCode=$?
else
asset="${asset##*!}"
printf " Compressing artifact '%s' to '%s' ...\n" "${artifact}" "${asset}"
(
cd "${artifact}" && \
tar -c --zstd --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
printf " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
uploadFile="${asset}"
else
printf " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't compress '%s' to zst file '%s'.${ANSI_NOCOLOR}\n" "${artifact}" "${asset}"
printf "::error title=%s::%s\n" "CompressionError" "Couldn't compress '${artifact}' to zst file '${asset}'."
ERRORS=$((ERRORS + 1))
continue
fi
elif [[ -e "${artifact}/${asset}" ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
uploadFile="${artifact}/${asset}"
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't find asset '%s' in artifact '%s'.${ANSI_NOCOLOR}\n" "${asset}" "${artifact}"
printf "::error title=%s::%s\n" "FileNotFound" "Couldn't find asset '${asset}' in artifact '${artifact}'."
ERRORS=$((ERRORS + 1))
continue
fi
printf " compute SHA256 checksum of '${uploadFile}' ... "
sha256=$(sha256sum -b ${uploadFile} | cut -d " " -f1)
sha256Checksums[$asset]="sha256:${sha256}"
printf "${ANSI_LIGHT_BLUE}${sha256}${ANSI_NOCOLOR}\n"
# Add asset to JSON inventory
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
if [[ "${categories}" != "${title}" ]]; then
printf " adding file '%s' with '%s' to JSON inventory ...\n" "${uploadFile#*/}" "${categories//;/ → }"
category=""
jsonEntry=$(jq -c -n \
--arg title "${title}" \
--arg sha256 "${sha256}" \
--arg file "${uploadFile#*/}" \
'{"file": $file, "sha256": $sha256, "title": $title}' \
)
while [[ "${categories}" != "${category}" ]]; do
category="${categories##*,}"
categories="${categories%,*}"
jsonEntry=$(jq -c -n --arg cat "${category}" --argjson value "${jsonEntry}" '{$cat: $value}')
done
jsonInventory=$(jq -c -n \
--argjson inventory "${jsonInventory}" \
--argjson file "${jsonEntry}" \
'$inventory * {"files": $file}' \
)
else
printf " adding file '%s' to JSON inventory ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}\n" "${uploadFile#*/}"
fi
fi
# Upload asset to existing release page
printf " uploading asset '%s' from '%s' with title '%s' ... " "${asset}" "${uploadFile}" "${title}"
gh release upload ${{ inputs.tag }} "${uploadFile}#${title}" --clobber
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf " checking assets SHA256 checksum ... \n"
ghSHA256=$(gh release view --json assets --jq ".assets[] | select(.name == \"${asset}\") | .digest" ${{ inputs.tag }})
if [[ "${ghSHA256}" == "${sha256Checksums[$asset]}" ]]; then
printf "${ANSI_LIGHT_GREEN}[PASSED]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}SHA256 checksum compare failed.${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Local: %s${ANSI_NOCOLOR}\n" "${sha256Checksums[$asset]}"
printf " ${ANSI_LIGHT_RED}GitHub: %s${ANSI_NOCOLOR}\n" "${ghSHA256}"
printf "::error title=%s::%s\n" "ChecksumError" "SHA256 checksum compare failed. Local=${sha256Checksums[$asset]} GitHub=${ghSHA256}"
ERRORS=$((ERRORS + 1))
continue
fi
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't upload asset '%s' from '%s' to release '%s'.${ANSI_NOCOLOR}\n" "${asset}" "${uploadFile}" "${{ inputs.tag }}"
printf "::error title=%s::%s\n" "UploadError" "Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.tag }}'."
ERRORS=$((ERRORS + 1))
continue
fi
done <<<'${{ inputs.assets }}'
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
inventoryTitle="Release Inventory (JSON)"
printf "Publish asset '%s' with title '%s'\n" "${{ inputs.inventory-json }}" "${inventoryTitle}"
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Writing JSON inventory to '${{ inputs.inventory-json }}' ...."
printf "%s\n" "$(jq -n --argjson inventory "${jsonInventory}" '$inventory')" > "${{ inputs.inventory-json }}"
cat "${{ inputs.inventory-json }}"
printf "::endgroup::\n"
# Upload inventory asset to existing release page
printf " uploading asset '%s' title '%s' ... " "${{ inputs.inventory-json }}" "${inventoryTitle}"
gh release upload ${{ inputs.tag }} "${{ inputs.inventory-json }}#${inventoryTitle}" --clobber
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't upload asset '%s' to release '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.inventory-json }}" "${{ inputs.tag }}"
printf "::error title=%s::%s\n" "UploadError" "Couldn't upload asset '${{ inputs.inventory-json }}' to release '${{ inputs.tag }}'."
ERRORS=$((ERRORS + 1))
continue
fi
fi
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Inspecting downloaded artifacts ..."
tree -pash -L 3 .
printf "::endgroup::\n"
if [[ $ERRORS -ne 0 ]]; then
printf "${ANSI_LIGHT_RED}%s errors detected in previous steps.${ANSI_NOCOLOR}\n" "${ERRORS}"
exit 1
fi
- name: 📑 Remove draft state from Release Page
id: removeDraft
if: ${{ ! inputs.draft }}
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
# Remove draft-state from release page
printf "Remove draft-state from release '%s' ... " "${title}"
releasePage=$(gh release edit --draft=false "${{ inputs.tag }}")
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf " Release page: %s\n" "${releasePage}"
printf "release_page=%s\n" "${releasePage}" >> "${GITHUB_OUTPUT}"
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't remove draft-state from release '%s'.${ANSI_NOCOLOR}\n" "${{ inputs.tag }}"
printf "::error title=%s::%s\n" "ReleasePage" "Couldn't remove draft-state from release '${{ inputs.tag }}'."
fi

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -30,21 +30,69 @@ on:
required: false required: false
default: '24.04' default: '24.04'
type: string type: string
unittest_artifacts_pattern:
required: false
default: '*-UnitTestReportSummary-XML-*'
type: string
merged_junit_filename:
description: 'Filename of the merged JUnit Test Summary.'
required: false
default: 'Unittesting.xml'
type: string
merged_junit_artifact: merged_junit_artifact:
description: 'Name of the merged JUnit Test Summary artifact.' description: 'Name of the merged JUnit Test Summary artifact.'
required: false required: false
default: '' default: ''
type: string type: string
merge-input-dialect:
description: 'JUnit dialect used to load and parse inputs for merging.'
required: false
default: 'pyTest-JUnit'
type: string
merge-output-dialect:
description: 'JUnit dialect used for writing the merged report.'
required: false
default: 'pyTest-JUnit'
type: string
additional_merge_args: additional_merge_args:
description: 'Additional merging arguments.' description: 'Additional merging arguments.'
required: false required: false
default: '"--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit"' default: '"--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit"'
type: string type: string
testsuite-summary-name:
description: 'Set TestsuiteSummary name.'
required: false
default: ''
type: string
publish:
description: 'Publish test report summary via Dorny Test-Reporter'
required: false
default: 'true'
type: string
report_title: report_title:
description: 'Title of the summary report in the pipeline''s sidebar' description: 'Title of the summary report in the pipeline''s sidebar'
required: false required: false
default: 'Unit Test Results' default: 'Unit Test Results'
type: string type: string
dorny:
description: 'Publish merged unittest results via Dorny Test-Reporter.'
required: false
default: 'true'
type: string
codecov:
description: 'Publish merged unittest results to Codecov.'
required: false
default: 'false'
type: string
codecov_flags:
description: 'Flags applied to the upload to Codecov'
required: false
default: 'unittest'
type: string
secrets:
CODECOV_TOKEN:
description: 'Token to push result to Codecov.'
required: false
jobs: jobs:
PublishTestResults: PublishTestResults:
@@ -54,42 +102,86 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Download Artifacts - name: 📥 Download Artifacts
uses: actions/download-artifact@v4 uses: pyTooling/download-artifact@v5
with: with:
path: artifacts pattern: ${{ inputs.unittest_artifacts_pattern }}
path: artifacts
- name: 🔎 Inspect extracted artifact (tarball)
run: |
tree -pash artifacts
- name: 🔧 Install pyEDAA.Reports (JUunit Parser and Merger) - name: 🔧 Install pyEDAA.Reports (JUunit Parser and Merger)
run: | run: |
python -m pip install --disable-pip-version-check --break-system-packages -U pyEDAA.Reports python -m pip install --disable-pip-version-check --break-system-packages -U pyEDAA.Reports
- name: Move JUnit files and collect them all to junit/ - name: Rename JUnit files and move them all into 'junit/'
run: | run: |
mkdir -p junit mkdir -p junit
ls -lAh artifacts/*/*.xml find artifacts/ -type f -path "*.xml" -exec sh -c 'cp -v $0 "junit/$(basename $(dirname $0)).$(basename $0)"' {} ';'
find artifacts/ -type f -path "*TestReportSummary*.xml" -exec sh -c 'cp -v $0 "junit/$(basename $(dirname $0)).$(basename $0)"' {} ';' tree -pash junit
tree -a junit
- name: 🔁 Merge JUnit Unit Test Summaries - name: 🔁 Merge JUnit Unit Test Summaries
run: | run: |
pyedaa-reports -v unittest "--merge=pyTest-JUnit:junit/*.xml" ${{ inputs.additional_merge_args }} "--output=pyTest-JUnit:Unittesting.xml" if [[ -n "${{ inputs.testsuite-summary-name }}" ]]; then
echo "cat Unittesting.xml" name="\"--name=${{ inputs.testsuite-summary-name }}\""
cat Unittesting.xml fi
pyedaa-reports -v unittest $name "--merge=${{ inputs.merge-input-dialect }}:junit/*.xml" ${{ inputs.additional_merge_args }} "--output=${{ inputs.merge-output-dialect }}:${{ inputs.merged_junit_filename }}"
printf "%s\n" "cat ${{ inputs.merged_junit_filename }}"
cat ${{ inputs.merged_junit_filename }}
- name: 📊 Publish Unit Test Results - name: 📊 Publish Unit Test Results
uses: dorny/test-reporter@v1 uses: dorny/test-reporter@v2
id: test-reporter
if: ( inputs.dorny == 'true' || inputs.publish == 'true' ) && inputs.report_title != ''
continue-on-error: true
with: with:
name: ${{ inputs.report_title }} name: ${{ inputs.report_title }}
path: Unittesting.xml path: ${{ inputs.merged_junit_filename }}
reporter: java-junit reporter: java-junit
- name: 📊 Publish unittest results at CodeCov
uses: codecov/test-results-action@v1
id: codecov
if: inputs.codecov == 'true'
continue-on-error: true
with:
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
files: ${{ inputs.merged_junit_filename }}
flags: ${{ inputs.codecov_flags }}
fail_ci_if_error: true
- name: 📤 Upload merged 'JUnit Test Summary' artifact - name: 📤 Upload merged 'JUnit Test Summary' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.merged_junit_artifact != '' if: inputs.merged_junit_artifact != ''
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.merged_junit_artifact }} name: ${{ inputs.merged_junit_artifact }}
path: Unittesting.xml path: ${{ inputs.merged_junit_filename }}
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
investigate: true
- name: Generate error messages
run: |
exitCode=0
if [[ "${{ steps.test-reporter.outcome }}" == "failure" ]]; then
printf "❌ Dorney/Test-Reporter: %s\n" "Failed to publish unittest results."
printf "::error title=%s::%s\n" "Dorney/Test-Reporter" "Failed to publish unittest results."
exitCode=1
else
printf "✅ Dorney/Test-Reporter: No errors to report.\n"
fi
if [[ "${{ steps.codecov.outcome }}" == "failure" ]]; then
printf "❌ CodeCov: %s\n" "Failed to publish unittest and code coverage results."
printf "::error title=%s::%s\n" "CodeCov" "Failed to publish unittest and code coverage results."
exitCode=1
else
printf "✅ CodeCov: No errors to report.\n"
fi
exit $exitCode

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -53,24 +53,24 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 📥 Download artifacts '${{ inputs.doc }}' from 'BuildTheDocs' job - name: 📥 Download artifacts '${{ inputs.doc }}' from 'SphinxDocumentation' job
uses: actions/download-artifact@v4 uses: pyTooling/download-artifact@v5
with: with:
name: ${{ inputs.doc }} name: ${{ inputs.doc }}
path: public path: public
- name: 📥 Download artifacts '${{ inputs.coverage }}' from 'Coverage' job - name: 📥 Download artifacts '${{ inputs.coverage }}' from 'Coverage' job
uses: pyTooling/download-artifact@v5
if: ${{ inputs.coverage != '' }} if: ${{ inputs.coverage != '' }}
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.coverage }} name: ${{ inputs.coverage }}
path: public/coverage path: public/coverage
- name: 📥 Download artifacts '${{ inputs.typing }}' from 'StaticTypeCheck' job - name: 📥 Download artifacts '${{ inputs.typing }}' from 'StaticTypeCheck' job
uses: pyTooling/download-artifact@v5
if: ${{ inputs.typing != '' }} if: ${{ inputs.typing != '' }}
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.typing }} name: ${{ inputs.typing }}
path: public/typing path: public/typing

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann # # Patrick Lehmann #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -32,7 +32,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
requirements: requirements:
description: 'Python dependencies to be installed through pip.' description: 'Python dependencies to be installed through pip.'
@@ -46,7 +46,7 @@ on:
type: string type: string
coverage_report_json_directory: coverage_report_json_directory:
description: '' description: ''
required: true required: false
type: string type: string
coverage_json_artifact: coverage_json_artifact:
description: 'Name of the coverage JSON artifact.' description: 'Name of the coverage JSON artifact.'
@@ -81,7 +81,10 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🔧 Install graphviz - name: 🔧 Install graphviz
run: sudo apt-get install -y --no-install-recommends graphviz run: sudo apt-get install -y --no-install-recommends graphviz
@@ -97,18 +100,20 @@ jobs:
python -m pip install --disable-pip-version-check ${{ inputs.requirements }} python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job - name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job
uses: pyTooling/download-artifact@v5
if: inputs.unittest_xml_artifact != '' if: inputs.unittest_xml_artifact != ''
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.unittest_xml_artifact }} name: ${{ inputs.unittest_xml_artifact }}
path: ${{ inputs.unittest_xml_directory }} path: ${{ inputs.unittest_xml_directory }}
investigate: true
- name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job - name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job
uses: pyTooling/download-artifact@v5
if: inputs.coverage_json_artifact != '' if: inputs.coverage_json_artifact != ''
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.coverage_json_artifact }} name: ${{ inputs.coverage_json_artifact }}
path: ${{ inputs.coverage_report_json_directory }} path: ${{ inputs.coverage_report_json_directory }}
investigate: true
- name: ☑ Generate HTML documentation - name: ☑ Generate HTML documentation
if: inputs.html_artifact != '' if: inputs.html_artifact != ''
@@ -119,12 +124,13 @@ jobs:
sphinx-build -v -n -b html -d _build/doctrees -j $(nproc) -w _build/html.log . _build/html sphinx-build -v -n -b html -d _build/doctrees -j $(nproc) -w _build/html.log . _build/html
- name: 📤 Upload 'HTML Documentation' artifact - name: 📤 Upload 'HTML Documentation' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.html_artifact != '' if: inputs.html_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.html_artifact }} name: ${{ inputs.html_artifact }}
path: ${{ inputs.doc_directory }}/_build/html working-directory: ${{ inputs.doc_directory }}/_build/html
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
@@ -134,7 +140,10 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🔧 Install graphviz - name: 🔧 Install graphviz
run: sudo apt-get install -y --no-install-recommends graphviz run: sudo apt-get install -y --no-install-recommends graphviz
@@ -150,18 +159,20 @@ jobs:
python -m pip install --disable-pip-version-check ${{ inputs.requirements }} python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job - name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job
uses: pyTooling/download-artifact@v5
if: inputs.unittest_xml_artifact != '' if: inputs.unittest_xml_artifact != ''
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.unittest_xml_artifact }} name: ${{ inputs.unittest_xml_artifact }}
path: ${{ inputs.unittest_xml_directory }} path: ${{ inputs.unittest_xml_directory }}
investigate: true
- name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job - name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job
uses: pyTooling/download-artifact@v5
if: inputs.coverage_json_artifact != '' if: inputs.coverage_json_artifact != ''
uses: actions/download-artifact@v4
with: with:
name: ${{ inputs.coverage_json_artifact }} name: ${{ inputs.coverage_json_artifact }}
path: ${{ inputs.coverage_report_json_directory }} path: ${{ inputs.coverage_report_json_directory }}
investigate: true
- name: ☑ Generate LaTeX documentation - name: ☑ Generate LaTeX documentation
if: inputs.latex_artifact != '' if: inputs.latex_artifact != ''
@@ -173,12 +184,95 @@ jobs:
sphinx-build -v -n -b latex -d _build/doctrees -j $(nproc) -w _build/latex.log . _build/latex sphinx-build -v -n -b latex -d _build/doctrees -j $(nproc) -w _build/latex.log . _build/latex
# --builder html --doctree-dir _build/doctrees --verbose --fresh-env --write-all --nitpicky --warning-file _build/html.log . _build/html # --builder html --doctree-dir _build/doctrees --verbose --fresh-env --write-all --nitpicky --warning-file _build/html.log . _build/html
- name: Workaround I - https://github.com/sphinx-doc/sphinx/issues/13190
if: inputs.latex_artifact != ''
run: |
printf "Changing directory to '${{ inputs.doc_directory || '.' }}/_build/latex' ...\n"
cd ${{ inputs.doc_directory || '.' }}/_build/latex
MIMETYPE_EXTENSIONS=(
"image/png:png"
"image/jpeg:jpg"
"image/svg+xml:svg"
)
printf "Changing file extension according to MIME type ...\n"
while IFS=$'\n' read -r file; do
printf " Checking '%s' ... " "${file}"
mime="$(file --mime-type -b "${file}")"
printf "[%s]\n" "${mime}"
found=0
for MIME in "${MIMETYPE_EXTENSIONS[@]}"; do
mimetype="${MIME%%:*}"
extension="${MIME#*:}"
if [[ "${mime}" == "${mimetype}" && "${file##*.}" != "${extension}" ]]; then
printf " Rename file to '%s' " "${file}.${extension}"
mv "${file}" "${file}.${extension}"
if [[ $? -eq 0 ]]; then
printf "[OK]\n"
else
printf "[FAILED]\n"
fi
printf " Patching LaTeX file for '%s' " "${file}"
sed -i "s:{{${file%.*}}\.${file##*.}}:{{${file}}.${extension}}:g" *.tex
if [[ $? -eq 0 ]]; then
printf "[OK]\n"
else
printf "[FAILED]\n"
fi
found=1
break
fi
done
if [[ $found -eq 0 ]]; then
printf "[SKIPPED]\n"
fi
done < <(find . -type f -not -iname "*.cls" -not -iname "*.sty" -not -iname "*.xdy" -not -iname "*.svg" -not -iname "*.png" -not -iname "*.jpg" | sed 's:./::')
- name: Workaround II - https://github.com/sphinx-doc/sphinx/issues/13189
if: inputs.latex_artifact != ''
run: |
printf "Changing directory to '${{ inputs.doc_directory || '.' }}/_build/latex' ...\n"
cd ${{ inputs.doc_directory || '.' }}/_build/latex
printf "Searching for downloaded images, that need normalization ...\n"
for imageExt in png svg jpg jpeg; do
printf " Processing '%s' ...\n" "${imageExt}"
while IFS=$'\n' read -r imageFile; do
newFile="${imageFile//%/_}";
printf " %s\n" "$imageFile";
if [[ "${imageFile}" != "${newFile}" ]]; then
printf " Rename file to '%s' " "${newFile}"
mv "${imageFile}" "${newFile}"
if [[ $? -eq 0 ]]; then
printf "[OK]\n"
else
printf "[FAILED]\n"
fi
printf " Patching LaTeX file for '%s' " "${newFile}"
sed -i "s:{{${imageFile%.*}}\.${imageFile##*.}}:{{${newFile%.*}}.${newFile##*.}}:g" *.tex
if [[ $? -eq 0 ]]; then
printf "[OK]\n"
else
printf "[FAILED]\n"
fi
fi
done < <(find . -type f -iname "*.$imageExt" | sed 's:./::')
done
- name: 📤 Upload 'LaTeX Documentation' artifact - name: 📤 Upload 'LaTeX Documentation' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.latex_artifact != '' if: inputs.latex_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.latex_artifact }} name: ${{ inputs.latex_artifact }}
path: ${{ inputs.doc_directory }}/_build/latex working-directory: ${{ inputs.doc_directory }}/_build/latex
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -33,7 +33,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
requirements: requirements:
description: 'Python dependencies to be installed through pip.' description: 'Python dependencies to be installed through pip.'
@@ -72,7 +72,7 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }} - name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v5
@@ -87,19 +87,20 @@ jobs:
run: ${{ inputs.commands }} run: ${{ inputs.commands }}
- name: 📤 Upload 'Static Typing Report' HTML artifact - name: 📤 Upload 'Static Typing Report' HTML artifact
uses: pyTooling/upload-artifact@v4
if: ${{ inputs.html_artifact != '' }} if: ${{ inputs.html_artifact != '' }}
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.html_artifact }} name: ${{ inputs.html_artifact }}
path: ${{ inputs.html_report }} working-directory: ${{ inputs.html_report }}
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Static Typing Report' JUnit artifact - name: 📤 Upload 'Static Typing Report' JUnit artifact
uses: pyTooling/upload-artifact@v4
if: ${{ inputs.junit_artifact != '' }} if: ${{ inputs.junit_artifact != '' }}
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.junit_artifact }} name: ${{ inputs.junit_artifact }}
path: ${{ inputs.junit_report }} path: ${{ inputs.junit_report }}

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -20,74 +20,65 @@
# # # #
# SPDX-License-Identifier: Apache-2.0 # # SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== # # ==================================================================================================================== #
name: Release name: Auto Tag
on: on:
workflow_call: workflow_call:
inputs: inputs:
ubuntu_image_version: ubuntu_image:
description: 'Ubuntu image version.' description: 'Name of the Ubuntu image.'
required: false required: false
default: '24.04' default: 'ubuntu-24.04'
type: string
version:
description: 'Version used as tag name.'
required: true
type: string
auto_tag:
description: 'Automatically add and push a tag.'
required: true
type: string
workflow:
description: 'Workflow to start after adding a tag.'
required: false
default: 'Pipeline.yml'
type: string type: string
jobs: jobs:
Release: AutoTag:
name: 📝 Create 'Release Page' on GitHub name: "🏷 Create tag '${{ inputs.version}}' on GitHub"
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}" runs-on: ${{ inputs.ubuntu_image }}
if: inputs.auto_tag == 'true'
# if: github.ref == 'refs/heads/${{ inputs.release_branch }}'
permissions:
contents: write # required for tag creation
actions: write # required to start a new pipeline
steps: steps:
- name: 🔁 Extract Git tag from GITHUB_REF - name: 🏷 Create release tag '${{ steps.FindPullRequest.outputs.version }}'
id: getVariables uses: actions/github-script@v7
run: | id: createReleaseTag
GIT_TAG=${GITHUB_REF#refs/*/} # if: inputs.auto_tag == 'true'
RELEASE_VERSION=${GIT_TAG#v}
RELEASE_DATETIME="$(date --utc '+%d.%m.%Y - %H:%M:%S')"
# write to step outputs
echo "gitTag=${GIT_TAG}" >> $GITHUB_OUTPUT
echo "version=${RELEASE_VERSION}" >> $GITHUB_OUTPUT
echo "datetime=${RELEASE_DATETIME}" >> $GITHUB_OUTPUT
- name: 📑 Create Release Page
id: createReleasePage
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with: with:
tag_name: ${{ steps.getVariables.outputs.gitTag }} script: |
# release_name: ${{ steps.getVariables.outputs.gitTag }} github.rest.git.createRef({
body: | owner: context.repo.owner,
**Automated Release created on: ${{ steps.getVariables.outputs.datetime }}** repo: context.repo.repo,
ref: 'refs/tags/${{ inputs.version }}',
sha: context.sha
})
# New Features - name: Trigger Workflow
uses: actions/github-script@v7
* tbd id: runReleaseTag
* tbd # if: inputs.auto_tag == 'true'
with:
# Changes script: |
github.rest.actions.createWorkflowDispatch({
* tbd owner: context.repo.owner,
* tbd repo: context.repo.repo,
workflow_id: '${{ inputs.workflow }}',
# Bug Fixes ref: '${{ inputs.version }}'
})
* tbd
* tbd
# Documentation
* tbd
* tbd
# Unit Tests
* tbd
* tbd
----------
# Related Issues and Pull-Requests
* tbd
* tbd
draft: true
prerelease: false

View File

@@ -3,7 +3,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -45,7 +45,7 @@ jobs:
env: env:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Build container image - name: Build container image
run: docker build -t ghcr.io/pytooling/releaser -f releaser/Dockerfile releaser run: docker build -t ghcr.io/pytooling/releaser -f releaser/Dockerfile releaser
@@ -62,9 +62,9 @@ jobs:
Composite: Composite:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt - run: printf "%s\n" "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
- name: Single - name: Single
uses: ./releaser/composite uses: ./releaser/composite
@@ -84,7 +84,7 @@ jobs:
- name: Add artifacts/*.txt - name: Add artifacts/*.txt
run: | run: |
mkdir artifacts mkdir artifacts
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt printf "%s\n" "Build some tool and generate some artifacts" > artifacts/artifact.txt
touch artifacts/empty_file.txt touch artifacts/empty_file.txt
- name: Single in subdir - name: Single in subdir
@@ -95,8 +95,8 @@ jobs:
- name: Add artifacts/*.md - name: Add artifacts/*.md
run: | run: |
echo "releaser hello" > artifacts/hello.md printf "%s\n" "releaser hello" > artifacts/hello.md
echo "releaser world" > artifacts/world.md printf "%s\n" "releaser world" > artifacts/world.md
- name: Directory wildcard - name: Directory wildcard
uses: ./releaser/composite uses: ./releaser/composite
@@ -107,7 +107,7 @@ jobs:
- name: Add artifacts/subdir - name: Add artifacts/subdir
run: | run: |
mkdir artifacts/subdir mkdir artifacts/subdir
echo "Test recursive glob" > artifacts/subdir/deep_file.txt printf "%s\n" "Test recursive glob" > artifacts/subdir/deep_file.txt
- name: Directory wildcard (recursive) - name: Directory wildcard (recursive)
uses: ./releaser/composite uses: ./releaser/composite
@@ -122,9 +122,9 @@ jobs:
- Composite - Composite
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt - run: printf "%s\n" "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
- name: Single - name: Single
uses: ./releaser uses: ./releaser
@@ -144,7 +144,7 @@ jobs:
- name: Add artifacts/*.txt - name: Add artifacts/*.txt
run: | run: |
mkdir artifacts mkdir artifacts
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt printf "%s\n" "Build some tool and generate some artifacts" > artifacts/artifact.txt
touch artifacts/empty_file.txt touch artifacts/empty_file.txt
- name: Single in subdir - name: Single in subdir
@@ -155,8 +155,8 @@ jobs:
- name: Add artifacts/*.md - name: Add artifacts/*.md
run: | run: |
echo "releaser hello" > artifacts/hello.md printf "%s\n" "releaser hello" > artifacts/hello.md
echo "releaser world" > artifacts/world.md printf "%s\n" "releaser world" > artifacts/world.md
- name: Directory wildcard - name: Directory wildcard
uses: ./releaser uses: ./releaser
@@ -167,7 +167,7 @@ jobs:
- name: Add artifacts/subdir - name: Add artifacts/subdir
run: | run: |
mkdir artifacts/subdir mkdir artifacts/subdir
echo "Test recursive glob" > artifacts/subdir/deep_file.txt printf "%s\n" "Test recursive glob" > artifacts/subdir/deep_file.txt
- name: Directory wildcard (recursive) - name: Directory wildcard (recursive)
uses: ./releaser uses: ./releaser

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -94,11 +94,26 @@ on:
required: false required: false
default: 'unit' default: 'unit'
type: string type: string
unittest_report_xml_directory:
description: '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'
type: string
coverage_config: coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.' description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
required: false required: false
default: 'pyproject.toml' default: 'pyproject.toml'
type: string type: string
coverage_report_html_directory:
description: ''
required: false
default: 'report/coverage/html'
type: string
unittest_xml_artifact: unittest_xml_artifact:
description: "Generate unit test report with junitxml and upload results as an artifact." description: "Generate unit test report with junitxml and upload results as an artifact."
required: false required: false
@@ -146,7 +161,10 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
with:
lfs: true
submodules: true
# Package Manager steps # Package Manager steps
- name: 🔧 Install homebrew dependencies on macOS - name: 🔧 Install homebrew dependencies on macOS
@@ -207,14 +225,14 @@ jobs:
packages = { packages = {
"coverage": "python-coverage:p", "coverage": "python-coverage:p",
"docstr_coverage": "python-pyaml:p", "docstr_coverage": "python-pyaml:p python-types-pyyaml:p",
"igraph": "igraph:p", "igraph": "igraph:p",
"jinja2": "python-markupsafe:p", "jinja2": "python-markupsafe:p",
"lxml": "python-lxml:p", "lxml": "python-lxml:p",
"numpy": "python-numpy:p", "numpy": "python-numpy:p",
"markupsafe": "python-markupsafe:p", "markupsafe": "python-markupsafe:p",
"pip": "python-pip:p", "pip": "python-pip:p",
"pyyaml": "python-pyyaml:p", "pyyaml": "python-pyyaml:p python-types-pyyaml:p",
"ruamel.yaml": "python-ruamel-yaml:p", "ruamel.yaml": "python-ruamel-yaml:p",
# "ruamel.yaml": "python-ruamel-yaml:p python-ruamel.yaml.clib:p", # "ruamel.yaml": "python-ruamel-yaml:p python-ruamel.yaml.clib:p",
"sphinx": "python-markupsafe:p", "sphinx": "python-markupsafe:p",
@@ -222,6 +240,7 @@ jobs:
"wheel": "python-wheel:p", "wheel": "python-wheel:p",
"pyedaa.projectmodel": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p", "pyedaa.projectmodel": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p",
"pyedaa.reports": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p", "pyedaa.reports": "python-ruamel-yaml:p python-ruamel.yaml.clib:p python-lxml:p",
"sphinx-reports": "python-markupsafe:p python-pyaml:p python-types-pyyaml:p",
} }
subPackages = { subPackages = {
"pytooling": { "pytooling": {
@@ -264,8 +283,8 @@ jobs:
# Python setup # Python setup
- name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}' - name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}'
if: matrix.system == 'msys2'
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
if: matrix.system == 'msys2'
with: with:
msystem: ${{ matrix.runtime }} msystem: ${{ matrix.runtime }}
update: true update: true
@@ -274,8 +293,8 @@ jobs:
${{ inputs.pacboy }} ${{ inputs.pacboy }}
- name: 🐍 Setup Python ${{ matrix.python }} - name: 🐍 Setup Python ${{ matrix.python }}
if: matrix.system != 'msys2'
uses: actions/setup-python@v5 uses: actions/setup-python@v5
if: matrix.system != 'msys2'
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
@@ -320,95 +339,36 @@ jobs:
if: matrix.system == 'msys2' && matrix.runtime == 'UCRT64' && inputs.ucrt64_before_script != '' if: matrix.system == 'msys2' && matrix.runtime == 'UCRT64' && inputs.ucrt64_before_script != ''
run: ${{ inputs.ucrt64_before_script }} run: ${{ inputs.ucrt64_before_script }}
# Read pyproject.toml
- name: 🔁 Extract configurations from pyproject.toml
id: getVariables
shell: python
run: |
from os import getenv
from pathlib import Path
from sys import version
from textwrap import dedent
print(f"Python: {version}")
from tomli import load as tomli_load
htmlDirectory = Path("htmlcov")
xmlFile = Path("./coverage.xml")
jsonFile = Path("./coverage.json")
coverageRC = "${{ inputs.coverage_config }}".strip()
# Read output paths from 'pyproject.toml' file
if coverageRC == "pyproject.toml":
pyProjectFile = Path("pyproject.toml")
if pyProjectFile.exists():
with pyProjectFile.open("rb") as file:
pyProjectSettings = tomli_load(file)
htmlDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"])
xmlFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"])
jsonFile = Path(pyProjectSettings["tool"]["coverage"]["json"]["output"])
else:
print(f"File '{pyProjectFile}' not found.")
print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.")
exit(1)
# Read output paths from '.coveragerc' file
elif len(coverageRC) > 0:
coverageRCFile = Path(coverageRC)
if coverageRCFile.exists():
with coverageRCFile.open("rb") as file:
coverageRCSettings = tomli_load(file)
htmlDirectory = Path(coverageRCSettings["html"]["directory"])
xmlFile = Path(coverageRCSettings["xml"]["output"])
jsonFile = Path(coverageRCSettings["json"]["output"])
else:
print(f"File '{coverageRCFile}' not found.")
print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.")
exit(1)
# 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_html_directory={htmlDirectory}
coverage_report_html_directory={htmlDirectory.as_posix()}
coverage_report_xml={xmlFile}
coverage_report_json={jsonFile}
"""))
print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}\n json={jsonFile}")
# Run pytests # Run pytests
- name: ✅ Run unit tests (Ubuntu/macOS) - name: ✅ Run unit tests (Ubuntu/macOS)
id: pytest_bash
if: matrix.system != 'windows' if: matrix.system != 'windows'
continue-on-error: true
run: | run: |
export ENVIRONMENT_NAME="${{ matrix.envname }}" export ENVIRONMENT_NAME="${{ matrix.envname }}"
export PYTHONPATH=$(pwd) export PYTHONPATH=$(pwd)
cd "${{ inputs.root_directory || '.' }}" cd "${{ inputs.root_directory || '.' }}"
[ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS [ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}' || unset PYTEST_ARGS
if [ -n '${{ inputs.coverage_config }}' ]; then if [ -n '${{ inputs.coverage_config }}' ]; then
echo "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" 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 }} coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}
else else
echo "python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" printf "%s\n" "python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}"
python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }} python -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}
fi fi
- name: ✅ Run unit tests (Windows) - name: ✅ Run unit tests (Windows)
id: pytest_posh
if: matrix.system == 'windows' if: matrix.system == 'windows'
continue-on-error: true
run: | run: |
$env:ENVIRONMENT_NAME = "${{ matrix.envname }}" $env:ENVIRONMENT_NAME = "${{ matrix.envname }}"
$env:PYTHONPATH = (Get-Location).ToString() $env:PYTHONPATH = (Get-Location).ToString()
cd "${{ inputs.root_directory || '.' }}" cd "${{ inputs.root_directory || '.' }}"
$PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=report/unit/TestReportSummary.xml" } else { "" } $PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}" } else { "" }
if ("${{ inputs.coverage_config }}") { 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 }}" 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 }} coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}
@@ -418,38 +378,42 @@ jobs:
} }
- name: Convert coverage to XML format (Cobertura) - name: Convert coverage to XML format (Cobertura)
id: convert_xml
if: inputs.coverage_xml_artifact != '' if: inputs.coverage_xml_artifact != ''
continue-on-error: true continue-on-error: true
run: coverage xml --data-file=.coverage run: coverage xml --data-file=.coverage
- name: Convert coverage to JSON format - name: Convert coverage to JSON format
id: convert_json
if: inputs.coverage_json_artifact != '' if: inputs.coverage_json_artifact != ''
continue-on-error: true continue-on-error: true
run: coverage json --data-file=.coverage run: coverage json --data-file=.coverage
- name: Convert coverage to HTML format - name: Convert coverage to HTML format
id: convert_html
if: inputs.coverage_html_artifact != '' if: inputs.coverage_html_artifact != ''
continue-on-error: true continue-on-error: true
run: | run: |
coverage html --data-file=.coverage -d ${{ steps.getVariables.outputs.coverage_report_html_directory }} coverage html --data-file=.coverage -d ${{ inputs.coverage_report_html_directory }}
rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore rm ${{ inputs.coverage_report_html_directory }}/.gitignore
# Upload artifacts # Upload artifacts
- name: 📤 Upload 'TestReportSummary.xml' artifact - name: 📤 Upload '${{ inputs.unittest_report_xml_filename }}' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.unittest_xml_artifact != '' if: inputs.unittest_xml_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.unittest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.unittest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: report/unit/TestReportSummary.xml working-directory: ${{ inputs.unittest_report_xml_directory }}
path: ${{ inputs.unittest_report_xml_filename }}
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
# - name: 📤 Upload 'Unit Tests HTML Report' artifact # - name: 📤 Upload 'Unit Tests HTML Report' artifact
# if: inputs.unittest_html_artifact != '' # if: inputs.unittest_html_artifact != ''
# continue-on-error: true # continue-on-error: true
# uses: actions/upload-artifact@v4 # uses: pyTooling/upload-artifact@v4
# with: # with:
# name: ${{ inputs.unittest_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} # name: ${{ inputs.unittest_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
# path: ${{ steps.getVariables.outputs.unittest_report_html_directory }} # path: ${{ steps.getVariables.outputs.unittest_report_html_directory }}
@@ -459,7 +423,7 @@ jobs:
- name: 📤 Upload 'Coverage SQLite Database' artifact - name: 📤 Upload 'Coverage SQLite Database' artifact
if: inputs.coverage_sqlite_artifact != '' if: inputs.coverage_sqlite_artifact != ''
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_sqlite_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.coverage_sqlite_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: .coverage path: .coverage
@@ -468,9 +432,9 @@ jobs:
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage XML Report' artifact - name: 📤 Upload 'Coverage XML Report' artifact
if: inputs.coverage_xml_artifact != '' if: inputs.coverage_xml_artifact != '' && steps.convert_xml.outcome == 'success'
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.coverage_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: ${{ steps.getVariables.outputs.coverage_report_xml }} path: ${{ steps.getVariables.outputs.coverage_report_xml }}
@@ -478,9 +442,9 @@ jobs:
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage JSON Report' artifact - name: 📤 Upload 'Coverage JSON Report' artifact
if: inputs.coverage_json_artifact != '' if: inputs.coverage_json_artifact != '' && steps.convert_json.outcome == 'success'
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_json_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.coverage_json_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: ${{ steps.getVariables.outputs.coverage_report_json }} path: ${{ steps.getVariables.outputs.coverage_report_json }}
@@ -488,11 +452,26 @@ jobs:
retention-days: 1 retention-days: 1
- name: 📤 Upload 'Coverage HTML Report' artifact - name: 📤 Upload 'Coverage HTML Report' artifact
if: inputs.coverage_html_artifact != '' if: inputs.coverage_html_artifact != '' && steps.convert_html.outcome == 'success'
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ inputs.coverage_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} name: ${{ inputs.coverage_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: ${{ steps.getVariables.outputs.coverage_report_html_directory }} working-directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
path: '*'
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
- name: Generate error messages
shell: bash
run: |
exitCode=0
if [[ "${{ steps.pytest_bash.outcome }}" == "failure" || "${{ steps.pytest_posh.outcome }}" == "failure" ]]; then
printf "❌ pytest: %s\n" "Error in pytest execution."
printf "::error title=%s::%s\n" "pytest" "Error in pytest execution."
exitCode=1
else
printf "✅ pytest: No errors.\n"
fi
exit $exitCode

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -33,7 +33,7 @@ on:
python_version: python_version:
description: 'Python version.' description: 'Python version.'
required: false required: false
default: '3.12' default: '3.13'
type: string type: string
jobs: jobs:
@@ -44,7 +44,7 @@ jobs:
steps: steps:
- name: ⏬ Checkout repository - name: ⏬ Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: 🐍 Setup Python - name: 🐍 Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
Params: Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.12 3.13" python_version_list: "3.12 3.13"
@@ -22,10 +22,10 @@ jobs:
include: ${{ fromJson(needs.Params.outputs.python_jobs) }} include: ${{ fromJson(needs.Params.outputs.python_jobs) }}
steps: steps:
- name: Content creation for ${{ matrix.system }}-${{ matrix.python }} - name: Content creation for ${{ matrix.system }}-${{ matrix.python }}
run: echo "${{ matrix.runs-on }}-${{ matrix.python }}" >> artifact.txt run: printf "%s\n" "${{ matrix.runs-on }}-${{ matrix.python }}" >> artifact.txt
- name: 📤 Upload artifact for ${{ matrix.system }}-${{ matrix.python }} - name: 📤 Upload artifact for ${{ matrix.system }}-${{ matrix.python }}
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-${{ matrix.system }}-${{ matrix.python }} name: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-${{ matrix.system }}-${{ matrix.python }}
path: artifact.txt path: artifact.txt
@@ -39,10 +39,10 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Package creation - name: Package creation
run: echo "Package" >> package.txt run: printf "%s\n" "Package" >> package.txt
- name: 📤 Upload artifact for ${{ matrix.system }}-${{ matrix.python }} - name: 📤 Upload artifact for ${{ matrix.system }}-${{ matrix.python }}
uses: actions/upload-artifact@v4 uses: pyTooling/upload-artifact@v4
with: with:
name: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }} name: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
path: package.txt path: package.txt
@@ -50,7 +50,7 @@ jobs:
retention-days: 1 retention-days: 1
ArtifactCleanUp: ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r2 uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r5
needs: needs:
- Params - Params
- Testing - Testing

View File

@@ -5,67 +5,72 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r5
ConfigParams: ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r2 uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r5
needs:
- DocCoverage
with: with:
package_name: pyDummy package_name: pyDummy
InstallParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with:
package_name: pyDummy
python_version_list: ''
UnitTestingParams: UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: pyDummy name: pyDummy
python_version_list: "3.9 3.10 3.11 3.12 3.13 pypy-3.9 pypy-3.10" 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" # disable_list: "windows:pypy-3.10"
PlatformTestingParams: PlatformTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Platform name: Platform
python_version_list: "" python_version_list: ""
system_list: "ubuntu windows macos mingw32 mingw64 clang64 ucrt64" system_list: "ubuntu windows macos mingw64 clang64 ucrt64"
UnitTesting: UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r2 uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
with: with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }}
unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }} unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }}
# coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} 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_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
# coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} # coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
# coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} # coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
PlatformTesting: PlatformTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r2 uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r5
needs: needs:
- ConfigParams
- PlatformTestingParams - PlatformTestingParams
with: with:
jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }} jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }}
# tests_directory: "" # tests_directory: ""
unittest_directory: platform unittest_directory: platform
unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }} unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }}
unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }} unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }}
coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }} coverage_report_html_directory: ${{ needs.ConfigParams.outputs.coverage_report_html_directory }}
coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }} unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }} unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}
coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }} coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }}
coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}
# Coverage: coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}
# uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r2 coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}
# needs:
# - UnitTestingParams
# with:
# python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
# artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
# secrets:
# codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
StaticTypeCheck: StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r2 uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r5
needs: needs:
- ConfigParams - ConfigParams
- UnitTestingParams - UnitTestingParams
@@ -73,147 +78,193 @@ jobs:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
commands: | commands: |
${{ needs.ConfigParams.outputs.mypy_prepare_command }} ${{ needs.ConfigParams.outputs.mypy_prepare_command }}
mypy --html-report htmlmypy -p ${{ needs.ConfigParams.outputs.package_fullname }} mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }}
html_report: 'htmlmypy' html_report: 'report/typing'
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
DocCoverage: DocCoverage:
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r1 uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
directory: sphinx_reports directory : ${{ needs.ConfigParams.outputs.package_directory }}
# fail_below: 70 # fail_below: 70
Package: Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r2 uses: pyTooling/Actions/.github/workflows/Package.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- UnitTesting # - UnitTesting
# - Coverage # - PlatformTesting
- PlatformTesting
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
Install:
uses: pyTooling/Actions/.github/workflows/InstallPackage.yml@r5
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 }}
PublishCoverageResults: PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams - UnitTestingParams
- UnitTesting - UnitTesting
- PlatformTesting - PlatformTesting
# - Coverage
with: with:
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} coverage_report_xml_directory: ${{ needs.ConfigParams.outputs.coverage_report_xml_directory }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} coverage_report_xml_filename: ${{ needs.ConfigParams.outputs.coverage_report_xml_filename }}
secrets: coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }} 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
secrets: inherit
PublishTestResults: PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r5
needs: needs:
- ConfigParams
- UnitTestingParams
- UnitTesting - UnitTesting
- PlatformTesting - PlatformTesting
with: with:
additional_merge_args: '-d "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit;reduce-depth:pytest.tests.platform"' additional_merge_args: '-d "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit;reduce-depth:pytest.tests.platform"'
testsuite-summary-name: ${{ needs.ConfigParams.outputs.package_fullname }}
merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
codecov: true
dorny: true
secrets: inherit
# VerifyDocs: # VerifyDocs:
# uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r2 # uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r5
# needs: # needs:
# - UnitTestingParams # - UnitTestingParams
# with: # with:
# python_version: ${{ needs.UnitTestingParams.outputs.python_version }} # python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
Documentation: Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r2 uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r5
needs: needs:
- UnitTestingParams
- ConfigParams - ConfigParams
- UnitTestingParams
- PublishTestResults - PublishTestResults
- PublishCoverageResults - PublishCoverageResults
# - VerifyDocs # - VerifyDocs
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }} coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }}
# unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
# coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
IntermediateCleanUp: IntermediateCleanUp:
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r1 uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- PublishCoverageResults - PublishCoverageResults
- PublishTestResults - PublishTestResults
- Documentation
with: with:
sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}- sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}-
xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}- xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-
PDFDocumentation: PDFDocumentation:
uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@r2 uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- Documentation - Documentation
with: with:
document: actions document: Actions
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }} 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: PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- Documentation - Documentation
# - PDFDocumentation - PDFDocumentation
# - Coverage
- PublishCoverageResults - PublishCoverageResults
- StaticTypeCheck - StaticTypeCheck
with: with:
doc: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} doc: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
coverage: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} coverage: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
ReleasePage: TriggerTaggedRelease:
uses: pyTooling/Actions/.github/workflows/Release.yml@r2 uses: pyTooling/Actions/.github/workflows/TagReleaseCommit.yml@r5
if: startsWith(github.ref, 'refs/tags')
needs: needs:
- Prepare
- UnitTesting - UnitTesting
- PlatformTesting - PlatformTesting
# - Coverage - Install
# - StaticTypeCheck # - StaticTypeCheck
- Package - Package
- PublishToGitHubPages - PublishToGitHubPages
permissions:
contents: write # required for create tag
actions: write # required for trigger workflow
with:
version: ${{ needs.Prepare.outputs.version }}
auto_tag: ${{ needs.Prepare.outputs.is_release_commit }}
secrets: inherit
ReleasePage:
uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@r5
needs:
- Prepare
- UnitTesting
- PlatformTesting
- Install
# - StaticTypeCheck
- Package
- PublishToGitHubPages
if: needs.Prepare.outputs.is_release_tag == 'true'
permissions:
contents: write
actions: write
with:
tag: ${{ needs.Prepare.outputs.version }}
secrets: inherit
PublishOnPyPI: PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r2 uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r5
if: startsWith(github.ref, 'refs/tags')
needs: needs:
- UnitTestingParams - UnitTestingParams
- ReleasePage - ReleasePage
# - Package # - Package
if: needs.Prepare.outputs.is_release_tag == 'true'
with: with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }} python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
requirements: -r dist/requirements.txt requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
secrets: secrets: inherit
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
ArtifactCleanUp: ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r2 uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r5
needs: needs:
- UnitTestingParams - UnitTestingParams
- PlatformTestingParams - PlatformTestingParams
- UnitTesting - UnitTesting
# - Coverage
- StaticTypeCheck - StaticTypeCheck
- PlatformTesting - PlatformTesting
- Documentation - Documentation
# - PDFDocumentation - PDFDocumentation
- PublishTestResults - PublishTestResults
- PublishCoverageResults - PublishCoverageResults
- PublishToGitHubPages - PublishToGitHubPages
@@ -223,7 +274,6 @@ jobs:
remaining: | remaining: |
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-* ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }}-* ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}-* ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}-* ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}-* ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}-*
@@ -235,9 +285,9 @@ jobs:
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }} ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }} ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }} ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}-* ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}-*

View File

@@ -1,4 +1,4 @@
name: Verification of Pipeline Templates name: Verification of Pipeline Templates (Namespace Package)
on: on:
push: push:
@@ -6,7 +6,14 @@ on:
jobs: jobs:
NamespacePackage: NamespacePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r2 uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r5
with: with:
package_namespace: pyExamples package_namespace: pyExamples
package_name: Extensions package_name: Extensions
codecov: true
codacy: true
dorny: true
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}

121
.github/workflows/_Checking_Nightly.yml vendored Normal file
View File

@@ -0,0 +1,121 @@
name: Verification of Nightly Releases
on:
push:
workflow_dispatch:
jobs:
Build:
name: Build something
runs-on: ubuntu-24.04
steps:
- name: 🖉 Build 1
run: |
printf "%s\n" "Document 1 $(date --utc '+%d.%m.%Y - %H:%M:%S')" > document1.txt
printf "%s\n" "Analysis log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > analysis.log
printf "%s\n" "Build log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > build.log
- name: 📤 Upload artifact
uses: pyTooling/upload-artifact@v4
with:
name: document
path: |
document1.txt
*.log
if-no-files-found: error
retention-days: 1
- name: 🖉 Program
run: |
printf "%s\n" "Document other $(date --utc '+%d.%m.%Y - %H:%M:%S')" > document1.txt
printf "%s\n" "Program $(date --utc '+%d.%m.%Y - %H:%M:%S')" > program.py
- name: 📤 Upload artifact
uses: actions/upload-artifact@v4
with:
name: other
path: |
*.txt
*.py
if-no-files-found: error
retention-days: 1
NightlyPage:
uses: pyTooling/Actions/.github/workflows/NightlyRelease.yml@r5
needs:
- Build
permissions:
contents: write
actions: write
with:
can-fail: true
prerelease: true
replacements: |
version=4.2.0
tool=myTool
prog=program
nightly_title: "Nightly Test Release"
nightly_description: |
This *nightly* release contains all latest and important artifacts created by %tool%'s CI pipeline.
# %tool% %version%
* %prog%
assets: |
document: document1.txt: Documentation
document: build.log: Logfile - %tool% - %tool%
other: document1.txt: SBOM - %version%
other: %prog%.py: Application - %tool% - %version%
document:!archive1.zip: Archive 1 - zip
document:!archive2.tgz: Archive 2 - tgz
document:!archive3.tar.gz: Archive 3 - tar.gz
document:!archive4.tzst: Archive 4 - tzst
document:!archive5.tar.zst:Archive 5 - tar.zst
document:$archive6.tgz: Archive 6 - tgz + dir
document:$archive7.tar.gz: Archive 7 - tar.gz + dir
document:$archive8.tzst: Archive 8 - tzst + dir
document:$archive9.tar.zst:Archive 9 - tar.zst + dir
secrets: inherit
NightlyPageWithInventory:
uses: ./.github/workflows/NightlyRelease.yml
needs:
- Build
permissions:
contents: write
actions: write
with:
can-fail: true
replacements: |
version=4.2.0
tool=myTool
prog=program
nightly_name: inventory
nightly_title: "Nightly Test Release with Inventory"
nightly_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-version: 4.2.5
inventory-categories: "kind1,kind2"
assets: |
# artifact: file: labels: asset title
document: document1.txt: doc,html: Documentation
document: build.log: build,log: Logfile - %tool% - %tool%
other: document1.txt: build,SBOM:SBOM - %version%
other: %prog%.py: app,binary:Application - %tool% - %version%
document:!archive1.zip: Archive 1 - zip
document:!archive2.tgz: Archive 2 - tgz
document:!archive3.tar.gz: Archive 3 - tar.gz
document:!archive4.tzst: Archive 4 - tzst
document:!archive5.tar.zst: Archive 5 - tar.zst
document:$archive6.tgz: Archive 6 - tgz + dir
document:$archive7.tar.gz: Archive 7 - tar.gz + dir
document:$archive8.tzst: Archive 8 - tzst + dir
document:$archive9.tar.zst: Archive 9 - tar.zst + dir
secrets: inherit

View File

@@ -6,24 +6,24 @@ on:
jobs: jobs:
Params_Default: Params_Default:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
Params_PythonVersions: Params_PythonVersions:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.11 3.12 pypy-3.9 pypy-3.10" python_version_list: "3.11 3.12 pypy-3.9 pypy-3.10"
Params_Systems: Params_Systems:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
system_list: "windows mingw32 mingw64" system_list: "windows mingw32 mingw64"
Params_Include: Params_Include:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.11" python_version_list: "3.11"
@@ -31,7 +31,7 @@ jobs:
include_list: "ubuntu:3.12 ubuntu:3.13" include_list: "ubuntu:3.12 ubuntu:3.13"
Params_Exclude: Params_Exclude:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.12" python_version_list: "3.12"
@@ -39,7 +39,7 @@ jobs:
exclude_list: "windows:3.12 windows:3.13" exclude_list: "windows:3.12 windows:3.13"
Params_Disable: Params_Disable:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.12" python_version_list: "3.12"
@@ -47,7 +47,7 @@ jobs:
disable_list: "windows:3.12 windows:3.13" disable_list: "windows:3.12 windows:3.13"
Params_All: Params_All:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r2 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r5
with: with:
name: Example name: Example
python_version_list: "3.12 3.13" python_version_list: "3.12 3.13"
@@ -83,7 +83,7 @@ jobs:
expectedPythonVersion = "3.13" expectedPythonVersion = "3.13"
expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"] expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"] expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.12", "ucrt64:3.11"]
expectedName = "Example" expectedName = "Example"
expectedArtifacts = { expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
@@ -146,7 +146,7 @@ jobs:
expectedPythonVersion = "3.13" expectedPythonVersion = "3.13"
expectedPythons = ["3.11", "3.12", "pypy-3.9", "pypy-3.10"] expectedPythons = ["3.11", "3.12", "pypy-3.9", "pypy-3.10"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"] expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"] expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.12", "ucrt64:3.11"]
expectedName = "Example" expectedName = "Example"
expectedArtifacts = { expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
@@ -209,7 +209,7 @@ jobs:
expectedPythonVersion = "3.13" expectedPythonVersion = "3.13"
expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"] expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"]
expectedSystems = ["windows"] expectedSystems = ["windows"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw32:3.11", "mingw64:3.11"] expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw32:3.12", "mingw64:3.11"]
expectedName = "Example" expectedName = "Example"
expectedArtifacts = { expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML", "unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",

View File

@@ -1,4 +1,4 @@
name: Verification of Pipeline Templates name: Verification of Pipeline Templates (Simple Package)
on: on:
push: push:
@@ -6,6 +6,14 @@ on:
jobs: jobs:
SimplePackage: SimplePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r2 uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r5
with: with:
package_name: pyDummy package_name: pyDummy
codecov: true
codacy: true
dorny: true
cleanup: false
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}

2
.gitignore vendored
View File

@@ -31,7 +31,7 @@ doc/pyDummy/**/*.*
# BuildTheDocs # BuildTheDocs
doc/_theme/**/*.* doc/_theme/**/*.*
# IntelliJ project files # PyCharm project files
/.idea/workspace.xml /.idea/workspace.xml
# Git files # Git files

View File

@@ -1,178 +0,0 @@
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# Unai Martinez-Corral #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 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: Pipeline
on:
workflow_dispatch:
jobs:
# This job is a workaround for global variables
# See https://github.com/actions/runner/issues/480
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: ToolName
# Optional
system_list: 'ubuntu windows msys2 macos'
python_version: '3.10'
python_version_list: '3.8 3.9 3.10'
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main
needs:
- Params
with:
jobs: ${{ needs.Params.outputs.python_jobs }}
# Optional
requirements: '-r tests/requirements.txt'
pacboy: >-
python-pip:p
python-wheel:p
python-coverage:p
python-lxml:p
mingw_requirements: '-r tests/requirements.mingw.txt'
tests_directory: 'tests'
unittest_directory: 'unit'
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}
Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@main
needs:
- Params
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
# Optional
python_version: ${{ needs..Params.outputs.python_version }}
requirements: '-r tests/requirements.txt'
tests_directory: 'tests'
unittest_directory: 'unit'
secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@main
needs:
- Params
with:
commands: |
mypy --junit-xml StaticTypingSummary.xml --html-report htmlmypy -p ToolName
html_artifact: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
junit_artifact: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_junit }}
# Optional
python_version: ${{ needs..Params.outputs.python_version }}
requirements: '-r tests/requirements.txt'
html_report: 'htmlmypy'
junit_report: 'StaticTypingSummary.xml'
allow_failure: true
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@main
needs:
- UnitTesting
- StaticTypeCheck
with:
# Optional
report_files: artifacts/**/*.xml
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@main
needs:
- Params
- Coverage
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
# Optional
python_version: ${{ needs..Params.outputs.python_version }}
requirements: 'wheel'
Release:
uses: pyTooling/Actions/.github/workflows/Release.yml@main
if: startsWith(github.ref, 'refs/tags')
needs:
- UnitTesting
- Coverage
- StaticTypeCheck
- Package
PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@main
if: startsWith(github.ref, 'refs/tags')
needs:
- Params
- Release
- Package
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
# Optional
python_version: ${{ needs..Params.outputs.python_version }}
requirements: 'wheel twine'
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
VerifyDocs:
uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@main
needs:
- Params
with:
# Optional
python_version: ${{ needs..Params.outputs.python_version }}
BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@main
needs:
- Params
- VerifyDocs
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main
needs:
- Params
- BuildTheDocs
- Coverage
- StaticTypeCheck
with:
doc: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}
# Optional
coverage: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
typing: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main
needs:
- Params
- PublishTestResults
- Coverage
- StaticTypeCheck
- BuildTheDocs
- PublishToGitHubPages
with:
package: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
remaining: |
${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-*
${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
${{ fromJson(needs.Params.outputs.artifact_names).statictyping_junit }}
${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

111
README.md
View File

@@ -10,52 +10,97 @@ This repository gathers reusable CI tooling for testing, packaging and distribut
See [GitHub Actions and GitHub Reusable Workflows](https://pytooling.github.io/Actions/Background.html) for more See [GitHub Actions and GitHub Reusable Workflows](https://pytooling.github.io/Actions/Background.html) for more
background information. background information.
## Reusable workflows ## Reusable Actions
- **Artifacts:**
[**pyTooling/upload-artifact**](https://github.com/pyTooling/upload-artifact): The upload-artifact action will
preserve file attributes like permissions.
[**pyTooling/download-artifact**](https://github.com/pyTooling/download-artifact): The download-artifact action will
preserve file attributes like permissions.
## Predefined Docker Images
- **Documentation:**
[**MikTeX**](https://github.com/pyTooling/MikTeX): A predefined MikTeX image based on Debian Bookworm + Python 3.13
with specific tools for documentation generation using e.g. Sphinx and related extensions.
## Reusable Workflows
This repository provides 10+ *Reusable Workflows* based on the CI pipelines of the repos in this GitHub organisation, This repository provides 10+ *Reusable Workflows* based on the CI pipelines of the repos in this GitHub organisation,
[EDA²](https://github.com/edaa-org), [VHDL](https://github.com/vhdl), and others. By combining them, Python packages can [EDA²](https://github.com/edaa-org), [VHDL](https://github.com/vhdl), and others. By combining them, Python packages can
be continuously tested and released along with Sphinx documentation sites, to GitHub Releases, GitHub Pages and PyPI. be continuously tested and released along with Sphinx documentation sites, to GitHub Releases, GitHub Pages and PyPI.
Optionally, coverage and static type check reports can be gathered and integrated into the online documentation. Optionally, coverage and static type check reports can be gathered and integrated into the online documentation.
[![](ExamplePipeline_dark.png)](ExamplePipeline_dark.png) [![](doc/_static/pyTooling-Actions-SimplePackage.png)](doc/_static/pyTooling-Actions-SimplePackage.png)
[![](ExamplePipeline_light.png)](ExamplePipeline_light.png)
As shown in the screenshots above, the expected order is: As shown in the screenshots above, the expected order is:
- Global: - **Global:**
- [Parameters](.github/workflows/Parameters.yml): a workaround for the limitations to handle global variables in [**Parameters**](.github/workflows/Parameters.yml): It generates output parameters with artifact names and job matrices
GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)). to be used in later running jobs.
It generates outputs with artifact names and job matrices to be used in later running jobs. It's a workaround for the limitations to handle global variables in GitHub Actions workflows (see
- Code testing/analysis: [actions/runner#480](https://github.com/actions/runner/issues/480)).
- [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and
optionally upload results as XML reports. Configuration options to `pytest` should be given via section [**ExtractConfiguration**](.github/workflows/ExtractConfiguration.yml): extracts configuration values from
`[tool.pytest.ini_options]` in a `pyproject.toml` file. `pyproject.toml` and exposes configured paths and filenames as job output parameters.
- [CoverageCollection](.github/workflows/CoverageCollection.yml): collect code coverage data (incl. branch coverage) - **Predefined pipelines:**
with `pytest`/`pytest-cov`/`coverage.py` using a single version of Python (latest). It generates HTML and Cobertura [**CompletePipeline**](.github/workflows/CompletePipeline.yml): is a predefined pipeline for typical Python projects
(XML)reports, upload the HTML report as an artifact, and upload the test results to Codecov and Codacy. Configuration using all predefined job templates of pyTooling at once: (unit testing, code coverage, static typing, documentation
options to `pytest` and `coverage.py` should be given via section `[tool.pytest.ini_options]` and `[tool.coverage.*]` report generation and publishing, packaging, releasing, ...)
in a `pyproject.toml` file. - **Code testing/analysis:**
- [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and [**ApplicationTesting**](.github/workflows/ApplicationTesting.yml): like UnitTesting, but running tests using an
optionally upload results as an HTML report. installed Python package.
Example `commands`:
[**UnitTesting**](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and
optionally upload results as XML reports. Configuration options to `pytest` should be given via section
`[tool.pytest.ini_options]` in a `pyproject.toml` file.
Besides test results, also code coverage data (incl. branch coverage) can be collected using
`pytest`/`pytest-cov`/`coverage.py`. Configuration options to `coverage.py` should be given via section
`[tool.coverage.*]` in a `pyproject.toml` file.
While multiple report formats can be created in the job, it's recommended to use `PublishTestResults` and/or
`PublishCoverageResults` to merge results from matrix runs and then generate final reports as XML, JSON or HTML.
Finally, reports can be published to GitHub Pages or cloud services like Codecov and Codacy.
[**StaticTypeCheck**](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and
optionally upload results as an HTML report.
[**VerifyDocs**](.github/workflows/VerifyDocs.yml): extract code examples from the README and test these code snippets.
- **Packaging and releasing:**
[**Package**](.github/workflows/Package.yml): generate source and wheel packages, and upload them as an artifact.
[**PublishOnPyPI**](.github/workflows/PublishOnPyPI.yml): publish source and wheel packages to PyPI.
[**PublishTestResults**](.github/workflows/PublishTestResults.yml): publish unit test results through GH action `dorny/test-reporter`.
[**PublishCoverageResults**](.github/workflows/PublishCoverageResults.yml): publish ucode coverage results.
[**NightlyRelease**](.github/workflows/NightlyRelease.yml): publish GitHub Release.
[**PublishReleaseNotes**](.github/workflows/PublishReleaseNotes.yml): publish GitHub Release.
- **Documentation:**
[**SphinxDocumentation**](.github/workflows/PublishCoverageResults.yml): create HTML and LaTeX documentation using
Sphinx.
[**LaTeXDocumentation**](.github/workflows/LaTeXDocumentation.yml): compile LaTeX documentation to a PDF file using
MikTeX.
[**PublishToGitHubPages**](.github/workflows/PublishToGitHubPages.yml): publish HTML documentation to GitHub Pages.
- **Cleanup:**
[**IntermediateCleanUp**](.github/workflows/IntermediateCleanUp.yml): delete intermediate artifacts.
[**ArtifactCleanUp**](.github/workflows/ArtifactCleanUp.yml): delete artifacts.
- **⚠ Deprecated ⚠:**
[**CoverageCollection**](.github/workflows/CoverageCollection.yml): Use `UnitTesting`, because is can collect code
coverage too. This avoids code duplication in job templates.
[**BuildTheDocs**](.github/workflows/BuildTheDocs.yml): Use `SphinxDocumentation`, `LaTeXDocumentation` and
`PublishToGitHubPages`. BuildTheDocs isn't maintained anymore.
- [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.
- [PublishOnPyPI](.github/workflows/PublishOnPyPI.yml): publish source and wheel packages to PyPI.
- [PublishTestResults](.github/workflows/PublishTestResults.yml): publish unit test results through GH action `dorny/test-reporter`.
- Documentation:
- [BuildTheDocs](.github/workflows/BuildTheDocs.yml): build Sphinx documentation with BuildTheDocs, and upload HTML as
an artifact.
- [PublishToGitHubPages](.github/workflows/PublishToGitHubPages.yml): publish HTML documentation to GitHub Pages.
- Cleanup:
- [ArtifactCleanUp](.github/workflows/ArtifactCleanUp.yml): delete artifacts.
### Example pipeline ### Example pipeline
[ExamplePipeline.yml](ExamplePipeline.yml) is an example Workflow which uses all of the Reusable Workflows. ExamplePipeline.yml is an example Workflow which uses all of the Reusable Workflows.
Python package/tool developers can copy it into their repos, in order to use al the reusable workflows straightaway. Python package/tool developers can copy it into their repos, in order to use al the reusable workflows straightaway.
Minimal required modifications are the following: Minimal required modifications are the following:

View File

@@ -1,2 +1,2 @@
wheel ~= 0.45 wheel ~= 0.45
twine ~= 5.1 twine ~= 6.1

View File

@@ -85,7 +85,7 @@ The following block shows a minimal YAML workflow file:
steps: steps:
# Clone repository # Clone repository
- uses: actions/checkout@v4 - uses: actions/checkout@v5
# Build your application, tool, artifacts, etc. # Build your application, tool, artifacts, etc.
- name: Build - name: Build
@@ -95,7 +95,7 @@ The following block shows a minimal YAML workflow file:
# Update tag and pre-release # Update tag and pre-release
# - Update (force-push) tag to the commit that is used in the workflow. # - Update (force-push) tag to the commit that is used in the workflow.
# - Upload artifacts defined by the user. # - Upload artifacts defined by the user.
- uses: pyTooling/Actions/releaser@r0 - uses: pyTooling/Actions/releaser@r4
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
files: | files: |

View File

@@ -60,12 +60,12 @@ Documentation Only (Sphinx)
jobs: jobs:
BuildTheDocs: BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r4
with: with:
artifact: Documentation artifact: Documentation
PublishToGitHubPages: PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r4
needs: needs:
- BuildTheDocs - BuildTheDocs
with: with:

View File

@@ -27,7 +27,7 @@ The simplest variant just uses the artifact name for the package.
jobs: jobs:
ArtifactCleanUp: ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0 uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r4
with: with:
package: Package package: Package
@@ -39,7 +39,7 @@ Complex Example
jobs: jobs:
ArtifactCleanUp: ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0 uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r4
needs: needs:
- Params - Params
- UnitTesting - UnitTesting

View File

@@ -30,7 +30,7 @@ Simple Example
jobs: jobs:
BuildTheDocs: BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r4
Complex Example Complex Example
@@ -40,7 +40,7 @@ Complex Example
jobs: jobs:
BuildTheDocs: BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r4
needs: needs:
- Params - Params
with: with:

View File

@@ -52,11 +52,10 @@ Simple Example
jobs: jobs:
Coverage: Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r4
with: with:
artifact: Coverage artifact: Coverage
secrets: secrets: inherit
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
Complex Example Complex Example
=============== ===============
@@ -65,14 +64,13 @@ Complex Example
jobs: jobs:
Coverage: Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r4
needs: needs:
- Params - Params
with: with:
python_version: ${{ needs.Params.outputs.python_version }} python_version: ${{ needs.Params.outputs.python_version }}
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }} artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
secrets: secrets: inherit
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
Parameters Parameters
********** **********

View File

@@ -33,7 +33,7 @@ Simple Example
jobs: jobs:
Package: Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r0 uses: pyTooling/Actions/.github/workflows/Package.yml@r4
with: with:
artifact: Package artifact: Package
@@ -45,7 +45,7 @@ Complex Example
jobs: jobs:
Package: Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r0 uses: pyTooling/Actions/.github/workflows/Package.yml@r4
needs: needs:
- Params - Params
- Coverage - Coverage

View File

@@ -35,7 +35,7 @@ requires a `name` parameter to create the artifact names.
jobs: jobs:
Params: Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
@@ -81,7 +81,7 @@ over resulting in the following combinations:
jobs: jobs:
UnitTestingParams: UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
python_version_list: "3.8 3.9 3.10 3.11 pypy-3.9 pypy-3.10" python_version_list: "3.8 3.9 3.10 3.11 pypy-3.9 pypy-3.10"
@@ -89,14 +89,14 @@ over resulting in the following combinations:
exclude_list: "windows:pypy-3.9 windows:pypy-3.10" exclude_list: "windows:pypy-3.9 windows:pypy-3.10"
PerformanceTestingParams: PerformanceTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
python_version_list: "3.11 3.12" python_version_list: "3.11 3.12"
system_list: "ubuntu windows macos" system_list: "ubuntu windows macos"
PlatformTestingParams: PlatformTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
python_version_list: "3.12" python_version_list: "3.12"
@@ -297,12 +297,12 @@ variables. Thus, this job is used to compute an output parameter that can be reu
jobs: jobs:
Params: Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
CodeCoverage: CodeCoverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r4
needs: needs:
- Params - Params
with: with:
@@ -330,12 +330,12 @@ A job description contains the following key-value pairs:
jobs: jobs:
Params: Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
UnitTesting: UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r4
needs: needs:
- Params - Params
with: with:
@@ -389,12 +389,12 @@ The supported artifacts are:
jobs: jobs:
Params: Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 uses: pyTooling/Actions/.github/workflows/Parameters.yml@r4
with: with:
name: pyTooling name: pyTooling
Coverage: Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@dev uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r4
needs: needs:
- Params - Params
with: with:

View File

@@ -42,7 +42,7 @@ by a Git tag. A secret is forwarded from GitHub secrets to a job secret.
# ... # ...
PublishOnPyPI: PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r4
if: startsWith(github.ref, 'refs/tags') if: startsWith(github.ref, 'refs/tags')
with: with:
artifact: Package artifact: Package
@@ -66,7 +66,7 @@ by that job. Finally, the list of requirements is overwritten to load a list of
# ... # ...
PublishOnPyPI: PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r4
if: startsWith(github.ref, 'refs/tags') if: startsWith(github.ref, 'refs/tags')
needs: needs:
- Params - Params

View File

@@ -34,7 +34,7 @@ Simple Example
jobs: jobs:
PublishTestResults: PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r4
Complex Example Complex Example
=============== ===============
@@ -49,7 +49,7 @@ Complex Example
# ... # ...
PublishTestResults: PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r4
needs: needs:
- CodeCoverage - CodeCoverage
- UnitTesting - UnitTesting

View File

@@ -29,7 +29,7 @@ Simple Example
# ... # ...
PublishToGitHubPages: PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r4
needs: needs:
- BuildTheDocs - BuildTheDocs
with: with:
@@ -43,7 +43,7 @@ Complex Example
jobs: jobs:
PublishToGitHubPages: PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r4
needs: needs:
- Params - Params
- BuildTheDocs - BuildTheDocs

View File

@@ -62,7 +62,7 @@ Simple Example
jobs: jobs:
Release: Release:
uses: pyTooling/Actions/.github/workflows/Release.yml@r0 uses: pyTooling/Actions/.github/workflows/Release.yml@r4
Complex Example Complex Example
@@ -72,7 +72,7 @@ Complex Example
jobs: jobs:
Release: Release:
uses: pyTooling/Actions/.github/workflows/Release.yml@r0 uses: pyTooling/Actions/.github/workflows/Release.yml@r4
if: startsWith(github.ref, 'refs/tags') if: startsWith(github.ref, 'refs/tags')
needs: needs:
- Package - Package

View File

@@ -29,7 +29,7 @@ Simple Example
jobs: jobs:
StaticTypeCheck: StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0 uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r4
with: with:
commands: | commands: |
touch pyTooling/__init__.py touch pyTooling/__init__.py
@@ -44,7 +44,7 @@ Complex Example
jobs: jobs:
StaticTypeCheck: StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0 uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r4
needs: needs:
- Params - Params
with: with:

View File

@@ -36,7 +36,7 @@ Simple Example
# ... # ...
UnitTesting: UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r0 uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r4
needs: needs:
- Params - Params
with: with:

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

View File

@@ -1,20 +1,29 @@
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
from importlib.util import find_spec
from sys import path as sys_path from sys import path as sys_path
from os.path import abspath from os.path import abspath
from pathlib import Path from pathlib import Path
from json import loads
from pyTooling.Packaging import extractVersionInformation from pyTooling.Packaging import extractVersionInformation
# ==============================================================================
# Project configuration
# ==============================================================================
githubNamespace = "pyTooling"
githubProject = "Actions"
pythonProject = "pyDummy"
directoryName = pythonProject.replace('.', '/')
# ==============================================================================
# Project paths
# ==============================================================================
ROOT = Path(__file__).resolve().parent ROOT = Path(__file__).resolve().parent
sys_path.insert(0, abspath(".")) sys_path.insert(0, abspath("."))
sys_path.insert(0, abspath("..")) sys_path.insert(0, abspath(".."))
sys_path.insert(0, abspath("../pyDummy")) sys_path.insert(0, abspath(f"../{directoryName}"))
# sys_path.insert(0, abspath("_extensions"))
# ============================================================================== # ==============================================================================
@@ -23,11 +32,7 @@ sys_path.insert(0, abspath("../pyDummy"))
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
githubNamespace = "pyTooling" packageInformationFile = Path(f"../{directoryName}/__init__.py")
githubProject = "Actions"
project = "pyDummy"
packageInformationFile = Path(f"../{project}/__init__.py")
versionInformation = extractVersionInformation(packageInformationFile) versionInformation = extractVersionInformation(packageInformationFile)
author = versionInformation.Author author = versionInformation.Author
@@ -105,7 +110,7 @@ html_last_updated_fmt = "%d.%m.%Y"
# Python settings # Python settings
# ============================================================================== # ==============================================================================
modindex_common_prefix = [ modindex_common_prefix = [
f"{project}." f"{pythonProject}."
] ]
# ============================================================================== # ==============================================================================
@@ -257,13 +262,13 @@ todo_link_only = True
# ============================================================================== # ==============================================================================
# report_unittest_testsuites = { # report_unittest_testsuites = {
# "src": { # "src": {
# "name": f"{project}", # "name": f"{pythonProject}",
# "xml_report": "../report/unit/unittest.xml", # "xml_report": "../report/unit/unittest.xml",
# } # }
# } # }
# report_codecov_packages = { # report_codecov_packages = {
# "src": { # "src": {
# "name": f"{project}", # "name": f"{pythonProject}",
# "json_report": "../report/coverage/coverage.json", # "json_report": "../report/coverage/coverage.json",
# "fail_below": 80, # "fail_below": 80,
# "levels": "default" # "levels": "default"
@@ -271,8 +276,8 @@ todo_link_only = True
# } # }
# report_doccov_packages = { # report_doccov_packages = {
# "src": { # "src": {
# "name": f"{project}", # "name": f"{pythonProject}",
# "directory": f"../{project}", # "directory": f"../{directoryName}",
# "fail_below": 80, # "fail_below": 80,
# "levels": "default" # "levels": "default"
# } # }
@@ -289,17 +294,17 @@ todo_link_only = True
# AutoAPI.Sphinx # AutoAPI.Sphinx
# ============================================================================== # ==============================================================================
autoapi_modules = { autoapi_modules = {
f"{project}": { f"{pythonProject}": {
"template": "package", "template": "package",
"output": project, "output": pythonProject,
"override": True "override": True
} }
} }
for directory in [mod for mod in Path(f"../{project}").iterdir() if mod.is_dir() and mod.name != "__pycache__"]: for directory in [mod for mod in Path(f"../{directoryName}").iterdir() if mod.is_dir() and mod.name != "__pycache__"]:
print(f"Adding module rule for '{project}.{directory.name}'") print(f"Adding module rule for '{pythonProject}.{directory.name}'")
autoapi_modules[f"{project}.{directory.name}"] = { autoapi_modules[f"{pythonProject}.{directory.name}"] = {
"template": "module", "template": "module",
"output": project, "output": pythonProject,
"override": True "override": True
} }

View File

@@ -4,4 +4,4 @@ Code Coverage Report
Code coverage report generated with `pytest <https://github.com/pytest-dev/pytest>`__ and `Coverage.py <https://github.com/nedbat/coveragepy/tree/master>`__. Code coverage report generated with `pytest <https://github.com/pytest-dev/pytest>`__ and `Coverage.py <https://github.com/nedbat/coveragepy/tree/master>`__.
.. #report:code-coverage:: .. #report:code-coverage::
:packageid: src :reportid: src

View File

@@ -5,7 +5,7 @@ pushd %~dp0
REM Command file for Sphinx documentation REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" ( if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build set SPHINXBUILD=py -3.13 -m sphinx.cmd.build
) )
set SOURCEDIR=. set SOURCEDIR=.
set BUILDDIR=_build set BUILDDIR=_build

View File

@@ -1,9 +1,9 @@
-r ../requirements.txt -r ../requirements.txt
pyTooling ~= 8.0 pyTooling ~= 8.5
# Enforce latest version on ReadTheDocs # Enforce latest version on ReadTheDocs
sphinx ~= 8.1 sphinx ~= 8.2
docutils ~= 0.21 docutils ~= 0.21
docutils_stubs ~= 0.0.22 docutils_stubs ~= 0.0.22
@@ -11,17 +11,9 @@ docutils_stubs ~= 0.0.22
sphinx_rtd_theme ~= 3.0 sphinx_rtd_theme ~= 3.0
# Sphinx Extenstions # Sphinx Extenstions
#sphinx.ext.coverage sphinxcontrib-mermaid ~= 1.0
#sphinxcontrib-actdiag>=0.8.5
sphinxcontrib-mermaid>=0.9.2
#sphinxcontrib-seqdiag>=0.8.5
#sphinxcontrib-textstyle>=0.2.1
#sphinxcontrib-spelling>=2.2.0
autoapi >= 2.0.1 autoapi >= 2.0.1
sphinx_design ~= 0.6.1 sphinx_design ~= 0.6.1
sphinx-copybutton >= 0.5.2 sphinx-copybutton >= 0.5.2
sphinx_autodoc_typehints ~= 2.5 sphinx_autodoc_typehints ~= 3.2
# changelog>=0.3.5 sphinx_reports ~= 0.9
sphinx_reports ~= 0.7
# BuildTheDocs Extensions (mostly patched Sphinx extensions)

View File

@@ -4,11 +4,11 @@
# percent encoding so that the URL is properly parsed. # percent encoding so that the URL is properly parsed.
.. # Sourcecode link to GitHub .. # Sourcecode link to GitHub
.. |SHIELD:svg:pyTooling-github| image:: https://img.shields.io/badge/pyTooling-Actions-63bf7f.svg?longCache=true&style=flat-square&longCache=true&logo=GitHub .. |SHIELD:svg:pyTooling-github| image:: https://img.shields.io/badge/pyTooling-Actions-63bf7f?longCache=true&style=flat-square&longCache=true&logo=GitHub
:alt: Sourcecode on GitHub :alt: Sourcecode on GitHub
:height: 22 :height: 22
:target: https://GitHub.com/pyTooling/Actions :target: https://GitHub.com/pyTooling/Actions
.. |SHIELD:png:pyTooling-github| image:: https://raster.shields.io/badge/pyTooling-Actions-63bf7f.svg?longCache=true&style=flat-square&longCache=true&logo=GitHub .. |SHIELD:png:pyTooling-github| image:: https://raster.shields.io/badge/pyTooling-Actions-63bf7f?longCache=true&style=flat-square&longCache=true&logo=GitHub
:alt: Sourcecode on GitHub :alt: Sourcecode on GitHub
:height: 22 :height: 22
:target: https://GitHub.com/pyTooling/Actions :target: https://GitHub.com/pyTooling/Actions
@@ -18,7 +18,7 @@
:alt: Code license :alt: Code license
:height: 22 :height: 22
:target: Code-License.html :target: Code-License.html
.. |SHIELD:png:pyTooling-src-license| image:: https://img.shields.io/pypi/l/pyTooling?longCache=true&style=flat-square&logo=Apache&label=code .. |SHIELD:png:pyTooling-src-license| image:: https://raster.shields.io/pypi/l/pyTooling?longCache=true&style=flat-square&logo=Apache&label=code
:alt: Code license :alt: Code license
:height: 22 :height: 22
:target: https://GitHub.com/pyTooling/Actions/blob/main/LICENSE.md :target: https://GitHub.com/pyTooling/Actions/blob/main/LICENSE.md
@@ -64,11 +64,11 @@
:target: https://pyTooling.github.io/pyTooling/ :target: https://pyTooling.github.io/pyTooling/
.. # Gitter .. # Gitter
.. |SHIELD:svg:pyTooling-gitter| image:: https://img.shields.io/badge/chat-on%20gitter-4db797.svg?longCache=true&style=flat-square&logo=gitter&logoColor=e8ecef .. |SHIELD:svg:pyTooling-gitter| image:: https://img.shields.io/badge/chat-on%20gitter-4db797?longCache=true&style=flat-square&logo=gitter&logoColor=e8ecef
:alt: Documentation License :alt: Documentation License
:height: 22 :height: 22
:target: https://gitter.im/hdl/community :target: https://gitter.im/hdl/community
.. |SHIELD:png:pyTooling-gitter| image:: https://raster.shields.io/badge/chat-on%20gitter-4db797.svg?longCache=true&style=flat-square&logo=gitter&logoColor=e8ecef .. |SHIELD:png:pyTooling-gitter| image:: https://raster.shields.io/badge/chat-on%20gitter-4db797?longCache=true&style=flat-square&logo=gitter&logoColor=e8ecef
:alt: Documentation License :alt: Documentation License
:height: 22 :height: 22
:target: https://gitter.im/hdl/community :target: https://gitter.im/hdl/community

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -34,9 +34,9 @@ A module for a set of dummy classes.
__author__ = "Patrick Lehmann" __author__ = "Patrick Lehmann"
__email__ = "Paebbels@gmail.com" __email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2024, Patrick Lehmann" __copyright__ = "2017-2025, Patrick Lehmann"
__license__ = "Apache License, Version 2.0" __license__ = "Apache License, Version 2.0"
__version__ = "0.4.4" __version__ = "0.4.5"
__keywords__ = ["GitHub Actions"] __keywords__ = ["GitHub Actions"]
__issue_tracker__ = "https://GitHub.com/pyTooling/Actions/issues" __issue_tracker__ = "https://GitHub.com/pyTooling/Actions/issues"
@@ -53,9 +53,9 @@ class Base:
_value: int #: An internal value. _value: int #: An internal value.
def __init__(self) -> None: def __init__(self) -> None:
""" # """
Initializes the base-class. # Initializes the base-class.
""" # """
self._value = 0 self._value = 0
@readonly @readonly
@@ -75,9 +75,9 @@ class Application(Base):
""" """
def __init__(self) -> None: def __init__(self) -> None:
""" # """
Initializes the dummy application. # Initializes the dummy application.
""" # """
super().__init__() super().__init__()
platform = Platform() platform = Platform()

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -34,7 +34,7 @@ A module for a set of dummy classes.
__author__ = "Patrick Lehmann" __author__ = "Patrick Lehmann"
__email__ = "Paebbels@gmail.com" __email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2024, Patrick Lehmann" __copyright__ = "2017-2025, Patrick Lehmann"
__license__ = "Apache License, Version 2.0" __license__ = "Apache License, Version 2.0"
__version__ = "0.14.8" __version__ = "0.14.8"
__keywords__ = ["GitHub Actions"] __keywords__ = ["GitHub Actions"]

View File

@@ -1,8 +1,8 @@
[build-system] [build-system]
requires = [ requires = [
"setuptools ~= 75.3", "setuptools >= 80.0",
"wheel ~= 0.45", "wheel ~= 0.45",
"pyTooling ~= 8.0" "pyTooling ~= 8.5"
] ]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
@@ -20,6 +20,12 @@ show_error_codes = true
namespace_packages = true namespace_packages = true
html_report = "report/typing" html_report = "report/typing"
[tool.pytest]
junit_xml = "report/unit/UnittestReportSummary.xml"
[tool.pyedaa-reports]
junit_xml = "report/unit/unittest.xml"
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "--tb=native" addopts = "--tb=native"
# Don't set 'python_classes = *' otherwise, pytest doesn't search for classes # Don't set 'python_classes = *' otherwise, pytest doesn't search for classes
@@ -30,6 +36,7 @@ filterwarnings = [
"error::DeprecationWarning", "error::DeprecationWarning",
"error::PendingDeprecationWarning" "error::PendingDeprecationWarning"
] ]
junit_logging = "all"
[tool.interrogate] [tool.interrogate]
color = true color = true

View File

@@ -79,7 +79,7 @@ jobs:
steps: steps:
# Clone repository # Clone repository
- uses: actions/checkout@v4 - uses: actions/checkout@v5
# Build your application, tool, artifacts, etc. # Build your application, tool, artifacts, etc.
- name: Build - name: Build

View File

@@ -3,7 +3,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -3,7 +3,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -5,7 +5,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -1 +1 @@
pyTooling ~= 8.0 pyTooling ~= 8.5

View File

@@ -88,7 +88,7 @@ if ($build)
rm -Force .\build\bdist.win-amd64 rm -Force .\build\bdist.win-amd64
rm -Force .\build\lib rm -Force .\build\lib
Write-Host -ForegroundColor Yellow "[live][BUILD] Building $PackageName package as wheel ..." Write-Host -ForegroundColor Yellow "[live][BUILD] Building $PackageName package as wheel ..."
py -3.12 -m build --wheel py -3.13 -m build --wheel --no-isolation
Write-Host -ForegroundColor Yellow "[live][BUILD] Building wheel finished" Write-Host -ForegroundColor Yellow "[live][BUILD] Building wheel finished"
} }
@@ -102,9 +102,9 @@ if ($install)
} }
else else
{ Write-Host -ForegroundColor Cyan "[ADMIN][UNINSTALL] Uninstalling $PackageName ..." { Write-Host -ForegroundColor Cyan "[ADMIN][UNINSTALL] Uninstalling $PackageName ..."
py -3.12 -m pip uninstall -y $PackageName py -3.13 -m pip uninstall -y $PackageName
Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Installing $PackageName from wheel ..." Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Installing $PackageName from wheel ..."
py -3.12 -m pip install .\dist\$PackageName-6.7.0-py3-none-any.whl py -3.13 -m pip install .\dist\$PackageName-8.1.0-py3-none-any.whl
Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Closing window in 5 seconds ..." Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Closing window in 5 seconds ..."
Start-Sleep -Seconds 5 Start-Sleep -Seconds 5

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #
@@ -39,13 +39,16 @@ packageName = "pyDummy"
packageDirectory = packageName packageDirectory = packageName
packageInformationFile = Path(f"{packageDirectory}/__init__.py") packageInformationFile = Path(f"{packageDirectory}/__init__.py")
setup(**DescribePythonPackageHostedOnGitHub( setup(
packageName=packageName, **DescribePythonPackageHostedOnGitHub(
description="pyDummy is a test package to verify GitHub actions for Python projects.", packageName=packageName,
gitHubNamespace=gitHubNamespace, description="pyDummy is a test package to verify GitHub actions for Python projects.",
unittestRequirementsFile=Path("tests/requirements.txt"), gitHubNamespace=gitHubNamespace,
sourceFileWithVersion=packageInformationFile, unittestRequirementsFile=Path("tests/requirements.txt"),
dataFiles={ sourceFileWithVersion=packageInformationFile,
packageName: ["py.typed"] dataFiles={
} packageName: ["py.typed"]
)) },
debug=True
)
)

View File

@@ -8,7 +8,7 @@ print(f"Python: {version}")
def loadRequirementsFile(requirementsFile: Path): def loadRequirementsFile(requirementsFile: Path):
requirements = [] requirements = []
with requirementsFile.open("r") as file: with requirementsFile.open("r", encoding="utf-8") as file:
for line in file.readlines(): for line in file.readlines():
line = line.strip() line = line.strip()
if line.startswith("#") or line.startswith("https") or line == "": if line.startswith("#") or line.startswith("https") or line == "":
@@ -84,7 +84,7 @@ for dependency in dependencies:
# Write jobs to special file # Write jobs to special file
github_output = Path(getenv("GITHUB_OUTPUT")) github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}") print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+") as f: with github_output.open("a+", encoding="utf-8") as f:
f.write(f"pacboy_packages={' '.join(pacboyPackages)}\n") f.write(f"pacboy_packages={' '.join(pacboyPackages)}\n")
print(f"GITHUB_OUTPUT:") print(f"GITHUB_OUTPUT:")

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -1,13 +1,13 @@
-r ../requirements.txt -r ../requirements.txt
# Coverage collection # Coverage collection
Coverage ~= 7.6 Coverage ~= 7.10
# Test Runner # Test Runner
pytest ~= 8.3 pytest ~= 8.4
pytest-cov ~= 6.0 pytest-cov ~= 6.2
# Static Type Checking # Static Type Checking
mypy ~= 1.13 mypy ~= 1.17
typing_extensions ~= 4.12 typing_extensions ~= 4.14
lxml ~= 5.3 lxml ~= 6.0

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -11,7 +11,7 @@
# # # #
# License: # # License: #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany # # Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #

View File

@@ -4,7 +4,7 @@
# Unai Martinez-Corral # # Unai Martinez-Corral #
# # # #
# ==================================================================================================================== # # ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors # # Copyright 2020-2025 The pyTooling Authors #
# # # #
# Licensed under the Apache License, Version 2.0 (the "License"); # # Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. # # you may not use this file except in compliance with the License. #