Compare commits

...

167 Commits

Author SHA1 Message Date
Patrick Lehmann
2eebeec719 v6.3.0 2025-10-01 15:16:07 +02:00
Patrick Lehmann
5b97eaf241 Gather a list of submodule names, pathes etc. 2025-10-01 12:55:33 +02:00
Patrick Lehmann
1e694005ed Fixed structure version datatype in inventory JSON. 2025-10-01 03:11:48 +02:00
Patrick Lehmann
46a2764e73 Fixed timestamp format in inventory JSON. 2025-10-01 00:53:09 +02:00
Patrick Lehmann
626d64ef6a Updated classification outputs. 2025-10-01 00:28:54 +02:00
Patrick Lehmann
fe4c9139c1 Print Git reference classification. 2025-09-30 23:47:28 +02:00
Patrick Lehmann
ae8a961e93 v6.2.0 2025-09-30 22:53:57 +02:00
Patrick Lehmann
e4b5ea3895 Check if commit is on default branch. 2025-09-30 21:34:33 +02:00
Patrick Lehmann
b61f479180 Fix computation of latest version from GH CLI. 2025-09-30 09:02:50 +02:00
Patrick Lehmann
9e6bbd52a6 v6.1.0 2025-09-25 00:26:08 +02:00
Patrick Lehmann
438207a68d Added latest released version to the inventory.json. 2025-09-25 00:21:48 +02:00
Patrick Lehmann
c08a164b9e v6.0.0 2025-09-24 15:25:58 +02:00
Patrick Lehmann
2e4f6f3e7c Documented InstallPackage. 2025-09-24 15:19:59 +02:00
Patrick Lehmann
a8e4c60424 Improved links. 2025-09-24 15:17:57 +02:00
Patrick Lehmann
c0547188f9 Changed documentation to @r6. 2025-09-24 09:53:51 +02:00
Patrick Lehmann
53a32fbf35 Also use JSON objects for SphinxDocumentation. 2025-09-24 00:17:43 +02:00
Patrick Lehmann
91736df13e Try to package myPackage or myFramework.Extension with same setup-routine. 2025-09-23 22:59:02 +02:00
Patrick Lehmann
d190b1a3b1 Update documentation according to latest changes - part 2. 2025-09-23 20:26:31 +02:00
Patrick Lehmann
055863ee5f Test Bandit reporting. 2025-09-23 20:24:35 +02:00
Patrick Lehmann
22bbe48d4b Check if artifacts have no basename (prefix). 2025-09-23 18:09:53 +02:00
Patrick Lehmann
4d260bfdf5 Publish Bandit report only if issues are found (avoid empty report). 2025-09-23 18:06:51 +02:00
Patrick Lehmann
f684e67bca Update documentation according to latest changes. 2025-09-23 17:00:35 +02:00
Patrick Lehmann
8ecefcec59 Added deprecation warnings. 2025-09-22 08:18:42 +02:00
Patrick Lehmann
b247eb4a53 Documentation fine-tuning. 2025-09-22 00:58:18 +02:00
Patrick Lehmann
fb67dd0fdb Handle namespace.* packages. 2025-09-21 00:18:32 +02:00
Patrick Lehmann
bfe857a4af Added Bandir, Radon metrics and PyLint checking. 2025-09-18 00:08:07 +02:00
Patrick Lehmann
1041e7b5c7 Reduced number of parameters by passing JSON objects. 2025-09-17 07:17:48 +02:00
Patrick Lehmann
e2e8b39c41 Rework StaticTypeCheck. 2025-09-15 22:16:24 +02:00
Patrick Lehmann
ae6f532e52 Improved landing page. 2025-09-13 22:27:52 +02:00
Patrick Lehmann
2fe6be15c7 Corrections after consistency, spelling and grammar check by Gemini. 2025-09-13 22:03:05 +02:00
Patrick Lehmann
15c9a23136 Improve mypy execution and less dependencies and manual/hard-coded Bash commands. 2025-09-13 21:06:37 +02:00
Patrick Lehmann
f79a63bf8e Improving StaticTypeCheck. 2025-09-13 18:22:03 +02:00
Patrick Lehmann
0c8f81d52e Allow LaTeXDocumentation jobs to fail. 2025-09-12 21:51:43 +02:00
Patrick Lehmann
250ad53b7b Documented StaticTypeCheck. 2025-09-12 00:38:01 +02:00
Patrick Lehmann
d1d591efb8 Reworked PublishToGitHubPages. 2025-09-10 08:23:17 +02:00
Patrick Lehmann
57f88b158f Fine tuning documentation pages. 2025-09-10 07:49:11 +02:00
Patrick Lehmann
f37f087334 Documented PublishCoverageResults. 2025-09-08 23:56:28 +02:00
Patrick Lehmann
dd30d0bc18 Reduced duplications. 2025-09-08 22:24:07 +02:00
Patrick Lehmann
35dd52d018 Documented CheckDocumentation. 2025-09-08 07:50:57 +02:00
Patrick Lehmann
a3816bfd59 Set releases always to latest. 2025-09-08 07:26:44 +02:00
Patrick Lehmann
e6b2d0a876 Continued PublishReleaseNotes. 2025-09-08 00:14:16 +02:00
Patrick Lehmann
52712f8491 Bumped dependencies. 2025-09-07 08:26:01 +02:00
Patrick Lehmann
d005debf69 Documented IntermediateCleanup and ArtifactCleanup. 2025-09-07 08:19:12 +02:00
Patrick Lehmann
ee869dead5 Documented PublishReleaseNotes. 2025-09-05 01:19:17 +02:00
Patrick Lehmann
1c99a4a914 Documented TagReleaseCommit. 2025-09-04 23:46:46 +02:00
Patrick Lehmann
d62ba6c06b Documented InstallPackage. 2025-09-02 07:45:26 +02:00
Patrick Lehmann
e5b29528b7 Reworked PublishTestResults. 2025-09-02 07:31:24 +02:00
Patrick Lehmann
e559fb8219 Reworked PublishOnPyPI. 2025-09-01 23:04:45 +02:00
Patrick Lehmann
d5ad74fa89 Documented Package. 2025-09-01 22:22:05 +02:00
Patrick Lehmann
e5706693e2 Restructuring example code. 2025-09-01 01:17:01 +02:00
Patrick Lehmann
729e406294 Reworked UnitTesting. 2025-09-01 00:36:52 +02:00
Patrick Lehmann
bb03bdcc11 Improved SphinxDocumentation and LaTeXDocumentation. 2025-08-31 12:07:05 +02:00
Patrick Lehmann
0adc72d26a Support cross-repository pull request runs from forks. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
277df7ea7f Partially documented SphinxDocumentation and LaTeXDocumentation. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
817c84af2e Adding and testing ubuntu-arm and windows-arm support. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
99c3752847 Reworked Parameters. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
13e42615b2 Documented ExtractConfiguration. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
6d75b849f5 Updated CompletePipeline. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
4f49964e57 Documented PrepareJob. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
b4d45277d1 Testing available runner images for Free/OpenSource Plan. 2025-08-29 22:15:12 +02:00
Patrick Lehmann
0f66b32418 Documented CompletePipeline. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
4badbda8e7 Restructured chapters. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
3141de852a Updated workflow references to @r5. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
a2db5ec238 Document all recently added workflow templates. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
c78cb4062f Fixed usage of Windows image. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
2c0e88f39a Check artifact uploading. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
e962cd6953 Fix potential artifact upload for XML and JSON coverage artifacts. 2025-08-29 22:15:11 +02:00
Patrick Lehmann
bde1b15783 Bumped Windows Server image from 2022 to 2025. 2025-08-29 22:15:11 +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
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
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
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
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
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
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
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
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
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
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
151 changed files with 12046 additions and 3282 deletions

3
.github/CODEOWNERS vendored Normal file
View File

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

View File

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

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -86,14 +86,15 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 📥 Download artifacts '${{ inputs.wheel }}' from 'Package' job
uses: pyTooling/download-artifact@v4
uses: pyTooling/download-artifact@v5
with:
name: ${{ inputs.wheel }}
path: install
# TODO: extract step to an Action so package lists are shared with UnitTesting (and GHDL?)
- name: Compute pacman/pacboy packages
id: pacboy
if: matrix.system == 'msys2'
@@ -134,20 +135,21 @@ jobs:
packages = {
"coverage": "python-coverage:p",
"docstr_coverage": "python-pyyaml:p",
"docstr_coverage": "python-pyyaml:p python-types-pyyaml:p",
"igraph": "igraph:p",
"jinja2": "python-markupsafe:p",
"lxml": "python-lxml:p",
"numpy": "python-numpy:p",
"markupsafe": "python-markupsafe: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",
"sphinx": "python-markupsafe:p",
"tomli": "python-tomli:p",
"wheel": "python-wheel: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",
"sphinx-reports": "python-markupsafe:p python-pyaml:p python-types-pyyaml:p",
}
subPackages = {
"pytooling": {
@@ -188,8 +190,8 @@ jobs:
f.write(f"pacboy_packages={' '.join(pacboyPackages)}\n")
- name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}'
if: matrix.system == 'msys2'
uses: msys2/setup-msys2@v2
if: matrix.system == 'msys2'
with:
msystem: ${{ matrix.runtime }}
update: true
@@ -198,8 +200,8 @@ jobs:
${{ inputs.pacboy }}
- name: 🐍 Setup Python ${{ matrix.python }}
uses: actions/setup-python@v6
if: matrix.system != 'msys2'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
@@ -224,22 +226,22 @@ jobs:
python -m pip install --disable-pip-version-check -U install/*.whl
- name: ✅ Run application tests (Ubuntu/macOS)
if: matrix.system != 'windows'
if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' )
run: |
export ENVIRONMENT_NAME="${{ matrix.envname }}"
cd "${{ inputs.root_directory || '.' }}"
[ -n '${{ inputs.apptest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS
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 }}
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 }}
fi
- name: ✅ Run application tests (Windows)
if: matrix.system == 'windows'
if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' )
run: |
$env:ENVIRONMENT_NAME = "${{ matrix.envname }}"

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -47,13 +47,13 @@ jobs:
steps:
- name: 🗑️ Delete package Artifacts
if: ${{ ! startsWith(github.ref, 'refs/tags') }}
uses: geekyeggo/delete-artifact@v5
if: ${{ ! startsWith(github.ref, 'refs/tags') }}
with:
name: ${{ inputs.package }}
- name: 🗑️ Delete remaining Artifacts
if: ${{ inputs.remaining != '' }}
uses: geekyeggo/delete-artifact@v5
if: ${{ inputs.remaining != '' }}
with:
name: ${{ inputs.remaining }}

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -37,11 +37,11 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: '❗ Deprecation message'
run: echo "::warning title=Deprecated::'BuildTheDocs.yml' is not maintained anymore. Please switch to 'SphinxDocumentation.yml', 'LaTeXDocumentation.yml' and 'ExtractConfiguration.yml'."
- name: ⚠️ Deprecation Warning
run: printf "::warning title=%s::%s\n" "Deprecated" "'BuildTheDocs.yml' template is deprecated. Please switch to 'SphinxDocumentation.yml'. See https://pytooling.github.io/Actions/JobTemplate/Documentation/SphinxDocumentation.html"
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 🛳️ Build documentation
uses: buildthedocs/btd@v0
@@ -49,8 +49,8 @@ jobs:
skip-deploy: true
- name: 📤 Upload 'documentation' artifacts
if: inputs.artifact != ''
uses: pyTooling/upload-artifact@v4
if: inputs.artifact != ''
with:
name: ${{ inputs.artifact }}
working-directory: doc/_build/html

201
.github/workflows/CheckCodeQuality.yml vendored Normal file
View File

@@ -0,0 +1,201 @@
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2025-2025 The pyTooling Authors #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
# #
# SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== #
name: Code Quality Checking
on:
workflow_call:
inputs:
ubuntu_image_version:
description: 'Ubuntu image version.'
required: false
default: '24.04'
type: string
python_version:
description: 'Python version.'
required: false
default: '3.13'
type: string
package_directory:
description: 'The package''s directory'
required: true
type: string
requirements:
description: 'Python dependencies to be installed through pip.'
required: false
default: '-r requirements.txt'
type: string
bandit:
description: 'Run bandit checks.'
required: false
default: 'true'
type: string
radon:
description: 'Run radon checks.'
required: false
default: 'true'
type: string
pylint:
description: 'Run pylint checks.'
required: false
default: 'true'
type: string
artifact:
description: 'Name of the package artifact.'
required: true
type: string
jobs:
Bandit:
name: 🚨 Security Scanning (Bandit)
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
if: inputs.bandit == 'true'
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: ⚙ Install dependencies for running bandit
run: python -m pip install --disable-pip-version-check bandit
- name: 👮 Bandit
id: bandit
if: inputs.artifact != ''
run: |
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_LIGHT_BLUE=$'\x1b[94m'
ANSI_NOCOLOR=$'\x1b[0m'
bandit_directory=report/bandit
bandit_fullpath=report/bandit/report.xml
tee "${GITHUB_OUTPUT}" <<EOF
bandit_directory=${bandit_directory}
bandit_fullpath=${bandit_fullpath}
EOF
mkdir -p ${bandit_directory}
printf "\nRun bandit ...\n"
bandit -c pyproject.toml -r ${{ inputs.package_directory }} -f xml -o ${bandit_fullpath}
if [[ $? -eq 0 ]]; then
printf "Bandit result: ${ANSI_LIGHT_GREEN}[PASSED]${ANSI_NOCOLOR}\n"
printf "bandit_passed=true\n" >> "${GITHUB_OUTPUT}"
else
faults=$(grep -Poh '(?<=<testsuite\sname="bandit"\stests=")(\d+)(?=">)' ${bandit_fullpath})
printf "Bandit result: ${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Bandit found %s issues.${ANSI_NOCOLOR}\n" "${faults}"
printf "::error title=%s::%s\n" "🚨 Security Scanning (Bandit)" "Bandi found ${faults} issues."
printf "bandit_passed=false\n" >> "${GITHUB_OUTPUT}"
printf "::group::${ANSI_LIGHT_BLUE}JUnit XML report created by Bandit ...${ANSI_NOCOLOR}\n"
cat ${bandit_fullpath}
printf "\n::endgroup::\n"
fi
- name: 📊 Publish Bandit Results
uses: dorny/test-reporter@v2
if: steps.bandit.outputs.bandit_passed == 'false'
continue-on-error: true
with:
name: 'Bandit Results'
path: ${{ steps.bandit.outputs.bandit_fullpath }}
reporter: java-junit
Radon:
name: ☢️ Metrics and Complexity
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
if: inputs.radon == 'true'
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: ⚙ Install dependencies for running radon
run: python -m pip install --disable-pip-version-check radon
- name: Code Metrics
# if: inputs.artifact != ''
run: |
radon raw ${{ inputs.package_directory }} -s
- name: Code Complexity
# if: inputs.artifact != ''
run: |
radon cc ${{ inputs.package_directory }} --total-average
- name: Halstead Complexity Metrics
# if: inputs.artifact != ''
run: |
radon hal ${{ inputs.package_directory }}
- name: Maintainability Index
# if: inputs.artifact != ''
run: |
radon mi ${{ inputs.package_directory }} -s
PyLint:
name: 🩺 Linting
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
if: inputs.pylint == 'true'
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: ⚙ Install dependencies for running PyLint
run: |
python -m pip install --disable-pip-version-check pylint
python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: 🩺 PyLint
# if: inputs.artifact != ''
run: |
pylint ${{ inputs.package_directory }}

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -32,7 +32,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
directory:
description: 'Source code directory to check.'
@@ -41,7 +41,7 @@ on:
fail_under:
description: 'Minimum required documentation coverage level'
required: false
default: 80
default: '80'
type: string
jobs:
@@ -50,23 +50,33 @@ jobs:
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: 🔧 Install wheel,tomli and pip dependencies (native)
- name: 🔧 Install docstr_coverage and interrogate dependencies
run: |
python -m pip install --disable-pip-version-check -U docstr_coverage interrogate
python -m pip install --disable-pip-version-check -U docstr_coverage interrogate[png]
- name: Run 'interrogate' Documentation Coverage Check
continue-on-error: true
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
continue-on-error: true
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

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -46,7 +46,7 @@ on:
unittest_system_list:
description: 'Space separated list of systems to run tests on.'
required: false
default: 'ubuntu windows macos macos-arm mingw64 ucrt64'
default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm mingw64 ucrt64'
type: string
unittest_include_list:
description: 'Space separated list of system:python items to be included into the list of test.'
@@ -56,12 +56,12 @@ on:
unittest_exclude_list:
description: 'Space separated list of system:python items to be excluded from the list of test.'
required: false
default: ''
default: 'windows-arm:3.9 windows-arm:3.10'
type: string
unittest_disable_list:
description: 'Space separated list of system:python items to be disabled from the list of test.'
required: false
default: ''
default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11'
type: string
apptest_python_version:
description: 'Python version.'
@@ -71,12 +71,12 @@ on:
apptest_python_version_list:
description: 'Space separated list of Python versions to run tests with.'
required: false
default: ""
default: ''
type: string
apptest_system_list:
description: 'Space separated list of systems to run tests on.'
required: false
default: 'ubuntu windows macos macos-arm ucrt64'
default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm ucrt64'
type: string
apptest_include_list:
description: 'Space separated list of system:python items to be included into the list of test.'
@@ -86,27 +86,50 @@ on:
apptest_exclude_list:
description: 'Space separated list of system:python items to be excluded from the list of test.'
required: false
default: ''
default: 'windows-arm:3.9 windows-arm:3.10'
type: string
apptest_disable_list:
description: 'Space separated list of system:python items to be disabled from the list of test.'
required: false
default: ''
default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11'
type: string
codecov:
description: 'Publish merged coverage and unittest reports to Codecov.'
required: false
default: 'false'
type: string
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
CODACY_PROJECT_TOKEN:
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:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@main
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@main
with:
package_namespace: ${{ inputs.package_namespace }}
package_name: ${{ inputs.package_name }}
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
@@ -115,32 +138,50 @@ jobs:
package_name: ${{ inputs.package_name }}
python_version: ${{ inputs.unittest_python_version }}
python_version_list: ${{ inputs.unittest_python_version_list }}
system_list: ${{ inputs.unittest_system_list }}
include_list: ${{ inputs.unittest_include_list }}
exclude_list: ${{ inputs.unittest_exclude_list }}
disable_list: ${{ inputs.unittest_disable_list }}
system_list: ${{ inputs.unittest_system_list }}
include_list: ${{ inputs.unittest_include_list }}
exclude_list: ${{ inputs.unittest_exclude_list }}
disable_list: ${{ inputs.unittest_disable_list }}
AppTestingParams:
# AppTestingParams:
# uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
# 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@main
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 }}
python_version: ${{ inputs.unittest_python_version }}
python_version_list: ''
system_list: ${{ inputs.unittest_system_list }}
include_list: ${{ inputs.unittest_include_list }}
exclude_list: ${{ inputs.unittest_exclude_list }}
disable_list: ${{ inputs.unittest_disable_list }}
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main
needs:
- ConfigParams
- UnitTestingParams
with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
# TODO: shouldn't this be configured by a parameter? Same as directories
requirements: "-r tests/unit/requirements.txt"
# pacboy: "msys/git python-lxml:p"
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
StaticTypeCheck:
@@ -149,31 +190,50 @@ jobs:
- ConfigParams
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
commands: |
${{ needs.ConfigParams.outputs.mypy_prepare_command }}
mypy --html-report report/typing -p ${{ needs.ConfigParams.outputs.package_fullname }}
html_report: 'report/typing'
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
cobertura_report: ${{ needs.ConfigParams.outputs.typing_report_cobertura }}
junit_report: ${{ needs.ConfigParams.outputs.typing_report_junit }}
html_report: ${{ needs.ConfigParams.outputs.typing_report_html }}
cobertura_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_cobertura }}
junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_junit }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
CodeQuality:
uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@main
needs:
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
package_directory: ${{ needs.UnitTestingParams.outputs.package_directory }}
artifact: CodeQuality
DocCoverage:
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@main
needs:
- ConfigParams
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
directory: ${{ inputs.package_namespace }}/${{ inputs.package_name }}
# fail_below: 70
directory: ${{ needs.UnitTestingParams.outputs.package_directory }}
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@main
needs:
- UnitTestingParams
- UnitTesting
# - UnitTesting
with:
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@main
needs:
- UnitTestingParams
- InstallParams
- Package
with:
jobs: ${{ needs.InstallParams.outputs.python_jobs }}
wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }}
# AppTesting:
# uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@main
@@ -189,23 +249,40 @@ jobs:
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@main
needs:
- ConfigParams
- UnitTestingParams
- UnitTesting
if: success() || failure()
with:
# coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
# coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
# coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
# coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
# coverage_report_xml_directory: ${{ needs.ConfigParams.outputs.coverage_report_xml_directory }}
# coverage_report_xml_filename: ${{ needs.ConfigParams.outputs.coverage_report_xml_filename }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
codecov: ${{ inputs.codecov }}
codacy: ${{ inputs.codacy }}
secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@main
needs:
- ConfigParams
- UnitTestingParams
- UnitTesting
if: success() || failure()
with:
merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
testsuite-summary-name: ${{ needs.UnitTestingParams.outputs.package_fullname }}
merged_junit_filename: ${{ fromJson(needs.ConfigParams.outputs.unittest_merged_report_xml).filename }}
merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
dorny: ${{ inputs.dorny }}
codecov: ${{ inputs.codecov }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# VerifyDocs:
# uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@main
@@ -222,13 +299,14 @@ jobs:
- PublishTestResults
- PublishCoverageResults
# - VerifyDocs
if: success() || failure()
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-ubuntu-native-3.12
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
IntermediateCleanUp:
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@main
@@ -236,20 +314,21 @@ jobs:
- UnitTestingParams
- PublishCoverageResults
- PublishTestResults
- Documentation
if: ( success() || failure() ) && inputs.cleanup == 'true'
with:
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:
# uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@main
# needs:
# - UnitTestingParams
# - Documentation
# with:
# document: pyEDAA.ProjectModel
# latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
# pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
PDFDocumentation:
uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@main
needs:
- UnitTestingParams
- Documentation
with:
document: ${{ needs.UnitTestingParams.outputs.package_fullname }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
can-fail: 'true'
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main
@@ -260,30 +339,61 @@ jobs:
- PublishCoverageResults
- StaticTypeCheck
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 }}
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@main
needs:
- Prepare
- UnitTesting
- Install
# - AppTesting
# - StaticTypeCheck
- Package
- PublishToGitHubPages
if: needs.Prepare.outputs.is_release_commit == 'true' && github.event_name != 'schedule'
permissions:
contents: write # required for create tag
actions: write # required for trigger workflow
with:
version: ${{ needs.Prepare.outputs.version }}
auto_tag: ${{ needs.Prepare.outputs.is_release_commit }}
secrets: inherit
ReleasePage:
uses: pyTooling/Actions/.github/workflows/Release.yml@main
if: startsWith(github.ref, 'refs/tags')
uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@main
needs:
- Package
- Prepare
- UnitTesting
- Install
# - AppTesting
# - 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:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@main
if: startsWith(github.ref, 'refs/tags')
needs:
- Prepare
- UnitTestingParams
- Package
- ReleasePage
if: needs.Prepare.outputs.is_release_tag == 'true'
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
requirements: '-r dist/requirements.txt'
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main
@@ -298,6 +408,7 @@ jobs:
- PublishToGitHubPages
# - PublishOnPyPI
- IntermediateCleanUp
if: inputs.cleanup == 'true'
with:
package: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
remaining: |

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -71,17 +71,17 @@ jobs:
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps:
- name: '❗ Deprecation message'
run: echo "::warning title=Deprecated::'CoverageCollection.yml' is not maintained anymore. Please switch to 'UnitTesting.yml', 'PublishCoverageResults.yml' and 'PublishTestResults.yml'."
- name: ⚠️ Deprecation Warning
run: printf "::warning title=%s::%s\n" "Deprecated" "'CoverageCollection.yml' template is deprecated. Please switch to 'PublishReleaseNotes.yml'. See https://pytooling.github.io/Actions/JobTemplate/Testing/UnitTesting.html"
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -150,7 +150,7 @@ jobs:
ABSDIR=$(pwd)
cd "${{ inputs.tests_directory || '.' }}"
[ -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
- name: Convert to cobertura format

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -32,16 +32,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
type: string
package_namespace:
description: 'Name of the tool''s namespace.'
required: false
default: ''
type: string
package_name:
description: 'Name of the tool''s package.'
required: true
default: '3.13'
type: string
coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
@@ -50,51 +41,51 @@ on:
type: string
outputs:
package_fullname:
unittest_report_xml:
description: ""
value: ${{ jobs.Extract.outputs.package_fullname }}
package_directory:
value: ${{ jobs.Extract.outputs.unittest_report_xml }}
unittest_merged_report_xml:
description: ""
value: ${{ jobs.Extract.outputs.package_directory }}
mypy_prepare_command:
value: ${{ jobs.Extract.outputs.unittest_merged_report_xml }}
coverage_report_html:
description: ""
value: ${{ jobs.Extract.outputs.mypy_prepare_command }}
coverage_report_html_directory:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_html_directory }}
coverage_report_xml_directory:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_xml_directory }}
value: ${{ jobs.Extract.outputs.coverage_report_html }}
coverage_report_xml:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_xml }}
coverage_report_json_directory:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_json_directory }}
coverage_report_json:
description: ""
value: ${{ jobs.Extract.outputs.coverage_report_json }}
typing_report_cobertura:
description: ""
value: ${{ jobs.Extract.outputs.typing_report_cobertura }}
typing_report_junit:
description: ""
value: ${{ jobs.Extract.outputs.typing_report_junit }}
typing_report_html:
description: ""
value: ${{ jobs.Extract.outputs.typing_report_html }}
jobs:
Extract:
name: 📓 Extract configurations from pyproject.toml
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
outputs:
package_fullname: ${{ steps.getPackageName.outputs.package_fullname }}
package_directory: ${{ steps.getPackageName.outputs.package_directory }}
mypy_prepare_command: ${{ steps.getPackageName.outputs.mypy_prepare_command }}
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: ${{ steps.getVariables.outputs.coverage_report_xml }}
coverage_report_json_directory: ${{ steps.getVariables.outputs.coverage_report_json_directory }}
coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }}
unittest_report_xml: ${{ steps.getVariables.outputs.unittest_report_xml }}
unittest_merged_report_xml: ${{ steps.getVariables.outputs.unittest_merged_report_xml }}
coverage_report_html: ${{ steps.getVariables.outputs.coverage_report_html }}
coverage_report_xml: ${{ steps.getVariables.outputs.coverage_report_xml }}
coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }}
typing_report_cobertura: ${{ steps.getVariables.outputs.typing_report_cobertura }}
typing_report_junit: ${{ steps.getVariables.outputs.typing_report_junit }}
typing_report_html: ${{ steps.getVariables.outputs.typing_report_html }}
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -102,52 +93,28 @@ jobs:
run: |
python -m pip install --disable-pip-version-check -U wheel tomli
- name: 🔁 Full package name and directory
id: getPackageName
shell: python
run: |
from os import getenv
from pathlib import Path
from textwrap import dedent
namespace = "${{ inputs.package_namespace }}".strip()
name = "${{ inputs.package_name }}".strip()
if namespace == "" or namespace == ".":
fullname = f"{name}"
directory = f"{name}"
mypy_prepare_command = ""
else:
fullname = f"{namespace}.{name}"
directory = f"{namespace}/{name}"
mypy_prepare_command = f"touch {namespace}/__init__.py"
github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f:
f.write(dedent(f"""\
package_fullname={fullname}
package_directory={directory}
mypy_prepare_command={mypy_prepare_command}
"""))
- name: 🔁 Extract configurations from pyproject.toml
id: getVariables
shell: python
run: |
from json import dumps as json_dumps
from os import getenv
from pathlib import Path
from sys import version
from textwrap import dedent
print(f"Python: {version}")
print(f"Python: {version} (of default installation)")
from tomli import load as tomli_load
htmlDirectory = Path("htmlcov")
xmlFile = Path("./coverage.xml")
jsonFile = Path("./coverage.json")
coverageRC = "${{ inputs.coverage_config }}".strip()
unittestXMLFile = Path("./unittest.xml")
coverageHTMLDirectory = Path("htmlcov")
coverageXMLFile = Path("./coverage.xml")
coverageJSONFile = Path("./coverage.json")
coverageRC = "${{ inputs.coverage_config }}".strip()
typingCoberturaFile = Path("report/typing/cobertura.xml")
typingJUnitFile = Path("report/typing/StaticTypingSummary.xml")
typingHTMLDirectory = Path("htmlmypy")
# Read output paths from 'pyproject.toml' file
if coverageRC == "pyproject.toml":
@@ -156,9 +123,14 @@ jobs:
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"])
unittestXMLFile = Path(pyProjectSettings["tool"]["pytest"]["junit_xml"])
mergedUnittestXMLFile = Path(pyProjectSettings["tool"]["pyedaa-reports"]["junit_xml"])
coverageHTMLDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"])
coverageXMLFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"])
coverageJSONFile= Path(pyProjectSettings["tool"]["coverage"]["json"]["output"])
typingCoberturaFile = Path(pyProjectSettings["tool"]["mypy"]["cobertura_xml_report"]) / "cobertura.xml"
typingJUnitFile = Path(pyProjectSettings["tool"]["mypy"]["junit_xml"])
typingHTMLDirectory = Path(pyProjectSettings["tool"]["mypy"]["html_report"])
else:
print(f"File '{pyProjectFile}' not found.")
print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.")
@@ -166,29 +138,123 @@ jobs:
# Read output paths from '.coveragerc' file
elif len(coverageRC) > 0:
print(f"::warning title=Deprecated::Using '{coverageRCFile}' is deprecated. Please use 'pyproject.toml'.")
coverageRCFile = Path(coverageRC)
if coverageRCFile.exists():
with coverageRCFile.open("rb") as file:
coverageRCSettings = tomli_load(file)
htmlDirectory = Path(coverageRCSettings["html"]["directory"])
xmlFile = Path(coverageRCSettings["xml"]["output"])
jsonFile = Path(coverageRCSettings["json"]["output"])
coverageHTMLDirectory = Path(coverageRCSettings["html"]["directory"])
coverageXMLFile = Path(coverageRCSettings["xml"]["output"])
coverageJSONFile = Path(coverageRCSettings["json"]["output"])
else:
print(f"File '{coverageRCFile}' not found.")
print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.")
exit(1)
unittest_report_xml = {
"fullpath": unittestXMLFile.as_posix(),
"directory": unittestXMLFile.parent.as_posix(),
"filename": unittestXMLFile.name
}
unittest_merged_report_xml = {
"fullpath": mergedUnittestXMLFile.as_posix(),
"directory": mergedUnittestXMLFile.parent.as_posix(),
"filename": mergedUnittestXMLFile.name
}
coverage_report_html = {
"fullpath": coverageHTMLDirectory.as_posix(),
"directory": coverageHTMLDirectory.as_posix()
}
coverage_report_xml = {
"fullpath": coverageXMLFile.as_posix(),
"directory": coverageXMLFile.parent.as_posix(),
"filename": coverageXMLFile.name
}
coverage_report_json = {
"fullpath": coverageJSONFile.as_posix(),
"directory": coverageJSONFile.parent.as_posix(),
"filename": coverageJSONFile.name
}
typing_report_cobertura = {
"fullpath": typingCoberturaFile.as_posix(),
"directory": typingCoberturaFile.parent.as_posix(),
"filename": typingCoberturaFile.name
}
typing_report_junit = {
"fullpath": typingJUnitFile.as_posix(),
"directory": typingJUnitFile.parent.as_posix(),
"filename": typingJUnitFile.name
}
typing_report_html = {
"fullpath": typingHTMLDirectory.as_posix(),
"directory": typingHTMLDirectory.as_posix()
}
# Write jobs to special file
github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f:
f.write(dedent(f"""\
coverage_report_html_directory={htmlDirectory.as_posix()}
coverage_report_xml_directory={xmlFile.parent.as_posix()}
coverage_report_xml={xmlFile.as_posix()}
coverage_report_json_directory={jsonFile.parent.as_posix()}
coverage_report_json={jsonFile.as_posix()}
unittest_report_xml={json_dumps(unittest_report_xml)}
unittest_merged_report_xml={json_dumps(unittest_merged_report_xml)}
coverage_report_html={json_dumps(coverage_report_html)}
coverage_report_xml={json_dumps(coverage_report_xml)}
coverage_report_json={json_dumps(coverage_report_json)}
typing_report_cobertura={json_dumps(typing_report_cobertura)}
typing_report_junit={json_dumps(typing_report_junit)}
typing_report_html={json_dumps(typing_report_html)}
"""))
print(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}
typing cobertura: {typingCoberturaFile}
typing junit: {typingJUnitFile}
typing html: {typingHTMLDirectory}
"""))
- name: Debug JSON objects
run: |
printf "unittest_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.unittest_report_xml }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_report_xml).filename }}"
printf "unittest_merged_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.unittest_merged_report_xml }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.unittest_merged_report_xml).filename }}"
printf "coverage_report_html: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_html }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_html).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_html).directory }}"
printf "coverage_report_xml: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_xml }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_xml).filename }}"
printf "coverage_report_json: JSON=%s\n" "${{ steps.getVariables.outputs.coverage_report_json }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.coverage_report_json).filename }}"
printf "typing_report_cobertura: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_cobertura }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_cobertura).filename }}"
printf "typing_report_junit: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_junit }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).directory }}"
printf " filename: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_junit).filename }}"
printf "typing_report_html: JSON=%s\n" "${{ steps.getVariables.outputs.typing_report_html }}"
printf " fullpath: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_html).fullpath }}"
printf " directory: %s\n" "${{ fromJSON(steps.getVariables.outputs.typing_report_html).directory }}"

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@v6
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' && matrix.system != 'windows-arm' )
run: |
python -m pip install --disable-pip-version-check -U install/*.whl
- name: 🔧 Install wheel from artifact (Windows)
if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' )
run: |
python -m pip install -v --disable-pip-version-check (Get-Item .\install\*.whl).FullName
- name: 📦 Run Package Version Check (Ubuntu/macOS)
if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' )
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 Package Version Check (Windows)
if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' )
run: |
$result=$(python -c "from ${{ inputs.package_name }} import __version__; print(f""Package version: {__version__}"")")
Write-Host $result
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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -30,12 +30,14 @@ on:
default: '24.04'
type: string
sqlite_coverage_artifacts_prefix:
description: 'Prefix for SQLite coverage artifacts'
description: 'Prefix for SQLite coverage artifacts to be removed.'
required: false
default: ''
type: string
xml_unittest_artifacts_prefix:
description: 'Prefix for XML unittest artifacts'
description: 'Prefix for XML unittest artifacts to be removed.'
required: false
default: ''
type: string
jobs:

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -29,43 +29,65 @@ on:
required: false
default: '24.04'
type: string
latex_artifact:
description: 'Name of the LaTeX documentation artifact.'
required: true
type: string
document:
description: 'LaTeX root document without *.tex extension.'
required: true
type: string
latex_artifact:
description: 'Name of the LaTeX documentation artifact.'
processor:
description: 'Name of the used LaTeX processor.'
required: false
default: ''
default: 'xelatex'
type: string
pdf_artifact:
description: 'Name of the PDF documentation artifact.'
required: false
default: ''
type: string
can-fail:
description: 'Translation from LaTeX to PDF may fail.'
required: false
default: 'false'
type: string
jobs:
PDFDocumentation:
name: 📓 Converting LaTeX Documentation to PDF
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
continue-on-error: ${{ inputs.can-fail == 'true' }}
steps:
- name: 📥 Download artifacts '${{ inputs.latex_artifact }}' from 'SphinxDocumentation' job
uses: pyTooling/download-artifact@v4
uses: pyTooling/download-artifact@v5
with:
name: ${{ inputs.latex_artifact }}
path: latex
- name: Compile LaTeX document
uses: xu-cheng/latex-action@master
# - name: Debug
# run: |
# tree -pash .
- name: Build LaTeX document using 'pytooling/miktex:sphinx'
uses: addnab/docker-run-action@v3
if: inputs.pdf_artifact != ''
with:
working_directory: latex
root_file: ${{ inputs.document }}.tex
image: pytooling/miktex:sphinx
options: -v ${{ github.workspace }}/latex:/latex --workdir /latex
run: |
# which ${{ inputs.processor }}
# pwd
# ls -lAh
latexmk -${{ inputs.processor }} "${{ inputs.document }}.tex"
- name: 📤 Upload 'PDF Documentation' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.pdf_artifact != ''
with:
name: ${{ inputs.pdf_artifact }}
working-directory: latex
path: ${{ inputs.document }}.pdf
if-no-files-found: error
retention-days: 1

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -68,23 +68,43 @@ on:
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
actions: write
# attestations: write
steps:
- name: ⚠️ Deprecation Warning
run: printf "::warning title=%s::%s\n" "NightlyRelease" "'NightlyRelease.yml' template is deprecated. Please switch to 'PublishReleaseNotes.yml'. See https://pytooling.github.io/Actions/JobTemplate/Release/PublishReleaseNotes.html"
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# The command 'git describe' (used for version) needs the history.
fetch-depth: 0
@@ -97,23 +117,23 @@ jobs:
run: |
set +e
ANSI_LIGHT_RED="\e[91m"
ANSI_LIGHT_GREEN="\e[92m"
ANSI_LIGHT_YELLOW="\e[93m"
ANSI_NOCOLOR="\e[0m"
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_LIGHT_YELLOW=$'\x1b[93m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
echo -n "Deleting release '${{ inputs.nightly_name }}' ... "
printf "%s" "Deleting release '${{ inputs.nightly_name }}' ... "
message="$(gh release delete ${{ inputs.nightly_name }} --yes 2>&1)"
if [[ $? -eq 0 ]]; then
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
elif [[ "${message}" == "release not found" ]]; then
echo -e "${ANSI_LIGHT_YELLOW}[NOT FOUND]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_YELLOW}[NOT FOUND]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}"
echo "::error title=InternalError::Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'."
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
@@ -122,19 +142,19 @@ jobs:
run: |
set +e
ANSI_LIGHT_RED="\e[91m"
ANSI_LIGHT_GREEN="\e[92m"
ANSI_NOCOLOR="\e[0m"
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_NOCOLOR=$'\x1b[0m'
export GH_TOKEN=${{ github.token }}
addDraft="--draft"
if ${{ inputs.prerelease }}; then
if [[ "${{ inputs.prerelease }}" == "true" ]]; then
addPreRelease="--prerelease"
fi
if ! ${{ inputs.latest }}; then
if [[ "${{ inputs.latest }}" == "false" ]]; then
addLatest="--latest=false"
fi
@@ -163,17 +183,17 @@ jobs:
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').
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
echo "Creating release '${{ inputs.nightly_name }}' ... "
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
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}"
echo "::error title=InternalError::Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'."
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
@@ -182,10 +202,11 @@ jobs:
run: |
set +e
ANSI_LIGHT_RED="\e[91m"
ANSI_LIGHT_GREEN="\e[92m"
ANSI_LIGHT_YELLOW="\e[93m"
ANSI_NOCOLOR="\e[0m"
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 }}
@@ -199,199 +220,293 @@ jobs:
replacement="${patternLine#*=}"
line="${line//"%$pattern%"/"$replacement"}"
done <<<'${{ inputs.replacements }}'
echo "$line"
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}" == "" ]]; then
if [[ "${assetLine}" == "" || "${assetLine:0:1}" == "#" ]]; then
continue
fi
# split assetLine colon separated triple: artifact:asset:title
artifact="${assetLine%%:*}"
remaining="${assetLine#*:}"
asset="${remaining%%:*}"
title="${remaining##*:}"
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}")"
echo "Publish asset '${asset}' from artifact '${artifact}' with title '${title}'"
echo -n " Checked asset for duplicates ... "
printf "%s\n" "Publish asset '${asset}' from artifact '${artifact}' with title '${title}'"
printf " %s" "Checked asset for duplicates ... "
if [[ -n "${assetFilenames[$asset]}" ]]; then
echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo "::error title=DuplicateAsset::Asset '${asset}' from artifact '${artifact}' was already uploaded to release '${{ inputs.nightly_name }}'."
ERRORS=1
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
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
assetFilenames[$asset]=1
fi
# Download artifact by artifact name
if [[ -n "${downloadedArtifacts[$artifact]}" ]]; then
echo -e " downloading '${artifact}' ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
printf " %s\n" "downloading '${artifact}' ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
else
echo " downloading '${artifact}' ... "
echo -n " gh run download $GITHUB_RUN_ID --dir \"${artifact}\" --name \"${artifact}\" "
printf " downloading '${artifact}' ...\n"
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
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't download artifact '${artifact}'.${ANSI_NOCOLOR}"
echo "::error title=ArtifactNotFound::Couldn't download artifact '${artifact}'."
ERRORS=1
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
echo -n " Checking for embedded tarball ... "
printf " %s" "Checking for embedded tarball ... "
if [[ -f "${artifact}/${{ inputs.tarball-name }}" ]]; then
echo -e "${ANSI_LIGHT_GREEN}[FOUND]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[FOUND]${ANSI_NOCOLOR}"
pushd "${artifact}" > /dev/null
echo -n " Extracting embedded tarball ... "
printf " %s" "Extracting embedded tarball ... "
tar -xf "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
fi
echo -n " Removing temporary tarball ... "
printf " %s" "Removing temporary tarball ... "
rm -f "${{ inputs.tarball-name }}"
if [[ $? -ne 0 ]]; then
echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
fi
popd > /dev/null
else
echo -e "${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}"
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.
echo -n " checking asset '${artifact}/${asset}' ... "
printf " %s" "checking asset '${artifact}/${asset}' ... "
if [[ "${asset}" == !*.zip ]]; then
echo -e "${ANSI_LIGHT_GREEN}[ZIP]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[ZIP]${ANSI_NOCOLOR}"
asset="${asset##*!}"
echo " Compressing artifact '${artifact}' to '${asset}' ..."
printf "::group:: %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
zip -r "../${asset}" *
)
if [[ $? -eq 0 ]]; then
echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
retCode=$?
printf "::endgroup::\n"
if [[ $retCode -eq 0 ]]; then
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zip file '${asset}'.${ANSI_NOCOLOR}"
echo "::error title=CompressionError::Couldn't compress '${artifact}' to zip file '${asset}'."
ERRORS=1
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
echo -e "${ANSI_LIGHT_GREEN}[TAR/GZ]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[TAR/GZ]${ANSI_NOCOLOR}"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
echo " Compressing artifact '${artifact}' to '${asset}' ..."
tar -c --gzip --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
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##*!}"
echo " Compressing artifact '${artifact}' to '${asset}' ..."
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
tar -c --gzip --file="../${asset}" *
tar -c --gzip --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to tgz file '${asset}'.${ANSI_NOCOLOR}"
echo "::error title=CompressionError::Couldn't compress '${artifact}' to tgz file '${asset}'."
ERRORS=1
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
echo -e "${ANSI_LIGHT_GREEN}[ZST]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[ZST]${ANSI_NOCOLOR}"
if [[ "${asset:0:1}" == "\$" ]]; then
asset="${asset##*$}"
dirName="${asset%.*}"
echo " Compressing artifact '${artifact}' to '${asset}' ..."
tar -c --zstd --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" .
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##*!}"
echo " Compressing artifact '${artifact}' to '${asset}' ..."
printf " %s\n" "Compressing artifact '${artifact}' to '${asset}' ..."
(
cd "${artifact}" && \
tar -c --zstd --file="../${asset}" *
tar -c --zstd --owner=0 --group=0 --file="../${asset}" *
)
retCode=$?
fi
if [[ $retCode -eq 0 ]]; then
echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf " %s\n" "Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${asset}"
else
echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zst file '${asset}'.${ANSI_NOCOLOR}"
echo "::error title=CompressionError::Couldn't compress '${artifact}' to zst file '${asset}'."
ERRORS=1
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
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
uploadFile="${artifact}/${asset}"
else
echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't find asset '${asset}' in artifact '${artifact}'.${ANSI_NOCOLOR}"
echo "::error title=FileNotFound::Couldn't find asset '${asset}' in artifact '${artifact}'."
ERRORS=1
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
echo -n " uploading asset '${asset}' from '${uploadFile}' with title '${title}' ... "
printf " %s" "uploading asset '${asset}' from '${uploadFile}' with title '${title}' ... "
gh release upload ${{ inputs.nightly_name }} "${uploadFile}#${title}" --clobber
if [[ $? -eq 0 ]]; then
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}"
echo "::error title=UploadError::Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'."
ERRORS=1
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 }}'
echo "Inspecting downloaded artifacts ..."
tree -L 3 .
if [[ "${{ inputs.inventory-json }}" != "" ]]; then
inventoryTitle="Release Inventory (JSON)"
if [[ $ERROR -ne 0 ]]; then
echo -e "${ANSI_LIGHT_RED}Errors detected in previous steps.${ANSI_NOCOLOR}"
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
@@ -400,19 +515,19 @@ jobs:
run: |
set +e
ANSI_LIGHT_RED="\e[91m"
ANSI_LIGHT_GREEN="\e[92m"
ANSI_NOCOLOR="\e[0m"
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
echo -n "Remove draft-state from release '${title}' ... "
printf "%s" "Remove draft-state from release '${title}' ... "
gh release edit --draft=false "${{ inputs.nightly_name }}"
if [[ $? -eq 0 ]]; then
echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
else
echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}"
echo -e "${ANSI_LIGHT_RED}Couldn't remove draft-state from release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}"
echo "::error title=ReleasePage::Couldn't remove draft-state from release '${{ inputs.nightly_name }}'."
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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -33,7 +33,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
requirements:
description: 'Python dependencies to be installed through pip; if empty, use pyproject.toml through build.'
@@ -46,20 +46,20 @@ on:
type: string
jobs:
Package:
name: 📦 Package in Source and Wheel Format
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
env:
artifact: ${{ inputs.artifact }}
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -79,15 +79,15 @@ jobs:
# build (not isolated)
- name: 🔧 [build] Install dependencies for packaging and release
- name: 🔧 [build - no-isolation] Install dependencies for packaging and release
if: inputs.requirements == 'no-isolation'
run: python -m pip install --disable-pip-version-check build
- name: 🔨 [build] Build Python package (source distribution)
- name: 🔨 [build - no-isolation] Build Python package (source distribution)
if: inputs.requirements == 'no-isolation'
run: python -m build --no-isolation --sdist
- name: 🔨 [build] Build Python package (binary distribution - wheel)
- name: 🔨 [build - no-isolation] Build Python package (binary distribution - wheel)
if: inputs.requirements == 'no-isolation'
run: python -m build --no-isolation --wheel

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -58,7 +58,7 @@ on:
system_list:
description: 'Space separated list of systems to run tests on.'
required: false
default: 'ubuntu windows macos macos-arm mingw64 ucrt64'
default: 'ubuntu ubuntu-arm windows windows-arm macos macos-arm mingw64 ucrt64'
type: string
include_list:
description: 'Space separated list of system:python items to be included into the list of test.'
@@ -68,22 +68,32 @@ on:
exclude_list:
description: 'Space separated list of system:python items to be excluded from the list of test.'
required: false
default: ''
default: 'windows-arm:3.9 windows-arm:3.10'
type: string
disable_list:
description: 'Space separated list of system:python items to be disabled from the list of test.'
required: false
default: ''
default: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11'
type: string
ubuntu_image:
description: 'The used GitHub Action image for Ubuntu based jobs.'
description: 'The used GitHub Action image for Ubuntu (x86-64) based jobs.'
required: false
default: 'ubuntu-24.04'
type: string
windows_image:
description: 'The used GitHub Action image for Windows based jobs.'
ubuntu_arm_image:
description: 'The used GitHub Action image for Ubuntu (aarch64) based jobs.'
required: false
default: 'windows-2022'
default: 'ubuntu-24.04-arm'
type: string
windows_image:
description: 'The used GitHub Action image for Windows Server (x86-64) based jobs.'
required: false
default: 'windows-2025'
type: string
windows_arm_image:
description: 'The used GitHub Action image for Windows (aarch64) based jobs.'
required: false
default: 'windows-11-arm'
type: string
macos_intel_image:
description: 'The used GitHub Action image for macOS (Intel x86-64) based jobs.'
@@ -93,36 +103,145 @@ on:
macos_arm_image:
description: 'The used GitHub Action image for macOS (ARM aarch64) based jobs.'
required: false
default: 'macos-14'
default: 'macos-15'
type: string
pipeline-delay:
description: 'Slow down this job, to delay the startup of the GitHub Action pipline.'
required: false
default: 0
type: number
outputs:
python_version:
description: "Default Python version for other jobs."
value: ${{ jobs.Parameters.outputs.python_version }}
python_jobs:
description: "List of Python versions (and system combinations) to be used in the matrix of other jobs."
value: ${{ jobs.Parameters.outputs.python_jobs }}
package_fullname:
description: "The package's full name."
value: ${{ jobs.Parameters.outputs.package_fullname }}
package_directory:
description: "The package's directory."
value: ${{ jobs.Parameters.outputs.package_directory }}
artifact_basename:
description: "Artifact base name."
value: ${{ jobs.Parameters.outputs.artifact_basename }}
artifact_names:
description: "Pre-defined artifact names for other jobs."
value: ${{ jobs.Parameters.outputs.artifact_names }}
params:
description: "Parameters to be used in other jobs."
value: ${{ jobs.Parameters.outputs.params }}
python_jobs:
description: "List of Python versions (and system combinations) to be used in the matrix of other jobs."
value: ${{ jobs.Parameters.outputs.python_jobs }}
jobs:
Parameters:
name: ✎ Generate pipeline parameters
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
outputs:
python_version: ${{ steps.params.outputs.python_version }}
python_jobs: ${{ steps.params.outputs.python_jobs }}
artifact_names: ${{ steps.params.outputs.artifact_names }}
params: ${{ steps.params.outputs.params }}
python_version: ${{ steps.variables.outputs.python_version }}
package_fullname: ${{ steps.variables.outputs.package_fullname }}
package_directory: ${{ steps.variables.outputs.package_directory }}
artifact_basename: ${{ steps.variables.outputs.artifact_basename }}
artifact_names: ${{ steps.artifacts.outputs.artifact_names }}
python_jobs: ${{ steps.jobs.outputs.python_jobs }}
steps:
- name: Generate 'params' and 'python_jobs'
id: params
- name: Generate a startup delay of ${{ inputs.pipeline-delay }} seconds
id: delay
if: inputs.pipeline-delay >= 0
run: |
sleep ${{ inputs.pipeline-delay }}
- name: Generate 'python_version'
id: variables
shell: python
run: |
from os import getenv
from pathlib import Path
from sys import exit
from textwrap import dedent
python_version = "${{ inputs.python_version }}".strip()
package_namespace = "${{ inputs.package_namespace }}".strip()
package_name = "${{ inputs.package_name }}".strip()
name = "${{ inputs.name }}".strip()
if package_namespace == "": # or package_namespace == ".":
package_fullname = package_name
package_directory = package_name
elif package_namespace[-2:] == ".*":
package_fullname = package_namespace[:-2]
package_directory = package_namespace[:-2]
else:
package_fullname = f"{package_namespace}.{package_name}"
package_directory = f"{package_namespace}/{package_name}"
artifact_basename = package_fullname if name == "" else name
if artifact_basename == "" or artifact_basename == ".":
print("::error title=Parameter::artifact_basename is empty.")
exit(1)
print("Variables:")
print(f" python_version: {python_version}")
print(f" package_fullname: {package_fullname}")
print(f" package_directory: {package_directory}")
print(f" artifact_basename: {artifact_basename}")
# Write jobs to special file
github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f:
f.write(dedent(f"""\
python_version={python_version}
package_fullname={package_fullname}
package_directory={package_directory}
artifact_basename={artifact_basename}
"""))
- name: Generate 'artifact_names'
id: artifacts
shell: python
run: |
from json import dumps as json_dumps
from os import getenv
from pathlib import Path
from textwrap import dedent
package_namespace = "${{ inputs.package_namespace }}".strip()
package_name = "${{ inputs.package_name }}".strip()
artifact_basename = "${{ steps.variables.outputs.artifact_basename }}"
artifact_names = {
"unittesting_xml": f"{artifact_basename}-UnitTestReportSummary-XML",
"unittesting_html": f"{artifact_basename}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{artifact_basename}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{artifact_basename}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{artifact_basename}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{artifact_basename}-CodeCoverage-SQLite",
"codecoverage_xml": f"{artifact_basename}-CodeCoverage-XML",
"codecoverage_json": f"{artifact_basename}-CodeCoverage-JSON",
"codecoverage_html": f"{artifact_basename}-CodeCoverage-HTML",
"statictyping_cobertura": f"{artifact_basename}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{artifact_basename}-StaticTyping-JUnit-XML",
"statictyping_html": f"{artifact_basename}-StaticTyping-HTML",
"package_all": f"{artifact_basename}-Packages",
"documentation_html": f"{artifact_basename}-Documentation-HTML",
"documentation_latex": f"{artifact_basename}-Documentation-LaTeX",
"documentation_pdf": f"{artifact_basename}-Documentation-PDF",
}
print("Artifacts Names ({len(artifact_names)}):")
for id, artifactName in artifact_names.items():
print(f" {id:>24}: {artifactName}")
# Write jobs to special file
github_output = Path(getenv("GITHUB_OUTPUT"))
print(f"GITHUB_OUTPUT: {github_output}")
with github_output.open("a+", encoding="utf-8") as f:
f.write(dedent(f"""\
artifact_names={json_dumps(artifact_names)}
"""))
- name: Generate 'python_jobs'
id: jobs
shell: python
run: |
from json import dumps as json_dumps
@@ -131,25 +250,17 @@ jobs:
from textwrap import dedent
from typing import Iterable
package_namespace = "${{ inputs.package_namespace }}".strip()
package_name = "${{ inputs.package_name }}".strip()
name = "${{ inputs.name }}".strip()
python_version = "${{ inputs.python_version }}".strip()
python_version = "${{ steps.variables.outputs.python_version }}"
name = "${{ steps.variables.outputs.artifact_basename }}"
systems = "${{ inputs.system_list }}".strip()
versions = "${{ inputs.python_version_list }}".strip()
include_list = "${{ inputs.include_list }}".strip()
exclude_list = "${{ inputs.exclude_list }}".strip()
disable_list = "${{ inputs.disable_list }}".strip()
if name == "":
if package_namespace == "" or package_namespace == ".":
name = f"{package_name}"
else:
name = f"{package_namespace}.{package_name}"
currentMSYS2Version = "3.11"
currentMSYS2Version = "3.12"
currentAlphaVersion = "3.14"
currentAlphaRelease = "3.14.0-alpha.1"
currentAlphaRelease = "3.14.0-rc.3"
if systems == "":
print("::error title=Parameter::system_list is empty.")
@@ -183,7 +294,7 @@ jobs:
if currentAlphaVersion in versions:
print(f"::notice title=Experimental::Python {currentAlphaVersion} ({currentAlphaRelease}) is a pre-release.")
for disable in disabled:
print(f"::warning title=Disabled Python Job::System '{disable}' temporarily disabled.")
print(f"::warning title=Disabled Python Job::{name}: Job combination '{disable}' temporarily disabled.")
# see https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json
data = {
@@ -197,16 +308,19 @@ jobs:
"3.13": { "icon": "🟢", "until": "2029.10" },
"3.14": { "icon": "🟣", "until": "2030.10" },
"pypy-3.7": { "icon": "⟲⚫", "until": "????.??" },
"pypy-3.8": { "icon": "⟲🔴", "until": "????.??" },
"pypy-3.9": { "icon": "⟲🟠", "until": "????.??" },
"pypy-3.10": { "icon": "⟲🟡", "until": "????.??" },
"pypy-3.8": { "icon": "⟲", "until": "????.??" },
"pypy-3.9": { "icon": "⟲🔴", "until": "????.??" },
"pypy-3.10": { "icon": "⟲🟠", "until": "????.??" },
"pypy-3.11": { "icon": "⟲🟡", "until": "????.??" },
},
# Runner systems (runner images) supported by GitHub Actions
"sys": {
"ubuntu": { "icon": "🐧", "runs-on": "${{ inputs.ubuntu_image }}", "shell": "bash", "name": "Linux (x86-64)" },
"windows": { "icon": "🪟", "runs-on": "${{ inputs.windows_image }}", "shell": "pwsh", "name": "Windows (x86-64)" },
"macos": { "icon": "🍎", "runs-on": "${{ inputs.macos_intel_image }}", "shell": "bash", "name": "macOS (x86-64)" },
"macos-arm": { "icon": "🍏", "runs-on": "${{ inputs.macos_arm_image }}", "shell": "bash", "name": "macOS (aarch64)" },
"ubuntu": { "icon": "🐧", "runs-on": "${{ inputs.ubuntu_image }}", "shell": "bash", "name": "Linux (x86-64)" },
"ubuntu-arm": { "icon": "", "runs-on": "${{ inputs.ubuntu_arm_image }}", "shell": "bash", "name": "Linux (aarch64)" },
"windows": { "icon": "🪟", "runs-on": "${{ inputs.windows_image }}", "shell": "pwsh", "name": "Windows (x86-64)" },
"windows-arm": { "icon": "🏢", "runs-on": "${{ inputs.windows_arm_image }}", "shell": "pwsh", "name": "Windows (aarch64)" },
"macos": { "icon": "🍎", "runs-on": "${{ inputs.macos_intel_image }}", "shell": "bash", "name": "macOS (x86-64)" },
"macos-arm": { "icon": "🍏", "runs-on": "${{ inputs.macos_arm_image }}", "shell": "bash", "name": "macOS (aarch64)" },
},
# Runtimes provided by MSYS2
"runtime": {
@@ -288,7 +402,7 @@ jobs:
{
"sysicon": data["runtime"][runtime]["icon"],
"system": "msys2",
"runs-on": "windows-latest",
"runs-on": "${{ inputs.windows_image }}",
"runtime": runtime.upper(),
"shell": "msys2 {0}",
"pyicon": data["python"][currentMSYS2Version]["icon"],
@@ -298,31 +412,11 @@ jobs:
for runtime, version in combinations if runtime not in data["sys"]
]
artifact_names = {
"unittesting_xml": f"{name}-UnitTestReportSummary-XML",
"unittesting_html": f"{name}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{name}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{name}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{name}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{name}-CodeCoverage-SQLite",
"codecoverage_xml": f"{name}-CodeCoverage-XML",
"codecoverage_json": f"{name}-CodeCoverage-JSON",
"codecoverage_html": f"{name}-CodeCoverage-HTML",
"statictyping_html": f"{name}-StaticTyping-HTML",
"package_all": f"{name}-Packages",
"documentation_html": f"{name}-Documentation-HTML",
"documentation_latex": f"{name}-Documentation-LaTeX",
"documentation_pdf": f"{name}-Documentation-PDF",
}
print("Parameters:")
print(f" python_version: {python_version}")
print(f" python_jobs ({len(jobs)}):\n" +
"".join([f" {{ " + ", ".join([f"\"{key}\": \"{value}\"" for key, value in job.items()]) + f" }},\n" for job in jobs])
)
print(f" artifact_names ({len(artifact_names)}):")
for id, name in artifact_names.items():
print(f" {id:>20}: {name}")
# Write jobs to special file
github_output = Path(getenv("GITHUB_OUTPUT"))
@@ -331,13 +425,17 @@ jobs:
f.write(dedent(f"""\
python_version={python_version}
python_jobs={json_dumps(jobs)}
artifact_names={json_dumps(artifact_names)}
"""))
- name: Verify out parameters
id: verify
run: |
echo 'python_version: ${{ steps.params.outputs.python_version }}'
echo 'python_jobs: ${{ steps.params.outputs.python_jobs }}'
echo 'artifact_names: ${{ steps.params.outputs.artifact_names }}'
echo 'params: ${{ steps.params.outputs.params }}'
printf "python_version: %s\n" '${{ steps.variables.outputs.python_version }}'
printf "package_fullname: %s\n" '${{ steps.variables.outputs.package_fullname }}'
printf "package_directory: %s\n" '${{ steps.variables.outputs.package_directory }}'
printf "artifact_basename: %s\n" '${{ steps.variables.outputs.artifact_basename }}'
printf "====================\n"
printf "artifact_names: %s\n" '${{ steps.artifacts.outputs.artifact_names }}'
printf "====================\n"
printf "python_jobs: %s\n" '${{ steps.jobs.outputs.python_jobs }}'
printf "====================\n"

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

@@ -0,0 +1,424 @@
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_default_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_default_branch }}
on_main_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_main_branch }}
on_release_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_release_branch }}
on_dev_branch:
description: ""
value: ${{ jobs.Prepare.outputs.on_dev_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 }}
has_submodules:
description: ""
value: ${{ jobs.Prepare.outputs.has_submodules }}
ref_kind:
description: ""
value: ${{ jobs.Prepare.outputs.ref_kind }}
default_branch:
description: ""
value: ${{ jobs.Prepare.outputs.default_branch }}
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 }}
git_submodule_count:
description: ""
value: ${{ jobs.Prepare.outputs.git_submodule_count }}
git_submodule_names:
description: ""
value: ${{ jobs.Prepare.outputs.git_submodule_names }}
git_submodule_paths:
description: ""
value: ${{ jobs.Prepare.outputs.git_submodule_paths }}
jobs:
Prepare:
name: Extract Information
runs-on: ubuntu-24.04
outputs:
on_default_branch: ${{ steps.Classify.outputs.on_default_branch }}
on_main_branch: ${{ steps.Classify.outputs.on_main_branch }}
on_release_branch: ${{ steps.Classify.outputs.on_release_branch }}
on_dev_branch: ${{ steps.Classify.outputs.on_dev_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 }}
has_submodules: ${{ steps.Classify.outputs.has_submodules }}
ref_kind: ${{ steps.Classify.outputs.ref_kind }}
default_branch: ${{ steps.Classify.outputs.default_branch }}
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 }}
git_submodule_count: ${{ steps.Classify.outputs.git_submodule_count }}
git_submodule_names: ${{ steps.Classify.outputs.git_submodule_names }}
git_submodule_paths: ${{ steps.Classify.outputs.git_submodule_paths }}
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'
export GH_TOKEN=${{ github.token }}
ref="${{ github.ref }}"
on_default_branch="false"
on_main_branch="false"
on_release_branch="false"
on_dev_branch="false"
is_regular_commit="false"
is_merge_commit="false"
is_release_commit="false"
is_nightly_tag="false"
is_release_tag="false"
has_submodules="false"
ref_kind="unknown"
default_branch=""
branch=""
tag=""
pr_number=""
version=""
git_submodule_count="0"
git_submodule_names=""
git_submodule_paths=""
printf "Classify Git reference '%s' " "${ref}"
if [[ "${ref:0:11}" == "refs/heads/" ]]; then
printf "${ANSI_LIGHT_GREEN}[BRANCH]\n"
ref_kind="branch"
branch="${ref:11}"
printf "Get default branch name ... "
defaultBranch=$(gh repo view "${{ github.repository }}" --json defaultBranchRef --jq '.defaultBranchRef.name' 2>&1)
if [[ $? -eq 0 ]]; then
printf "${ANSI_LIGHT_GREEN} [OK]\n"
default_branch="${defaultBranch}"
printf " default_branch=%s\n" "${default_branch}"
else
printf "${ANSI_LIGHT_RED} [FAILED]\n"
printf " %s\n" "${default_branch}"
fi
printf "Commit checks:\n"
printf " Commit kind "
if [[ -z "$(git rev-list -1 --merges ${{ github.sha }}~1..${{ github.sha }})" ]]; then
is_regular_commit="true"
printf "${ANSI_LIGHT_YELLOW}[REGULAR]${ANSI_NOCOLOR}\n"
else
is_merge_commit="true"
printf "${ANSI_LIGHT_GREEN}[MERGE]${ANSI_NOCOLOR}\n"
fi
printf "Branch checks:\n"
if [[ "${branch}" == "${defaultBranch}" ]]; then
on_default_branch="true"
printf " Commit on default branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${defaultBranch}"
fi
if [[ "${branch}" == "${{ inputs.main_branch }}" ]]; then
on_main_branch="true"
printf " Commit on main branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.main_branch }}"
fi
if [[ "${branch}" == "${{ inputs.release_branch }}" ]]; then
on_release_branch="true"
printf " Commit on release branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.release_branch }}"
fi
if [[ "${branch}" == "${{ inputs.development_branch }}" ]]; then
on_dev_branch="true"
printf " Commit on development branch ${ANSI_LIGHT_BLUE}'%s'${ANSI_NOCOLOR}\n" "${{ inputs.development_branch }}"
fi
if [[ "${is_merge_commit}" == "true" ]]; then
printf "Release checks:\n"
printf " Release kind "
if [[ "${on_main_branch}" == "true" ]]; then
is_release_commit="true"
printf "${ANSI_LIGHT_GREEN}[RELEASE]${ANSI_NOCOLOR}\n"
elif [[ "${on_version_branch}" == "true" ]]; then
is_release_commit="true"
printf "${ANSI_LIGHT_GREEN}[RELEASE]${ANSI_NOCOLOR}\n"
elif [[ "${on_release_branch}" == "true" ]]; then
is_prerelease_commit="true"
printf "${ANSI_LIGHT_YELLOW}[PRERELEASE]${ANSI_NOCOLOR}\n"
fi
fi
elif [[ "${ref:0:10}" == "refs/tags/" ]]; then
printf "${ANSI_LIGHT_GREEN}[TAG]\n"
ref_kind="tag"
tag="${ref:10}"
printf "Tag checks:\n"
printf " Check if tag is on main branch '%s' ... " "${{ inputs.main_branch }}"
git branch --remotes --contains $(git rev-parse --verify "tags/${tag}~0") | grep "origin/${{ inputs.main_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.main_branch }}"
printf "::error title=TagCheck::Tag '%s' isn't on branch '%s'.\n" "${tag}" "${{ inputs.main_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
printf "${ANSI_LIGHT_YELLOW}[PULL REQUEST]\n"
ref_kind="pullrequest"
pr_number=${ref:11}
pr_number=${pr_number%%/*}
printf "Pull Request checks:\n"
printf " Number: %s\n" "${pr_number}"
else
printf "${ANSI_LIGHT_RED}[UNKNOWN]\n"
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
# Submodules
if [[ -f .gitsubmodules ]]; then
has_submodules="true"
git_modules_file=.gitmodules # $(git rev-parse --show-toplevel)/.gitmodules
git_submodule_count="$(grep -Po '(?<=\[submodule \")(.*)(?=\"\])' "${git_modules_file}" | wc -l)"
git_submodule_names="$(grep -Po '(?<=\[submodule \")(.*)(?=\"\])' "${git_modules_file}" | paste -sd ':' -)"
git_submodule_paths="$(git config --file "${git_modules_file}" --null --name-only --get-regexp '\.path$' | xargs -0 -n1 git config --file "${git_modules_file}" --get | paste -sd ':' -)"
fi
printf "\nWriting output variables ...\n"
tee --append "${GITHUB_OUTPUT}" <<EOF
on_default_branch=${on_default_branch}
on_main_branch=${on_main_branch}
on_release_branch=${on_release_branch}
on_dev_branch=${on_dev_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}
has_submodules=${has_submodules}
ref_kind=${ref_kind}
default_branch=${default_branch}
branch=${branch}
tag=${tag}
pr_number=${pr_number}
version=${version}
git_submodule_count=${git_submodule_count}
git_submodule_names=${git_submodule_names}
git_submodule_paths=${git_submodule_paths}
EOF
# TODO: why not is_release_commit?
# TODO: how to support version branches and hotfix releases on version branches?
- name: 🔁 Find merged PullRequest from second parent of current SHA (${{ github.sha }})
id: FindPullRequest
if: steps.Classify.outputs.is_merge_commit == 'true'
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_default_branch: %s\n" "${{ steps.Classify.outputs.on_default_branch }}"
printf "on_main_branch: %s\n" "${{ steps.Classify.outputs.on_main_branch }}"
printf "on_release_branch: %s\n" "${{ steps.Classify.outputs.on_release_branch }}"
printf "on_dev_branch: %s\n" "${{ steps.Classify.outputs.on_dev_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 "has_submodules: %s\n" "${{ steps.Classify.outputs.has_submodules }}"
printf "ref_kind: %s\n" "${{ steps.Classify.outputs.ref_kind }}"
printf "default_branch: %s\n" "${{ steps.Classify.outputs.default_branch }}"
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 }}"
printf "git_submodule_*:\n"
printf " *_count_: %s\n" "${{ steps.FindPullRequest.outputs.git_submodule_count }}"
printf " *_names: %s\n" "${{ steps.FindPullRequest.outputs.git_submodule_names }}"
printf " *_paths: %s\n" "${{ steps.FindPullRequest.outputs.git_submodule_paths }}"

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -31,13 +31,38 @@ on:
type: string
coverage_artifacts_pattern:
required: false
default: '*-CodeCoverage-*'
default: '*-CodeCoverage-SQLite-*'
type: string
coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
required: false
default: 'pyproject.toml'
type: string
coverage_report_xml:
description: 'Directory containing the XML coverage report file.'
required: false
default: >-
{ "directory": "report/coverage",
"filename": "coverage.xml",
"fullpath": "report/coverage/coverage.xml"
}
type: string
coverage_report_json:
description: 'Directory containing the JSON coverage report file.'
required: false
default: >-
{ "directory": "report/coverage",
"filename": "coverage.json",
"fullpath": "report/coverage/coverage.json"
}
type: string
coverage_report_html:
description: 'HTML root directory of the generated coverage report.'
required: false
default: >-
{ "directory": "report/coverage/html"
}
type: string
coverage_sqlite_artifact:
description: 'Name of the SQLite coverage artifact.'
required: false
@@ -58,10 +83,23 @@ on:
required: false
default: ''
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:
codacy_token:
description: 'Token to push result to codacy.'
required: true
CODECOV_TOKEN:
description: 'Token to push result to Codecov.'
required: false
CODACY_TOKEN:
description: 'Token to push result to Codacy.'
required: false
jobs:
PublishCoverageResults:
@@ -71,118 +109,56 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
- name: Download Artifacts
uses: pyTooling/download-artifact@v4
- name: 📥 Download Artifacts
uses: pyTooling/download-artifact@v5
with:
pattern: ${{ inputs.coverage_artifacts_pattern }}
path: artifacts
- name: 🔎 Inspect extracted artifact (tarball)
run: |
tree -psh artifacts
tree -pash artifacts
- name: 🔧 Install coverage and tomli
run: |
python -m pip install -U --disable-pip-version-check --break-system-packages coverage[toml] tomli
- name: 🔁 Extract configurations from pyproject.toml
id: getVariables
shell: python
- name: Rename .coverage files and move them all into 'coverage/'
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
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)
run: coverage combine --data-file=.coverage coverage/
- name: Report code coverage
run: coverage report --rcfile=pyproject.toml --data-file=.coverage
run: coverage report --rcfile=${{ inputs.coverage_config }} --data-file=.coverage
- name: Convert to XML format (Cobertura)
if: inputs.coverage_xml_artifact != ''
run: coverage xml --data-file=.coverage
if: inputs.coverage_xml_artifact != '' || inputs.codecov || inputs.codacy
run: coverage xml --rcfile=${{ inputs.coverage_config }} --data-file=.coverage
- name: Convert to JSON format
if: inputs.coverage_json_artifact != ''
run: coverage json --data-file=.coverage
run: coverage json --rcfile=${{ inputs.coverage_config }} --data-file=.coverage
- name: Convert to HTML format
if: inputs.coverage_html_artifact != ''
run: |
coverage html --data-file=.coverage -d report/coverage/html
rm report/coverage/html/.gitignore
tree -a report/coverage/html
coverage html --rcfile=${{ inputs.coverage_config }} --data-file=.coverage
rm ${{ fromJson(inputs.coverage_report_html).directory }}/.gitignore
tree -pash ${{ fromJson(inputs.coverage_report_html).directory }}
- name: 📤 Upload 'Coverage SQLite Database' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_sqlite_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_sqlite_artifact }}
path: .coverage
@@ -190,49 +166,69 @@ jobs:
retention-days: 1
- name: 📤 Upload 'Coverage XML Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_xml_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_xml_artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_xml }}
working-directory: ${{ fromJson(inputs.coverage_report_xml).directory }}
path: ${{ fromJson(inputs.coverage_report_xml).filename }}
if-no-files-found: error
retention-days: 1
- name: 📤 Upload 'Coverage JSON Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_json_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_json_artifact }}
path: ${{ steps.getVariables.outputs.coverage_report_json }}
working-directory: ${{ fromJson(inputs.coverage_report_json).directory }}
path: ${{ fromJson(inputs.coverage_report_json).filename }}
if-no-files-found: error
retention-days: 1
- name: 📤 Upload 'Coverage HTML Report' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.coverage_html_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_html_artifact }}
working-directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
working-directory: ${{ fromJson(inputs.coverage_report_html).directory }}
path: '*'
if-no-files-found: error
retention-days: 1
- name: 📊 Publish code coverage at CodeCov
if: inputs.CodeCov == true
continue-on-error: true
uses: codecov/codecov-action@v5
id: codecov
if: inputs.codecov == 'true'
continue-on-error: true
with:
files: ${{ steps.getVariables.outputs.coverage_report_xml }}
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
files: ${{ fromJson(inputs.coverage_report_xml).fullpath }}
flags: unittests
env_vars: PYTHON
fail_ci_if_error: true
- name: 📉 Publish code coverage at Codacy
if: inputs.Codacy == true
continue-on-error: true
uses: codacy/codacy-coverage-reporter-action@v1
id: codacy
if: inputs.codacy == 'true'
continue-on-error: true
with:
project-token: ${{ secrets.codacy_token }}
coverage-reports: ${{ steps.getVariables.outputs.coverage_report_xml }}
project-token: ${{ secrets.CODACY_TOKEN }}
coverage-reports: ${{ fromJson(inputs.coverage_report_xml).fullpath }}
- 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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -33,7 +33,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
requirements:
description: 'Python dependencies to be installed through pip.'
@@ -50,38 +50,37 @@ on:
required: false
jobs:
PublishOnPyPI:
name: 🚀 Publish to PyPI
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps:
- name: 📥 Download artifacts '${{ inputs.artifact }}' from 'Package' job
uses: pyTooling/download-artifact@v4
uses: pyTooling/download-artifact@v5
with:
name: ${{ inputs.artifact }}
path: dist
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: ⚙ Install dependencies for packaging and release
run: python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: Release Python source package to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload dist/*.tar.gz
- name: ⤴ Release Python wheel package to PyPI
- name: Publish Python wheel package to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload dist/*.whl
- name: ⤴ Publish Python source package to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload dist/*.tar.gz
- name: 🗑️ Delete packaging Artifacts
uses: geekyeggo/delete-artifact@v5
with:

View File

@@ -0,0 +1,856 @@
# ==================================================================================================================== #
# 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: true
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: true
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
printf "%s\n" "${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 }}}"
#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
printf "%s\n" "${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
STRUCT_VERSION="1.1"
# Use GitHub API to ask for latest version
printf "Get latest released version via GitHub API ...\n"
printf " gh release list --json tagName,isLatest --jq '.[] | select(.isLatest == true) | .tagName' "
latestVersion=$(gh release list --json tagName,isLatest --jq '.[] | select(.isLatest == true) | .tagName')
if [[ $? -eq 0 ]]; then
if [[ -z "${latestVersion}" ]]; then
printf "${ANSI_LIGHT_RED}[UNKNOWN]${ANSI_NOCOLOR}\n"
printf " latest=unknown\n"
latestVersion="unknown"
else
printf "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}\n"
printf " latest=%s\n" "${latestVersion}"
fi
else
printf "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}\n"
printf " ${ANSI_LIGHT_RED}Couldn't get latest released version '%s'.${ANSI_NOCOLOR}\n" "${latestVersion}"
printf "::error title=%s::%s\n" "GitHub Release API" "Couldn't get latest released version '${latestVersion}'."
latestVersion="error"
fi
# 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 structVersion "${STRUCT_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 jsonLatest "$(jq -c -n \
--arg version "${latestVersion}" \
--arg release "${{ github.server_url }}/${{ github.repository }}/releases/download/${latestVersion}" \
'{"version": $version, "release-url": $release}' \
)" \
--argjson categories "$(jq -c -n \
'$ARGS.positional' \
--args "${inventoryCategories[@]}" \
)" \
'{"tag": $tag, "version": $version, "git-hash": $hash, "repository-url": $repo, "release-url": $release, "categories": $categories, "latest": $jsonLatest}' \
)" \
'{"version": "$structVersion", "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
printf " downloading '${artifact}' ...\n"
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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -32,28 +32,67 @@ on:
type: string
unittest_artifacts_pattern:
required: false
default: '*-UnitTestReportSummary-*'
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:
description: 'Name of the merged JUnit Test Summary artifact.'
required: false
default: ''
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:
description: 'Additional merging arguments.'
required: false
default: '"--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit"'
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: boolean
default: 'true'
type: string
report_title:
description: 'Title of the summary report in the pipeline''s sidebar'
required: false
default: 'Unit Test Results'
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:
PublishTestResults:
@@ -63,48 +102,86 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download Artifacts
uses: pyTooling/download-artifact@v4
- name: 📥 Download Artifacts
uses: pyTooling/download-artifact@v5
with:
pattern: ${{ inputs.unittest_artifacts_pattern }}
path: artifacts
path: artifacts
- name: 🔎 Inspect extracted artifact (tarball)
run: |
tree -psh artifacts
tree -pash artifacts
- name: 🔧 Install pyEDAA.Reports (JUunit Parser and Merger)
run: |
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: |
mkdir -p junit
ls -lAh artifacts/*/*.xml
find artifacts/ -type f -path "*TestReportSummary*.xml" -exec sh -c 'cp -v $0 "junit/$(basename $(dirname $0)).$(basename $0)"' {} ';'
tree -a junit
find artifacts/ -type f -path "*.xml" -exec sh -c 'cp -v $0 "junit/$(basename $(dirname $0)).$(basename $0)"' {} ';'
tree -pash junit
- name: 🔁 Merge JUnit Unit Test Summaries
run: |
pyedaa-reports -v unittest "--merge=pyTest-JUnit:junit/*.xml" ${{ inputs.additional_merge_args }} "--output=pyTest-JUnit:Unittesting.xml"
echo "cat Unittesting.xml"
cat Unittesting.xml
if [[ -n "${{ inputs.testsuite-summary-name }}" ]]; then
name="\"--name=${{ inputs.testsuite-summary-name }}\""
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
uses: dorny/test-reporter@v1
if: inputs.publish && inputs.report_title != ''
uses: dorny/test-reporter@v2
id: test-reporter
if: ( inputs.dorny == 'true' || inputs.publish == 'true' ) && inputs.report_title != ''
continue-on-error: true
with:
name: ${{ inputs.report_title }}
path: Unittesting.xml
path: ${{ inputs.merged_junit_filename }}
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
if: inputs.merged_junit_artifact != ''
uses: pyTooling/upload-artifact@v4
if: inputs.merged_junit_artifact != ''
with:
name: ${{ inputs.merged_junit_artifact }}
path: Unittesting.xml
path: ${{ inputs.merged_junit_filename }}
if-no-files-found: error
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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -53,24 +53,24 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 📥 Download artifacts '${{ inputs.doc }}' from 'BuildTheDocs' job
uses: pyTooling/download-artifact@v4
- name: 📥 Download artifacts '${{ inputs.doc }}' from 'SphinxDocumentation' job
uses: pyTooling/download-artifact@v5
with:
name: ${{ inputs.doc }}
path: public
- name: 📥 Download artifacts '${{ inputs.coverage }}' from 'Coverage' job
uses: pyTooling/download-artifact@v5
if: ${{ inputs.coverage != '' }}
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.coverage }}
path: public/coverage
- name: 📥 Download artifacts '${{ inputs.typing }}' from 'StaticTypeCheck' job
uses: pyTooling/download-artifact@v5
if: ${{ inputs.typing != '' }}
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.typing }}
path: public/typing

View File

@@ -3,7 +3,7 @@
# Patrick Lehmann #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -32,7 +32,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
requirements:
description: 'Python dependencies to be installed through pip.'
@@ -44,24 +44,29 @@ on:
required: false
default: 'doc'
type: string
coverage_report_json_directory:
description: ''
required: true
type: string
coverage_json_artifact:
description: 'Name of the coverage JSON artifact.'
required: false
default: ''
type: string
coverage_report_json:
description: 'Directory where coverage JSON artifact will be extracted.'
required: false
default: >-
{ "directory": "report/coverage"
}
type: string
unittest_xml_artifact:
description: 'Name of the unittest XML artifact.'
required: false
default: ''
type: string
unittest_xml_directory:
description: 'Directory where unittest XML artifact is extracted.'
unittest_xml:
description: 'Directory where unittest XML artifact will be extracted.'
required: false
default: 'report/unit'
default: >-
{ "directory": "report/unit"
}
type: string
html_artifact:
description: 'Name of the HTML documentation artifact.'
@@ -81,7 +86,7 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
@@ -90,7 +95,7 @@ jobs:
run: sudo apt-get install -y --no-install-recommends graphviz
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -100,18 +105,20 @@ jobs:
python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job
uses: pyTooling/download-artifact@v5
if: inputs.unittest_xml_artifact != ''
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.unittest_xml_artifact }}
path: ${{ inputs.unittest_xml_directory }}
path: ${{ fromJson(inputs.unittest_xml).directory }}
investigate: true
- name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job
uses: pyTooling/download-artifact@v5
if: inputs.coverage_json_artifact != ''
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.coverage_json_artifact }}
path: ${{ inputs.coverage_report_json_directory }}
path: ${{ fromJson(inputs.coverage_report_json).directory }}
investigate: true
- name: ☑ Generate HTML documentation
if: inputs.html_artifact != ''
@@ -122,9 +129,9 @@ jobs:
sphinx-build -v -n -b html -d _build/doctrees -j $(nproc) -w _build/html.log . _build/html
- name: 📤 Upload 'HTML Documentation' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.html_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.html_artifact }}
working-directory: ${{ inputs.doc_directory }}/_build/html
@@ -138,7 +145,7 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
@@ -147,7 +154,7 @@ jobs:
run: sudo apt-get install -y --no-install-recommends graphviz
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -157,18 +164,20 @@ jobs:
python -m pip install --disable-pip-version-check ${{ inputs.requirements }}
- name: 📥 Download artifacts '${{ inputs.unittest_xml_artifact }}' from 'Unittesting' job
uses: pyTooling/download-artifact@v5
if: inputs.unittest_xml_artifact != ''
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.unittest_xml_artifact }}
path: ${{ inputs.unittest_xml_directory }}
path: ${{ fromJson(inputs.unittest_xml).directory }}
investigate: true
- name: 📥 Download artifacts '${{ inputs.coverage_json_artifact }}' from 'PublishCoverageResults' job
uses: pyTooling/download-artifact@v5
if: inputs.coverage_json_artifact != ''
uses: pyTooling/download-artifact@v4
with:
name: ${{ inputs.coverage_json_artifact }}
path: ${{ inputs.coverage_report_json_directory }}
path: ${{ fromJson(inputs.coverage_report_json).directory }}
investigate: true
- name: ☑ Generate LaTeX documentation
if: inputs.latex_artifact != ''
@@ -180,10 +189,92 @@ jobs:
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
- 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
uses: pyTooling/upload-artifact@v4
if: inputs.latex_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.latex_artifact }}
working-directory: ${{ inputs.doc_directory }}/_build/latex

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -33,49 +33,71 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
requirements:
description: 'Python dependencies to be installed through pip.'
required: false
default: '-r tests/requirements.txt'
type: string
commands:
description: 'Commands to run the static type checks.'
required: true
mypy_options:
description: 'Additional mypy options.'
required: false
default: ''
type: string
cobertura_report:
description: 'Cobertura file to upload as an artifact.'
required: false
default: >-
{ "fullpath": "report/typing/cobertura.xml",
"directory": "report/typing",
"filename": "cobertura.xml"
}
type: string
junit_report:
description: 'JUnit file to upload as an artifact.'
required: false
default: >-
{ "fullpath": "report/typing/StaticTypingSummary.xml",
"directory": "report/typing",
"filename": "StaticTypingSummary.xml"
}
type: string
html_report:
description: 'Directory to upload as an artifact.'
required: false
default: 'htmlmypy'
default: >-
{ "directory": "report/typing/html"
}
# "fullpath": "report/typing/html",
type: string
junit_report:
description: 'junit file to upload as an artifact.'
cobertura_artifact:
description: 'Name of the typing cobertura artifact (Cobertura XML).'
required: false
default: 'StaticTypingSummary.xml'
default: ''
type: string
junit_artifact:
description: 'Name of the typing junit artifact (JUnit XML).'
required: false
default: ''
type: string
html_artifact:
description: 'Name of the typing artifact (HTML report).'
required: true
type: string
junit_artifact:
description: 'Name of the typing junit artifact (junit XML).'
required: false
default: ''
type: string
jobs:
StaticTypeCheck:
name: 👀 Check Static Typing using Python ${{ inputs.python_version }}
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 🐍 Setup Python ${{ inputs.python_version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
@@ -84,25 +106,70 @@ jobs:
- name: Check Static Typing
continue-on-error: true
run: ${{ inputs.commands }}
run: mypy ${{ inputs.mypy_options }}
- name: 📤 Upload 'Static Typing Report' HTML artifact
- name: Debug output directories
continue-on-error: true
run: |
# List directory contents
set +e
ANSI_LIGHT_RED=$'\x1b[91m'
ANSI_LIGHT_GREEN=$'\x1b[92m'
ANSI_LIGHT_YELLOW=$'\x1b[93m'
ANSI_LIGHT_BLUE=$'\x1b[94m'
ANSI_NOCOLOR=$'\x1b[0m'
if [[ "${{ fromJson(inputs.html_report).directory }}" != "" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.html_report).directory }}' ..."
tree ${{ fromJson(inputs.html_report).directory }}
printf "::endgroup::\n"
fi
if [[ "${{ fromJson(inputs.junit_report).directory }}" != "" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.junit_report).directory }}' ..."
tree ${{ fromJson(inputs.junit_report).directory }}
printf "::endgroup::\n"
if [[ "${{ fromJson(inputs.cobertura_report).directory }}" != "" && "${{ fromJson(inputs.junit_report).directory }}" != "${{ fromJson(inputs.cobertura_report).directory }}" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.cobertura_report).directory }}' ..."
tree ${{ fromJson(inputs.cobertura_report).directory }}
printf "::endgroup::\n"
fi
elif [[ "${{ fromJson(inputs.cobertura_report).directory }}" != "" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "Content of '${{ fromJson(inputs.cobertura_report).directory }}' ..."
tree ${{ fromJson(inputs.cobertura_report).directory }}
printf "::endgroup::\n"
fi
- name: 📤 Upload '${{ inputs.html_artifact }}' HTML artifact
uses: pyTooling/upload-artifact@v4
if: ${{ inputs.html_artifact != '' }}
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.html_artifact }}
working-directory: ${{ inputs.html_report }}
working-directory: ${{ fromJson(inputs.html_report).directory }}
path: '*'
if-no-files-found: error
retention-days: 1
- name: 📤 Upload 'Static Typing Report' JUnit artifact
- name: 📤 Upload '${{ inputs.junit_artifact }}' JUnit artifact
uses: pyTooling/upload-artifact@v4
if: ${{ inputs.junit_artifact != '' }}
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.junit_artifact }}
path: ${{ inputs.junit_report }}
working-directory: ${{ fromJson(inputs.junit_report).directory }}
path: ${{ fromJson(inputs.junit_report).filename }}
if-no-files-found: error
retention-days: 1
- name: 📤 Upload '${{ inputs.cobertura_artifact }}' Cobertura artifact
uses: pyTooling/upload-artifact@v4
if: ${{ inputs.cobertura_artifact != '' }}
continue-on-error: true
with:
name: ${{ inputs.cobertura_artifact }}
working-directory: ${{ fromJson(inputs.cobertura_report).directory }}
path: ${{ fromJson(inputs.cobertura_report).filename }}
if-no-files-found: error
retention-days: 1

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -20,74 +20,65 @@
# #
# SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== #
name: Release
name: Auto Tag
on:
workflow_call:
inputs:
ubuntu_image_version:
description: 'Ubuntu image version.'
ubuntu_image:
description: 'Name of the Ubuntu image.'
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
jobs:
Release:
name: 📝 Create 'Release Page' on GitHub
runs-on: "ubuntu-${{ inputs.ubuntu_image_version }}"
AutoTag:
name: "🏷 Create tag '${{ inputs.version}}' on GitHub"
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:
- name: 🔁 Extract Git tag from GITHUB_REF
id: getVariables
run: |
GIT_TAG=${GITHUB_REF#refs/*/}
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 }}
- name: 🏷 Create release tag '${{ steps.FindPullRequest.outputs.version }}'
uses: actions/github-script@v8
id: createReleaseTag
# if: inputs.auto_tag == 'true'
with:
tag_name: ${{ steps.getVariables.outputs.gitTag }}
# release_name: ${{ steps.getVariables.outputs.gitTag }}
body: |
**Automated Release created on: ${{ steps.getVariables.outputs.datetime }}**
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/${{ inputs.version }}',
sha: context.sha
})
# New Features
* tbd
* tbd
# Changes
* tbd
* tbd
# Bug Fixes
* tbd
* tbd
# Documentation
* tbd
* tbd
# Unit Tests
* tbd
* tbd
----------
# Related Issues and Pull-Requests
* tbd
* tbd
draft: true
prerelease: false
- name: Trigger Workflow
uses: actions/github-script@v8
id: runReleaseTag
# if: inputs.auto_tag == 'true'
with:
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: '${{ inputs.workflow }}',
ref: '${{ inputs.version }}'
})

View File

@@ -1,176 +0,0 @@
# ==================================================================================================================== #
# Authors: #
# 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: Test Releaser
on:
push:
tags:
- '*'
- '!tip'
- '!v*'
branches:
- '**'
- '!r*'
workflow_dispatch:
schedule:
- cron: '0 0 * * 4'
env:
CI: true
jobs:
Image:
runs-on: ubuntu-24.04
env:
DOCKER_BUILDKIT: 1
steps:
- uses: actions/checkout@v4
- name: Build container image
run: docker build -t ghcr.io/pytooling/releaser -f releaser/Dockerfile releaser
- name: Push container image
uses: ./with-post-step
with:
main: |
echo '${{ github.token }}' | docker login ghcr.io -u GitHub-Actions --password-stdin
docker push ghcr.io/pytooling/releaser
post: docker logout ghcr.io
Composite:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
- name: Single
uses: ./releaser/composite
with:
rm: true
token: ${{ secrets.GITHUB_TOKEN }}
files: artifact-*.txt
- name: List
uses: ./releaser/composite
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: |
artifact-*.txt
README.md
- name: Add artifacts/*.txt
run: |
mkdir artifacts
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt
touch artifacts/empty_file.txt
- name: Single in subdir
uses: ./releaser/composite
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/artifact.txt
- name: Add artifacts/*.md
run: |
echo "releaser hello" > artifacts/hello.md
echo "releaser world" > artifacts/world.md
- name: Directory wildcard
uses: ./releaser/composite
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/*
- name: Add artifacts/subdir
run: |
mkdir artifacts/subdir
echo "Test recursive glob" > artifacts/subdir/deep_file.txt
- name: Directory wildcard (recursive)
uses: ./releaser/composite
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/**
Test:
needs:
- Image
- Composite
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: echo "Build some tool and generate some (versioned) artifacts" > artifact-$(date -u +"%Y-%m-%dT%H-%M-%SZ").txt
- name: Single
uses: ./releaser
with:
rm: true
token: ${{ secrets.GITHUB_TOKEN }}
files: artifact-*.txt
- name: List
uses: ./releaser
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: |
artifact-*.txt
README.md
- name: Add artifacts/*.txt
run: |
mkdir artifacts
echo "Build some tool and generate some artifacts" > artifacts/artifact.txt
touch artifacts/empty_file.txt
- name: Single in subdir
uses: ./releaser
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/artifact.txt
- name: Add artifacts/*.md
run: |
echo "releaser hello" > artifacts/hello.md
echo "releaser world" > artifacts/world.md
- name: Directory wildcard
uses: ./releaser
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/*
- name: Add artifacts/subdir
run: |
mkdir artifacts/subdir
echo "Test recursive glob" > artifacts/subdir/deep_file.txt
- name: Directory wildcard (recursive)
uses: ./releaser
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/**

View File

@@ -4,7 +4,7 @@
# 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"); #
# you may not use this file except in compliance with the License. #
@@ -85,20 +85,55 @@ on:
default: ''
type: string
tests_directory:
description: 'Path to the directory containing tests (relative to root_directory).'
description: 'Path to the directory containing tests (relative from root_directory).'
required: false
default: 'tests'
type: string
unittest_directory:
description: 'Path to the directory containing unit tests (relative to tests_directory).'
description: 'Path to the directory containing unit tests (relative from tests_directory).'
required: false
default: 'unit'
type: string
unittest_report_xml:
description: 'JSON object describing the path where to save the unittest summary report XML.'
required: false
default: >-
{ "directory": "report/unit",
"filename": "TestReportSummary.xml",
"fullpath": "report/unit/TestReportSummary.xml"
}
type: string
coverage_config:
description: 'Path to the .coveragerc file. Use pyproject.toml by default.'
required: false
default: 'pyproject.toml'
type: string
coverage_report_xml:
description: 'JSON object describing the path where the coverage report in XML format will be generated.'
required: false
default: >-
{ "directory": "report/coverage",
"filename": "coverage.xml",
"fullpath": "report/coverage/coverage.xml"
}
type: string
coverage_report_json:
description: 'JSON object describing the path where the coverage report in JSON format will be generated.'
required: false
default: >-
{ "directory": "report/coverage",
"filename": "coverage.json",
"fullpath": "report/coverage/coverage.json"
}
type: string
coverage_report_html:
description: 'JSON object describing the path where the coverage report in HTML format will be generated.'
required: false
default: >-
{ "directory": "report/coverage/html",
}
# "fullpath": "report/coverage/html"
type: string
unittest_xml_artifact:
description: "Generate unit test report with junitxml and upload results as an artifact."
required: false
@@ -146,7 +181,7 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
submodules: true
@@ -157,7 +192,7 @@ jobs:
run: brew install ${{ inputs.brew }}
- name: 🔧 Install apt dependencies on Ubuntu
if: matrix.system == 'ubuntu' && inputs.apt != ''
if: ( matrix.system == 'ubuntu' || matrix.system == 'ubuntu-arm' ) && inputs.apt != ''
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends ${{ inputs.apt }}
@@ -210,14 +245,14 @@ jobs:
packages = {
"coverage": "python-coverage:p",
"docstr_coverage": "python-pyaml:p",
"docstr_coverage": "python-pyaml:p python-types-pyyaml:p",
"igraph": "igraph:p",
"jinja2": "python-markupsafe:p",
"lxml": "python-lxml:p",
"numpy": "python-numpy:p",
"markupsafe": "python-markupsafe: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 python-ruamel.yaml.clib:p",
"sphinx": "python-markupsafe:p",
@@ -225,6 +260,7 @@ jobs:
"wheel": "python-wheel: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",
"sphinx-reports": "python-markupsafe:p python-pyaml:p python-types-pyyaml:p",
}
subPackages = {
"pytooling": {
@@ -267,8 +303,8 @@ jobs:
# Python setup
- name: '🟦 Setup MSYS2 for ${{ matrix.runtime }}'
if: matrix.system == 'msys2'
uses: msys2/setup-msys2@v2
if: matrix.system == 'msys2'
with:
msystem: ${{ matrix.runtime }}
update: true
@@ -277,8 +313,8 @@ jobs:
${{ inputs.pacboy }}
- name: 🐍 Setup Python ${{ matrix.python }}
uses: actions/setup-python@v6
if: matrix.system != 'msys2'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
@@ -310,10 +346,10 @@ jobs:
run: ${{ inputs.macos_arm_before_script }}
- name: 🐧 Ubuntu before scripts
if: matrix.system == 'ubuntu' && inputs.ubuntu_before_script != ''
if: ( matrix.system == 'ubuntu' || matrix.system == 'ubuntu-arm' ) && inputs.ubuntu_before_script != ''
run: ${{ inputs.ubuntu_before_script }}
# Windows before script
# TODO: Windows before script
- name: 🪟🟦 MinGW64 before scripts
if: matrix.system == 'msys2' && matrix.runtime == 'MINGW64' && inputs.mingw64_before_script != ''
@@ -323,95 +359,37 @@ jobs:
if: matrix.system == 'msys2' && matrix.runtime == 'UCRT64' && 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
# TODO: allow configuration of pytest_args
- name: ✅ Run unit tests (Ubuntu/macOS)
if: matrix.system != 'windows'
id: pytest_bash
if: ( matrix.system != 'windows' && matrix.system != 'windows-arm' )
continue-on-error: true
run: |
export ENVIRONMENT_NAME="${{ matrix.envname }}"
export PYTHONPATH=$(pwd)
cd "${{ inputs.root_directory || '.' }}"
[ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS
[ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=${{ fromJson(inputs.unittest_report_xml).fullpath }}' || unset PYTEST_ARGS
if [ -n '${{ inputs.coverage_config }}' ]; then
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 }}
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 }}
fi
- name: ✅ Run unit tests (Windows)
if: matrix.system == 'windows'
id: pytest_posh
if: ( matrix.system == 'windows' || matrix.system == 'windows-arm' )
continue-on-error: true
run: |
$env:ENVIRONMENT_NAME = "${{ matrix.envname }}"
$env:PYTHONPATH = (Get-Location).ToString()
cd "${{ inputs.root_directory || '.' }}"
$PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=report/unit/TestReportSummary.xml" } else { "" }
$PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=${{ fromJson(inputs.unittest_report_xml).fullpath }}" } else { "" }
if ("${{ inputs.coverage_config }}") {
Write-Host "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}"
coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}
@@ -421,32 +399,35 @@ jobs:
}
- name: Convert coverage to XML format (Cobertura)
id: convert_xml
if: inputs.coverage_xml_artifact != ''
continue-on-error: true
run: coverage xml --data-file=.coverage
- name: Convert coverage to JSON format
id: convert_json
if: inputs.coverage_json_artifact != ''
continue-on-error: true
run: coverage json --data-file=.coverage
- name: Convert coverage to HTML format
id: convert_html
if: inputs.coverage_html_artifact != ''
continue-on-error: true
run: |
coverage html --data-file=.coverage -d ${{ steps.getVariables.outputs.coverage_report_html_directory }}
rm ${{ steps.getVariables.outputs.coverage_report_html_directory }}/.gitignore
coverage html --data-file=.coverage -d ${{ fromJson(inputs.coverage_report_html).directory }}
rm ${{ fromJson(inputs.coverage_report_html).directory }}/.gitignore
# Upload artifacts
- name: 📤 Upload 'TestReportSummary.xml' artifact
- name: 📤 Upload '${{ fromJson(inputs.unittest_report_xml).filename }}' artifact
uses: pyTooling/upload-artifact@v4
if: inputs.unittest_xml_artifact != ''
continue-on-error: true
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.unittest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
working-directory: report/unit
path: TestReportSummary.xml
working-directory: ${{ fromJson(inputs.unittest_report_xml).directory }}
path: ${{ fromJson(inputs.unittest_report_xml).filename }}
if-no-files-found: error
retention-days: 1
@@ -456,7 +437,7 @@ jobs:
# uses: pyTooling/upload-artifact@v4
# with:
# name: ${{ inputs.unittest_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
# path: ${{ steps.getVariables.outputs.unittest_report_html_directory }}
# path: ${{ inputs.unittest_report_html_directory }}
# if-no-files-found: error
# retention-days: 1
@@ -472,32 +453,48 @@ jobs:
retention-days: 1
- 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
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: ${{ steps.getVariables.outputs.coverage_report_xml }}
working-directory: ${{ fromJson(inputs.coverage_report_xml).directory }}
path: ${{ fromJson(inputs.coverage_report_xml).filename }}
if-no-files-found: error
retention-days: 1
- 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
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_json_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
path: ${{ steps.getVariables.outputs.coverage_report_json }}
working-directory: ${{ fromJson(inputs.coverage_report_json).directory }}
path: ${{ fromJson(inputs.coverage_report_json).filename }}
if-no-files-found: error
retention-days: 1
- 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
uses: pyTooling/upload-artifact@v4
with:
name: ${{ inputs.coverage_html_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }}
working-directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }}
working-directory: ${{ fromJson(inputs.coverage_report_html).directory }}
path: '*'
if-no-files-found: error
retention-days: 1
- 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 #
# #
# ==================================================================================================================== #
# Copyright 2020-2024 The pyTooling Authors #
# 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. #
@@ -33,7 +33,7 @@ on:
python_version:
description: 'Python version.'
required: false
default: '3.12'
default: '3.13'
type: string
jobs:
@@ -44,10 +44,10 @@ jobs:
steps:
- name: ⏬ Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 🐍 Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}

View File

@@ -22,7 +22,7 @@ jobs:
include: ${{ fromJson(needs.Params.outputs.python_jobs) }}
steps:
- 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 }}
uses: pyTooling/upload-artifact@v4
@@ -39,7 +39,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Package creation
run: echo "Package" >> package.txt
run: printf "%s\n" "Package" >> package.txt
- name: 📤 Upload artifact for ${{ matrix.system }}-${{ matrix.python }}
uses: pyTooling/upload-artifact@v4

View File

@@ -0,0 +1,38 @@
name: Testing available GitHub Action Images
on:
push:
workflow_dispatch:
jobs:
RunnerTest:
name: ${{ matrix.icon }} ${{ matrix.name }}
runs-on: ${{ matrix.image }}
continue-on-error: ${{ matrix.can-fail }}
strategy:
fail-fast: false
matrix:
include:
- {icon: '🐧', name: 'Ubuntu 22.04 (x86-64)', image: 'ubuntu-22.04', shell: 'bash', can-fail: false}
- {icon: '🐧', name: 'Ubuntu 24.04 (x86-64)', image: 'ubuntu-24.04', shell: 'bash', can-fail: false} # latest
- {icon: '🍎', name: 'macOS-13 (x86-64)', image: 'macos-13', shell: 'bash', can-fail: false}
- {icon: '🍎', name: 'macOS-14 (x86-64)', image: 'macos-14-large', shell: 'bash', can-fail: true } # not in free plan
- {icon: '🍎', name: 'macOS-15 (x86-64)', image: 'macos-15-large', shell: 'bash', can-fail: true } # not in free plan
- {icon: '🍏', name: 'macOS-13 (aarch64)', image: 'macos-13-xlarge', shell: 'bash', can-fail: true } # not in free plan
- {icon: '🍏', name: 'macOS-14 (aarch64)', image: 'macos-14', shell: 'bash', can-fail: false} # latest
- {icon: '🍏', name: 'macOS-15 (aarch64)', image: 'macos-15', shell: 'bash', can-fail: false}
- {icon: '🪟', name: 'Windows Server 2022', image: 'windows-2022', shell: 'bash', can-fail: false}
- {icon: '🪟', name: 'Windows Server 2025', image: 'windows-2025', shell: 'bash', can-fail: false} # latest
# Third party images by ARM for aarch64
- {icon: '⛄', name: 'Ubuntu 22.04 (aarch64)', image: 'ubuntu-22.04-arm', shell: 'bash', can-fail: false}
- {icon: '⛄', name: 'Ubuntu 24.04 (aarch64)', image: 'ubuntu-24.04-arm', shell: 'bash', can-fail: false}
- {icon: '🏢', name: 'Windows 11 (arch64)', image: 'windows-11-arm', shell: 'bash', can-fail: false}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: 'uname -a'
run: uname -a

View File

@@ -5,62 +5,71 @@ on:
workflow_dispatch:
jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@main
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@main
with:
package_name: pyDummy
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: pyDummy
python_version_list: "3.9 3.10 3.11 3.12 3.13 pypy-3.9 pypy-3.10"
# disable_list: "windows:pypy-3.10"
package_name: 'myPackage'
python_version_list: '3.9 3.10 3.11 3.12 3.13 3.14 pypy-3.10 pypy-3.11'
disable_list: 'windows-arm:pypy-3.10 windows-arm:pypy-3.11'
PlatformTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: Platform
python_version_list: ""
system_list: "ubuntu windows macos mingw32 mingw64 clang64 ucrt64"
package_name: 'myPackage'
name: 'Platform'
python_version_list: ''
system_list: 'ubuntu ubuntu-arm windows windows-arm macos mingw64 clang64 ucrt64'
InstallParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
package_name: 'myPackage'
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
python_version_list: ''
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main
needs:
- ConfigParams
- UnitTestingParams
with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }}
# coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
# coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
# coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
# coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
PlatformTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@main
needs:
- ConfigParams
- PlatformTestingParams
with:
jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }}
jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }}
# tests_directory: ""
unittest_directory: platform
unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}
unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}
unittest_directory: platform
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }}
unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }}
coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }}
coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}
# Coverage:
# uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@main
# 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 }}
coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@main
@@ -69,55 +78,82 @@ jobs:
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
commands: |
${{ needs.ConfigParams.outputs.mypy_prepare_command }}
mypy --html-report htmlmypy -p ${{ needs.ConfigParams.outputs.package_fullname }}
html_report: 'htmlmypy'
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
html_report: ${{ needs.ConfigParams.outputs.typing_report_html }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
CodeQuality:
uses: pyTooling/Actions/.github/workflows/CheckCodeQuality.yml@main
needs:
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
package_directory: ${{ needs.UnitTestingParams.outputs.package_directory }}
artifact: CodeQuality
DocCoverage:
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r1
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@main
needs:
- ConfigParams
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
directory: ${{ needs.ConfigParams.outputs.package_directors }}
directory : ${{ needs.ConfigParams.outputs.package_directory }}
# fail_below: 70
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@main
needs:
- UnitTestingParams
- UnitTesting
# - Coverage
- PlatformTesting
# - UnitTesting
# - PlatformTesting
with:
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@main
needs:
- UnitTestingParams
- InstallParams
- Package
with:
jobs: ${{ needs.InstallParams.outputs.python_jobs }}
wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }}
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@main
needs:
- ConfigParams
- UnitTestingParams
- UnitTesting
- PlatformTesting
# - Coverage
with:
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
codecov: true
codacy: true
secrets: inherit
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@main
needs:
- ConfigParams
- UnitTestingParams
- UnitTesting
- PlatformTesting
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.UnitTestingParams.outputs.package_fullname }}
merged_junit_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
codecov: true
dorny: true
secrets: inherit
# VerifyDocs:
# uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@main
@@ -135,23 +171,23 @@ jobs:
- PublishCoverageResults
# - VerifyDocs
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
coverage_report_json_directory: ${{ needs.ConfigParams.outputs.coverage_report_json_directory }}
# unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
# coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
unittest_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
IntermediateCleanUp:
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r1
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@main
needs:
- UnitTestingParams
- PublishCoverageResults
- PublishTestResults
- Documentation
with:
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:
uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@main
@@ -159,48 +195,71 @@ jobs:
- UnitTestingParams
- Documentation
with:
document: actions
document: 'Actions'
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@main
needs:
- UnitTestingParams
- Documentation
# - PDFDocumentation
# - Coverage
- PDFDocumentation
- PublishCoverageResults
- StaticTypeCheck
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 }}
typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
typing: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_html }}
ReleasePage:
uses: pyTooling/Actions/.github/workflows/Release.yml@main
if: startsWith(github.ref, 'refs/tags')
TriggerTaggedRelease:
uses: pyTooling/Actions/.github/workflows/TagReleaseCommit.yml@main
needs:
- Prepare
- UnitTesting
- PlatformTesting
# - Coverage
- Install
# - StaticTypeCheck
- Package
- 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@main
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:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@main
if: startsWith(github.ref, 'refs/tags')
needs:
- UnitTestingParams
- ReleasePage
# - Package
if: needs.Prepare.outputs.is_release_tag == 'true'
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
secrets: inherit
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@main
@@ -208,11 +267,10 @@ jobs:
- UnitTestingParams
- PlatformTestingParams
- UnitTesting
# - Coverage
- StaticTypeCheck
- PlatformTesting
- Documentation
# - PDFDocumentation
- PDFDocumentation
- PublishTestResults
- PublishCoverageResults
- PublishToGitHubPages
@@ -222,7 +280,6 @@ jobs:
remaining: |
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-*
${{ 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_json }}-*
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}-*
@@ -234,9 +291,9 @@ jobs:
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).statictyping_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_html }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }}-*
${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }}-*

View File

@@ -8,8 +8,12 @@ jobs:
NamespacePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@main
with:
package_namespace: pyExamples
package_name: Extensions
package_namespace: myFramework
package_name: Extension
codecov: true
codacy: true
dorny: true
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}

View File

@@ -12,9 +12,9 @@ jobs:
steps:
- name: 🖉 Build 1
run: |
echo "Document 1 $(date --utc '+%d.%m.%Y - %H:%M:%S')" > document1.txt
echo "Analysis log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > analysis.log
echo "Build log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > build.log
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
@@ -28,8 +28,8 @@ jobs:
- name: 🖉 Program
run: |
echo "Document other $(date --utc '+%d.%m.%Y - %H:%M:%S')" > document1.txt
echo "Program $(date --utc '+%d.%m.%Y - %H:%M:%S')" > program.py
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
@@ -42,49 +42,27 @@ jobs:
retention-days: 1
NightlyPage:
uses: pyTooling/Actions/.github/workflows/NightlyRelease.yml@main
uses: ./.github/workflows/PublishReleaseNotes.yml
needs:
- Build
secrets: inherit
permissions:
contents: write
actions: write
# attestations: write
actions: write
with:
can-fail: true
prerelease: true
replacements: |
version=4.2.0
tool=myTool
prog=program
nightly_title: "Nightly Release"
nightly_description: |
This *nightly* release contains all latest and important artifacts created by GHDL's CI pipeline.
tag: 4.2.0
title: "Nightly Test Release"
description: |
This *nightly* release contains all latest and important artifacts created by %tool%'s CI pipeline.
# GHDL %version%
# %tool% %version%
GHDL offers the simulator and synthesis tool for VHDL. GHDL can be build for various backends:
* `gcc` - using the GCC compiler framework
* `mcode` - in memory code generation
* `llvm` - using the LLVM compiler framework
* `llvm-jit` - using the LLVM compiler framework, but in memory
The following asset categories are provided for GHDL:
* macOS x64-64 builds as TAR/GZ file
* macOS aarch64 builds as TAR/GZ file
* Ubuntu 24.04 LTS builds as TAR/GZ file
* Windows builds for standalone usage (without MSYS2) as ZIP file
* MSYS2 packages as TAR/ZST file
# pyGHDL %version%
The Python package `pyGHDL` offers Python binding (`pyGHDL.libghdl`) to a `libghdl` shared library (`*.so`/`*.dll`).
In addition to the low-level binding layer, pyGHDL offers:
* a Language Server Protocol (LSP) instance for e.g. live code checking by editors
* a Code Document Object Model (CodeDOM) based on [pyVHDLModel](https://github.com/VHDL/pyVHDLModel)
The following asset categories are provided for pyGHDL:
* Platform specific Python wheel package for Ubuntu incl. `pyGHDL...so`
* Platform specific Python wheel package for Windows incl. `pyGHDL...dll`
* %prog%
assets: |
document: document1.txt: Documentation
document: build.log: Logfile - %tool% - %tool%
@@ -99,3 +77,46 @@ jobs:
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/PublishReleaseNotes.yml
needs:
- Build
permissions:
contents: write
actions: write
with:
can-fail: true
replacements: |
version=4.2.0
tool=myTool
prog=program
tag: inventory
title: "Nightly Test Release with Inventory"
description: |
This *nightly* release contains all latest and important artifacts created by %tool%'s CI pipeline.
# %tool% %version%
* %prog%
* inventory.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

@@ -14,7 +14,7 @@ jobs:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: Example
python_version_list: "3.11 3.12 pypy-3.9 pypy-3.10"
python_version_list: "3.12 3.13 pypy-3.10 pypy-3.11"
Params_Systems:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
@@ -26,25 +26,25 @@ jobs:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: Example
python_version_list: "3.11"
python_version_list: "3.12"
system_list: "ubuntu windows macos macos-arm"
include_list: "ubuntu:3.12 ubuntu:3.13"
include_list: "ubuntu:3.13 ubuntu:3.14 ubuntu-arm:3.12"
Params_Exclude:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: Example
python_version_list: "3.12"
python_version_list: "3.13"
system_list: "ubuntu windows macos macos-arm"
exclude_list: "windows:3.12 windows:3.13"
exclude_list: "windows:3.13 windows:3.14"
Params_Disable:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
with:
name: Example
python_version_list: "3.12"
python_version_list: "3.13"
system_list: "ubuntu windows macos macos-arm"
disable_list: "windows:3.12 windows:3.13"
disable_list: "windows:3.13 windows:3.14"
Params_All:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@main
@@ -55,15 +55,9 @@ jobs:
include_list: "windows:3.10 windows:3.11 windows:3.13"
exclude_list: "macos:3.12 macos:3.13"
Params_Check:
Params_Check_Default:
needs:
- Params_Default
- Params_PythonVersions
- Params_Systems
- Params_Include
- Params_Exclude
- Params_Disable
- Params_All
runs-on: ubuntu-24.04
defaults:
run:
@@ -72,7 +66,7 @@ jobs:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
# Params_Default
- name: Checking results from 'Params_Default'
run: |
from json import loads as json_loads
@@ -82,24 +76,28 @@ jobs:
expectedPythonVersion = "3.13"
expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"]
expectedSystems = ["ubuntu", "ubuntu-arm", "windows", "windows-arm", "macos", "macos-arm"]
excludedJobs = ["windows-arm:3.9", "windows-arm:3.10"]
includeJobs = ["mingw64:3.12", "ucrt64:3.12"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_Default.outputs.python_version }}"""
@@ -135,7 +133,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_PythonVersions
Params_Check_PythonVersions:
needs:
- Params_PythonVersions
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_PythonVersions'
run: |
from json import loads as json_loads
@@ -144,25 +153,29 @@ jobs:
from pyTooling.Common import zipdicts
expectedPythonVersion = "3.13"
expectedPythons = ["3.11", "3.12", "pypy-3.9", "pypy-3.10"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw64:3.11", "ucrt64:3.11"]
expectedPythons = ["3.12", "3.13", "pypy-3.10", "pypy-3.11"]
expectedSystems = ["ubuntu", "ubuntu-arm", "windows", "windows-arm", "macos", "macos-arm"]
excludedJobs = ["windows-arm:pypy-3.10", "windows-arm:pypy-3.11"]
includeJobs = ["mingw64:3.12", "ucrt64:3.12"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_PythonVersions.outputs.python_version }}"""
@@ -198,7 +211,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_Systems
Params_Check_Systems:
needs:
- Params_Systems
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_Systems'
run: |
from json import loads as json_loads
@@ -209,23 +233,27 @@ jobs:
expectedPythonVersion = "3.13"
expectedPythons = ["3.9", "3.10", "3.11", "3.12", "3.13"]
expectedSystems = ["windows"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["mingw32:3.11", "mingw64:3.11"]
excludedJobs = []
includeJobs = ["mingw64:3.12", "ucrt64:3.12"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_Systems.outputs.python_version }}"""
@@ -261,7 +289,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_Include
Params_Check_Include:
needs:
- Params_Include
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_Include'
run: |
from json import loads as json_loads
@@ -272,23 +311,27 @@ jobs:
expectedPythonVersion = "3.13"
expectedPythons = ["3.12"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["ubuntu:3.11", "ubuntu:3.12"]
excludedJobs = []
includeJobs = ["ubuntu:3.13", "ubuntu:3.14", "ubuntu-arm:3.12"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_Include.outputs.python_version }}"""
@@ -324,7 +367,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_Exclude
Params_Check_Exclude:
needs:
- Params_Exclude
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_Exclude'
run: |
from json import loads as json_loads
@@ -333,25 +387,29 @@ jobs:
from pyTooling.Common import zipdicts
expectedPythonVersion = "3.13"
expectedPythons = ["3.12"]
expectedPythons = ["3.13"]
expectedSystems = ["ubuntu", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons]
excludedJobs = []
includeJobs = []
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}"""
@@ -387,7 +445,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_Disable
Params_Check_Disable:
needs:
- Params_Disable
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_Disable'
run: |
from json import loads as json_loads
@@ -396,30 +465,34 @@ jobs:
from pyTooling.Common import zipdicts
expectedPythonVersion = "3.13"
expectedPythons = ["3.12"]
expectedSystems = ["ubuntu", "macos", "macos-arm"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons]
expectedPythons = ["3.13"]
expectedSystems = ["ubuntu", "windows", "macos", "macos-arm"]
excludedJobs = ["windows:3.13"]
includeJobs = []
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_Exclude.outputs.python_version }}"""
actualPythonJobs = json_loads("""${{ needs.Params_Exclude.outputs.python_jobs }}""".replace("'", '"'))
actualArtifactNames = json_loads("""${{ needs.Params_Exclude.outputs.artifact_names }}""".replace("'", '"'))
actualPythonVersion = """${{ needs.Params_Disable.outputs.python_version }}"""
actualPythonJobs = json_loads("""${{ needs.Params_Disable.outputs.python_jobs }}""".replace("'", '"'))
actualArtifactNames = json_loads("""${{ needs.Params_Disable.outputs.artifact_names }}""".replace("'", '"'))
errors = 0
if actualPythonVersion != expectedPythonVersion:
@@ -450,7 +523,18 @@ jobs:
print(f"All checks PASSED.")
exit(errors)
# Params_All
Params_Check_All:
needs:
- Params_All
runs-on: ubuntu-24.04
defaults:
run:
shell: python
steps:
- name: Install dependencies
shell: bash
run: pip install --disable-pip-version-check --break-system-packages pyTooling
- name: Checking results from 'Params_All'
run: |
from json import loads as json_loads
@@ -461,23 +545,27 @@ jobs:
expectedPythonVersion = "3.13"
expectedPythons = ["3.12", "3.13"]
expectedSystems = ["ubuntu", "macos-arm", "windows"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons] + ["windows:3.10", "windows:3.11", "windows:3.13"]
excludedJobs = []
includeJobs = ["windows:3.10", "windows:3.11", "windows:3.13"]
expectedJobs = [f"{system}:{python}" for system in expectedSystems for python in expectedPythons if f"{system}:{python}" not in excludedJobs] + includeJobs
expectedName = "Example"
expectedArtifacts = {
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
"unittesting_xml": f"{expectedName}-UnitTestReportSummary-XML",
"unittesting_html": f"{expectedName}-UnitTestReportSummary-HTML",
"perftesting_xml": f"{expectedName}-PerformanceTestReportSummary-XML",
"benchtesting_xml": f"{expectedName}-BenchmarkTestReportSummary-XML",
"apptesting_xml": f"{expectedName}-ApplicationTestReportSummary-XML",
"codecoverage_sqlite": f"{expectedName}-CodeCoverage-SQLite",
"codecoverage_xml": f"{expectedName}-CodeCoverage-XML",
"codecoverage_json": f"{expectedName}-CodeCoverage-JSON",
"codecoverage_html": f"{expectedName}-CodeCoverage-HTML",
"statictyping_cobertura": f"{expectedName}-StaticTyping-Cobertura-XML",
"statictyping_junit": f"{expectedName}-StaticTyping-JUnit-XML",
"statictyping_html": f"{expectedName}-StaticTyping-HTML",
"package_all": f"{expectedName}-Packages",
"documentation_html": f"{expectedName}-Documentation-HTML",
"documentation_latex": f"{expectedName}-Documentation-LaTeX",
"documentation_pdf": f"{expectedName}-Documentation-PDF",
}
actualPythonVersion = """${{ needs.Params_All.outputs.python_version }}"""

View File

@@ -8,7 +8,12 @@ jobs:
SimplePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@main
with:
package_name: pyDummy
package_name: myPackage
codecov: true
codacy: true
dorny: true
cleanup: false
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}

7
.gitignore vendored
View File

@@ -15,6 +15,9 @@ coverage.xml
/report/unit
/tests/*.github
# bandit
/report/bandit
# setuptools
/build/**/*.*
/dist/**/*.*
@@ -25,8 +28,8 @@ coverage.xml
# Sphinx
doc/_build/
doc/pyDummy/**/*.*
!doc/pyDummy/index.rst
doc/myPackage/**/*.*
!doc/myPackage/index.rst
# BuildTheDocs
doc/_theme/**/*.*

8
.idea/Actions.iml generated
View File

@@ -1,7 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/myFramework" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/myPackage" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/doc/_build" />
<excludeFolder url="file://$MODULE_DIR$/report" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>

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
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,
[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.
Optionally, coverage and static type check reports can be gathered and integrated into the online documentation.
[![](ExamplePipeline_dark.png)](ExamplePipeline_dark.png)
[![](ExamplePipeline_light.png)](ExamplePipeline_light.png)
[![](doc/_static/pyTooling-Actions-SimplePackage.png)](doc/_static/pyTooling-Actions-SimplePackage.png)
As shown in the screenshots above, the expected order is:
- Global:
- [Parameters](.github/workflows/Parameters.yml): a workaround for the limitations to handle global variables in
GitHub Actions workflows (see [actions/runner#480](https://github.com/actions/runner/issues/480)).
It generates outputs with artifact names and job matrices to be used in later running jobs.
- Code testing/analysis:
- [UnitTesting](.github/workflows/UnitTesting.yml): run unit test with `pytest` using multiple versions of Python, and
optionally upload results as XML reports. Configuration options to `pytest` should be given via section
`[tool.pytest.ini_options]` in a `pyproject.toml` file.
- [CoverageCollection](.github/workflows/CoverageCollection.yml): collect code coverage data (incl. branch coverage)
with `pytest`/`pytest-cov`/`coverage.py` using a single version of Python (latest). It generates HTML and Cobertura
(XML)reports, upload the HTML report as an artifact, and upload the test results to Codecov and Codacy. Configuration
options to `pytest` and `coverage.py` should be given via section `[tool.pytest.ini_options]` and `[tool.coverage.*]`
in a `pyproject.toml` file.
- [StaticTypeCheck](.github/workflows/StaticTypeCheck.yml): collect static type check result with `mypy`, and
optionally upload results as an HTML report.
Example `commands`:
- **Global:**
[**Parameters**](.github/workflows/Parameters.yml): It generates output parameters 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
[actions/runner#480](https://github.com/actions/runner/issues/480)).
[**ExtractConfiguration**](.github/workflows/ExtractConfiguration.yml): extracts configuration values from
`pyproject.toml` and exposes configured paths and filenames as job output parameters.
- **Predefined pipelines:**
[**CompletePipeline**](.github/workflows/CompletePipeline.yml): is a predefined pipeline for typical Python projects
using all predefined job templates of pyTooling at once: (unit testing, code coverage, static typing, documentation
report generation and publishing, packaging, releasing, ...)
- **Code testing/analysis:**
[**ApplicationTesting**](.github/workflows/ApplicationTesting.yml): like UnitTesting, but running tests using an
installed Python package.
[**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
[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.
Minimal required modifications are the following:

View File

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

15
doc/Action/Actions.rst Normal file
View File

@@ -0,0 +1,15 @@
.. grid:: 2
.. grid-item::
:columns: 2
.. rubric:: Post-Processing
* :ref:`ACTION/WithPostStep`
.. grid-item::
:columns: 2
.. rubric:: Deprecated
* :ref:`ACTION/Releaser`

View File

@@ -1,8 +1,17 @@
.. _ACTION/Releaser:
.. index::
single: GitHub Action; Releaser
Releaser
########
.. attention::
The **Releaser** action is deprecated.
Use the new GitHub Action workflow template :ref:`JOBTMPL/PublishReleaseNotes` as a replacement with lots of
additional features.
**Releaser** is a Docker GitHub Action written in Python.
**Releaser** allows to keep a GitHub Release of type pre-release and its artifacts up to date with latest builds.
@@ -85,7 +94,7 @@ The following block shows a minimal YAML workflow file:
steps:
# Clone repository
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# Build your application, tool, artifacts, etc.
- name: Build

View File

@@ -1,4 +1,6 @@
.. _ACTION/WithPostStep:
.. index::
single: GitHub Action; WithPostStep
with-post-step
##############

View File

@@ -1,7 +1,31 @@
.. _ACTION:
.. index::
single: GitHub Action
Overview
########
The following 2 actions are provided by **Actions**:
* :ref:`ACTION/Releaser`
* :ref:`ACTION/WithPostStep`
.. include:: Actions.rst
.. _ACTION/Instantiation:
.. index::
single: GitHub Action; Instantiation
Instantiation
*************
.. code-block:: yaml
jobs:
<JobName>:
steps:
- ...
- name: <Name>
uses: ./with-post-step
with:
<Param1>: <Value1>
<Param2>: <Value2>

25
doc/CodeCoverage.rst Normal file
View File

@@ -0,0 +1,25 @@
.. _CODECOV:
Code Coverage Report
####################
.. grid:: 2
.. grid-item::
:columns: 8
.. report:code-coverage::
:reportid: src
.. grid-item::
:columns: 4
.. report:code-coverage-legend::
:reportid: src
:style: vertical-table
----------
Code coverage report generated with `pytest <https://github.com/pytest-dev/pytest>`__,
`Coverage.py <https://github.com/nedbat/coveragepy/tree/master>`__ and visualized by
`sphinx-reports <https://github.com/pyTooling/sphinx-reports>`__.

24
doc/DocCoverage.rst Normal file
View File

@@ -0,0 +1,24 @@
.. _DOCCOV:
Documentation Coverage Report
#############################
.. grid:: 2
.. grid-item::
:columns: 5
.. report:doc-coverage::
:reportid: src
.. grid-item::
:columns: 7
.. report:doc-coverage-legend::
:reportid: src
:style: vertical-table
----------
Documentation coverage generated with `"""docstr-coverage""" <https://github.com/HunterMcGushion/docstr_coverage>`__ and
visualized by `sphinx-reports <https://github.com/pyTooling/sphinx-reports>`__.

128
doc/Glossary.rst Normal file
View File

@@ -0,0 +1,128 @@
Glossary
########
.. glossary::
Bandit
Bandit is a tool designed to find common security issues in Python code.
:Source Code: `github.com/PyCQA/bandit/ <https://github.com/PyCQA/bandit/>`__
:Package: `pypi.org/project/bandit/ <https://pypi.org/project/bandit/>`__
:Documentation: `bandit.readthedocs.io/ <https://bandit.readthedocs.io/>`__
build
A simple, correct Python build frontend.
:Source Code: `github.com/pypa/build/ <https://github.com/pypa/build/>`__
:Package: `pypi.org/project/build/ <https://pypi.org/project/build/>`__
:Documentation: `build.pypa.io/ <https://build.pypa.io/>`__
Codacy
.. todo:: Add description of Codacy.
:Cloud Service: `Codacy.com <https://www.codacy.com/>`__
CodeCov
.. todo:: Add description of CodeCov.
:Cloud Service: `Codecov.io <https://about.codecov.io/>`__
Coverage.py
The code coverage tool for Python.
:Source Code: `github.com/nedbat/coveragepy/ <https://github.com/nedbat/coveragepy/>`__
:Package: `pypi.org/project/coverage/ <https://pypi.org/project/coverage/>`__
:Documentation: `coverage.readthedocs.io/ <https://coverage.readthedocs.io/>`__
delete-artifact
A GitHub Action to deletes artifacts within the workflow run.
:Source Code: `github.com/GeekyEggo/delete-artifact/ <https://github.com/GeekyEggo/delete-artifact/>`__
:Marketplace: `github.com/marketplace/actions/delete-artifact/ <https://github.com/marketplace/actions/delete-artifact/>`__
:README: `github.com/GeekyEggo/delete-artifact ⭢ README.md <https://github.com/GeekyEggo/delete-artifact/blob/main/README.md>`__
docstr_coverage
Docstring coverage analysis and rating for Python.
:Source Code: `github.com/HunterMcGushion/docstr_coverage/ <https://github.com/HunterMcGushion/docstr_coverage/>`__
:Package: `pypi.org/project/docstr_coverage/ <https://pypi.org/project/docstr_coverage/>`__
:Documentation: `docstr-coverage.readthedocs.io/ <https://docstr-coverage.readthedocs.io/>`__
gh
GitHubs official command line tool.
:Source Code: `github.com/cli/cli/ <https://github.com/cli/cli/>`__
:Documentation: `cli.github.com/manual/ <https://cli.github.com/manual/>`__
GitHub Pages
GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files straight from a repository
on GitHub, optionally runs the files through a build process, and publishes a website.
:Documentation: https://docs.github.com/en/pages
interrogate
Explain yourself! Interrogate a codebase for docstring coverage.
:Source Code: `github.com/econchick/interrogate/ <https://github.com/econchick/interrogate/>`__
:Package: `pypi.org/project/interrogate/ <https://pypi.org/project/interrogate/>`__
:Documentation: `interrogate.readthedocs.io/ <https://interrogate.readthedocs.io/>`__
MikTeX
MiKTeX is a modern TeX distribution for Windows, Linux and macOS.
:Source Code: `github.com/MiKTeX/miktex/ <https://github.com/MiKTeX/miktex/>`__
:Documentation: `miktex.org/ <https://miktex.org/>`__
mypy
Optional static typing for Python.
:Source Code: `github.com/python/mypy/ <https://github.com/python/mypy/>`__
:Package: `pypi.org/project/mypy/ <https://pypi.org/project/mypy/>`__
:Documentation: `www.mypy-lang.org/ <https://www.mypy-lang.org/>`__
pyEDAA.Reports
A collection of various (EDA tool-specific) report data formats.
:Source Code: `github.com/edaa-org/pyEDAA.Reports/ <https://github.com/edaa-org/pyEDAA.Reports/>`__
:Package: `pypi.org/project/pyEDAA.Reports/ <https://pypi.org/project/pyEDAA.Reports/>`__
:Documentation: `edaa-org.github.io/pyEDAA.Reports/ <https://edaa-org.github.io/pyEDAA.Reports/>`__
pip
The Python package installer.
:Source Code: `github.com/pypa/pip/ <https://github.com/pypa/pip/>`__
:Package: `pypi.org/project/pip/ <https://pypi.org/project/pip/>`__
:Documentation: `pip.pypa.io/ <https://pip.pypa.io/>`__
PyPI
Find, install and publish Python packages with the Python Package Index.
:Cloud Service: `PyPI.org <https://pypi.org/>`__
pytest
The pytest framework makes it easy to write small tests, yet scales to support complex functional testing.
:Source Code: `github.com/pytest-dev/pytest/ <https://github.com/pytest-dev/pytest/>`__
:Package: `pypi.org/project/pytest/ <https://pypi.org/project/pytest/>`__
:Documentation: `pytest.org/ <https://pytest.org/>`__
Sphinx
The Sphinx documentation generator.
:Source Code: `github.com/sphinx-doc/sphinx/ <https://github.com/sphinx-doc/sphinx/>`__
:Package: `pypi.org/project/sphinx/ <https://pypi.org/project/sphinx/>`__
:Documentation: `www.sphinx-doc.org/ <https://www.sphinx-doc.org/>`__
Test Reporter
Displays test results from popular testing frameworks directly in GitHub.
:Source Code: `github.com/dorny/test-reporter/ <https://github.com/dorny/test-reporter/>`__
:Marketplace: `github.com/marketplace/actions/test-reporter/ <https://github.com/marketplace/actions/test-reporter/>`__
:README: `github.com/dorny/test-reporter ⭢ README.md <https://github.com/dorny/test-reporter/blob/main/README.md>`__
twine
Utilities for interacting with PyPI.
:Source Code: `github.com/pypa/twine/ <https://github.com/pypa/twine/>`__
:Package: `pypi.org/project/twine/ <https://pypi.org/project/twine/>`__
:Documentation: `twine.readthedocs.io/ <https://twine.readthedocs.io/>`__

View File

@@ -36,10 +36,13 @@ to handover input parameters to the template.
on:
push:
workflow_dispatch:
schedule:
# Every Friday at 22:00 - rerun pipeline to check for dependency-based issues
- cron: '0 22 * * 5'
jobs:
<InstanceName>:
uses: <GitHubOrganization>/<Repository>/.github/workflows/<Template>.yml@v0
uses: <GitHubOrganization>/<Repository>/.github/workflows/<Template>.yml@r6
with:
<Param1>: <Value>
@@ -57,15 +60,18 @@ Documentation Only (Sphinx)
on:
push:
workflow_dispatch:
schedule:
# Every Friday at 22:00 - rerun pipeline to check for dependency-based issues
- cron: '0 22 * * 5'
jobs:
BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r6
with:
artifact: Documentation
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r6
needs:
- BuildTheDocs
with:

View File

@@ -0,0 +1,826 @@
.. _JOBTMPL/CompletePipeline:
.. index::
single: build; CompletePipeline Template
single: Bandit; CompletePipeline Template
single: CodeCov; CompletePipeline Template
single: Codacy; CompletePipeline Template
single: Coverage.py; CompletePipeline Template
single: docstr_coverage; CompletePipeline Template
single: GitHub Pages; CompletePipeline Template
single: interrogate; CompletePipeline Template
single: MikTeX; CompletePipeline Template
single: mypy; CompletePipeline Template
single: PyPI; CompletePipeline Template
single: pytest; CompletePipeline Template
single: pyEDAA.Reports; CompletePipeline Template
single: Sphinx; CompletePipeline Template
single: Test Reporter; CompletePipeline Template
single: twine; CompletePipeline Template
single: GitHub Action Reusable Workflow; CompletePipeline Template
CompletePipeline
################
The ``CompletePipeline`` job template is the combination of almost all job templates offered by pyTooling/Actions in a
single workflow template. If fulfills all needs to test, package, document, publish and release Python code from GitHub.
It can be used for simple Python packages as well as namespace packages.
.. topic:: Features
.. grid:: 3
.. grid-item::
:columns: 4
.. rubric:: Testing
* Run unit tests before packaging using :term:`pytest`.
* Run platform tests before packaging using :term:`pytest`.
* Run application tests using packaged code on target platform.
.. rubric:: Code Quality
* Collect code coverage using :term:`Coverage.py`.
* Check documentation coverage using :term:`docstr_coverage` and :term:`interrogate`.
* Check static typing closure using :term:`mypy`.
* Static Application Security Testing (SAST) using :term:`bandit`
.. rubric:: Report Handling
* Merge unit test results into one report using :term:`pyEDAA.Reports`.
* Merge code coverage results into one report using :term:`Coverage.py`.
.. grid-item::
:columns: 4
.. rubric:: Documentation
* Compile documentation using :term:`Sphinx` as HTML and LaTeX.
* Translate LaTeX documentation to PDF using :term:`MikTeX`.
.. rubric:: Publishing Results
* GitHub Pipeline Summary
* Publish unittest results using :term:`Test Reporter`.
* :term:`GitHub Pages`
* Publish HTML documentation to GitHub Pages.
* :term:`Codecov`
* Publish code coverage to CodeCov.
* Publish unittest results to CodeCov.
* :term:`Codacy`
* Publish code coverage to Codacy.
.. grid-item::
:columns: 4
.. rubric:: Packaging
* Package as wheel using :term:`build`.
* Install wheel on target platform using pip.
* Upload to PyPI using :term:`twine`.
.. rubric:: Releasing
* Automatic tagging of merge commits on main branch to trigger a tagged pipeline (release pipeline).
* Create a release page with text derived from pull request description.
.. topic:: Behavior
.. include:: _Behavior.rst
.. topic:: Pipeline Graph
.. image:: ../../_static/pyTooling-Actions-SimplePackage.png
.. topic:: Dependencies
.. dropdown:: Expand List
:animate: fade-in-slide-down
:icon: codescan
:color: muted
.. grid:: 2
.. grid-item::
:columns: 6
* :ref:`pyTooling/Actions/.github/workflows/PrepareJob.yml <JOBTMPL/PrepareJob>`
* :gh:`actions/checkout`
* :gh:`GitHub command line tool 'gh' <cli/cli>`
* :ref:`pyTooling/Actions/.github/workflows/Parameters.yml <JOBTMPL/Parameters>`
* :ref:`pyTooling/Actions/.github/workflows/ExtractConfiguration.yml <JOBTMPL/ExtractConfiguration>`
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :pypi:`wheel`
* :pypi:`tomli`
* :ref:`pyTooling/Actions/.github/workflows/UnitTesting.yml <JOBTMPL/UnitTesting>`
* :gh:`actions/checkout`
* :gh:`msys2/setup-msys2`
* :gh:`actions/setup-python`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* apt: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/apt` parameter.
* homebrew: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/brew` parameter.
* MSYS2: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/pacboy` parameter.
* pip
* :pypi:`wheel`
* :pypi:`tomli`
* Python packages specified via :ref:`JOBTMPL/UnitTesting/Input/requirements` or
:ref:`JOBTMPL/UnitTesting/Input/mingw_requirements` parameter.
* :ref:`pyTooling/Actions/.github/workflows/ApplicationTesting.yml <JOBTMPL/ApplicationTesting>`
* :ref:`pyTooling/Actions/.github/workflows/CheckDocumentation.yml <JOBTMPL/CheckDocumentation>`
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* pip
* :pypi:`docstr_coverage`
* :pypi:`interrogate`
* :ref:`pyTooling/Actions/.github/workflows/StaticTypeCheck.yml <JOBTMPL/StaticTypeCheck>`
* :ref:`pyTooling/Actions/.github/workflows/Package.yml <JOBTMPL/Package>`
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* pip
* :pypi:`build`
* :pypi:`wheel`
* :ref:`pyTooling/Actions/.github/workflows/PublishTestResults.yml <JOBTMPL/PublishTestResults>`
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* pip
* :pypi:`pyEDAA.Reports`
* :gh:`dorny/test-reporter`
* :gh:`codecov/test-results-action`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
.. grid-item::
:columns: 6
* :ref:`pyTooling/Actions/.github/workflows/PublishCoverageResults.yml <JOBTMPL/PublishCoverageResults>`
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* pip
* :pypi:`coverage`
* :pypi:`tomli`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* :gh:`codecov/codecov-action`
* :gh:`codacy/codacy-coverage-reporter-action`
* :ref:`pyTooling/Actions/.github/workflows/SphinxDocumentation.yml <JOBTMPL/SphinxDocumentation>`
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* apt
* `graphviz <https://graphviz.org/>`__
* pip
* :pypi:`wheel`
* Python packages specified via :ref:`JOBTMPL/SphinxDocumentation/Input/requirements` parameter.
* :ref:`pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml <JOBTMPL/LaTeXDocumentation>`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* :gh:`addnab/docker-run-action`
* :dockerhub:`pytooling/miktex <pytooling/miktex:sphinx>`
* :ref:`pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml <JOBTMPL/PublishToGitHubPages>`
* :ref:`pyTooling/Actions/.github/workflows/PublishOnPyPI.yml <JOBTMPL/PublishOnPyPI>`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`actions/setup-python`
* :gh:`geekyeggo/delete-artifact`
* pip
* :pypi:`wheel`
* :pypi:`twine`
* :ref:`pyTooling/Actions/.github/workflows/TagReleaseCommit.yml <JOBTMPL/TagReleaseCommit>`
* :gh:`actions/github-script`
* :ref:`pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml <JOBTMPL/PublishReleaseNotes>`
* :gh:`actions/checkout`
* ``gh`` (GitHub command line interface)
* ``jq`` (JSON processing)
* apt
* zstd
* :ref:`pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml <JOBTMPL/IntermediateCleanUp>`
* :gh:`geekyeggo/delete-artifact`
* :ref:`pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml <JOBTMPL/ArtifactCleanUp>`
* :gh:`geekyeggo/delete-artifact`
.. _JOBTMPL/CompletePipeline/Instantiation:
Instantiation
*************
The following instantiation example creates a ``SimplePackage`` job derived from job template ``CompletePipeline``
version ``@r6``. It only requires the `package_name` parameter to run a full pipeline suitable for a Python project.
.. grid:: 2
.. grid-item::
:columns: 6
.. tab-set::
.. tab-item:: Simple Package
:sync: Simple
.. code-block:: yaml
name: Pipeline
jobs:
SimplePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r6
with:
package_name: myPackage
.. tab-item:: Namespace Package
:sync: Namespace
.. code-block:: yaml
name: Pipeline
jobs:
NamespacePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r6
with:
package_namespace: myFramework
package_name: Extension
.. grid-item::
:columns: 6
.. tab-set::
.. tab-item:: Simple Package
:sync: Simple
.. code-block::
📂ProjectRoot/
📂myFramework/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. tab-item:: Namespace Package
:sync: Namespace
.. code-block::
📂ProjectRoot/
📂myFramework/
📂Extension/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. _JOBTMPL/CompletePipeline/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/CompletePipeline/Inputs>`
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================+
| :ref:`JOBTMPL/CompletePipeline/Input/package_namespace` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/package_name` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_python_version_list` | no | string | ``'3.9 3.10 3.11 3.12 3.13'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_system_list` | no | string | ``'ubuntu windows macos macos-arm ucrt64'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_include_list` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_exclude_list` | no | string | ``'windows-arm:3.9 windows-arm:3.10'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/unittest_disable_list` | no | string | ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_python_version_list` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_system_list` | no | string | ``'ubuntu windows macos macos-arm ucrt64'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_include_list` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_exclude_list` | no | string | ``'windows-arm:3.9 windows-arm:3.10'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/apptest_disable_list` | no | string | ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/codecov` | no | string | ``'false'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/codacy` | no | string | ``'false'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/dorny` | no | string | ``'false'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/CompletePipeline/Input/cleanup` | no | string | ``'true'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/CompletePipeline/Secrets>`
+-----------------------------------------------------------+----------+----------+--------------+
| Token Name | Required | Type | Default |
+===========================================================+==========+==========+==============+
| :ref:`JOBTMPL/CompletePipeline/Secret/PYPI_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
| :ref:`JOBTMPL/CompletePipeline/Secret/CODECOV_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
| :ref:`JOBTMPL/CompletePipeline/Secret/CODACY_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
.. rubric:: Goto :ref:`output parameters <JOBTMPL/CompletePipeline/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/CompletePipeline/Inputs:
Input Parameters
****************
.. _JOBTMPL/CompletePipeline/Input/package_namespace:
package_namespace
=================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid Python namespace.
:Description: In case the package is a Python namespace package, the name of the library's or package's namespace
needs to be specified using this parameter. |br|
In case of a simple Python package, this parameter must be specified as an empty string (``''``),
which is the default.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Example Instantiation
.. code-block:: yaml
name: Pipeline
jobs:
NamespacePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r6
with:
package_namespace: myFramework
package_name: Extension
.. grid-item::
:columns: 4
.. rubric:: Example Directory Structure
.. code-block::
📂ProjectRoot/
📂myFramework/
📂Extension/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. _JOBTMPL/CompletePipeline/Input/package_name:
package_name
============
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid Python package name.
:Description: In case of a simple Python package, this package's name is specified using this parameter. |br|
In case the package is a Python namespace package, the parameter
:ref:`JOBTMPL/CompletePipeline/Input/package_namespace` must be specified, too.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Example Instantiation
.. code-block:: yaml
name: Pipeline
jobs:
SimplePackage:
uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r6
with:
package_name: myPackage
.. grid-item::
:columns: 4
.. rubric:: Example Directory Structure
.. code-block::
📂ProjectRoot/
📂myFramework/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. _JOBTMPL/CompletePipeline/Input/unittest_python_version:
unittest_python_version
=======================
:Type: string
:Required: no
:Default Value: ``'3.13'``
:Possible Values: Any valid Python version conforming to the pattern ``<major>.<minor>`` or ``pypy-<major>.<minor>``. |br|
See `actions/python-versions - available Python versions <https://github.com/actions/python-versions>`__
and `actions/setup-python - configurable Python versions <https://github.com/actions/setup-python>`__.
:Description: The default Python version used for intermediate jobs using Python tools.
In case :ref:`JOBTMPL/CompletePipeline/Input/unittest_python_version_list` is empty, this default
version is used to populate the :ref:`JOBTMPL/CompletePipeline/Input/unittest_python_version_list`
parameter.
.. _JOBTMPL/CompletePipeline/Input/unittest_python_version_list:
unittest_python_version_list
============================
:Type: string
:Required: no
:Default Value: ``'3.9 3.10 3.11 3.12 3.13'``
:Possible Values: A space separated list of valid Python versions conforming to the pattern ``<major>.<minor>`` or
``pypy-<major>.<minor>``.
:Description: The list of space-separated Python versions used for unit testing.
.. include:: ../PythonVersionList.rst
.. _JOBTMPL/CompletePipeline/Input/unittest_system_list:
unittest_system_list
====================
:Type: string
:Required: no
:Default Value: ``'ubuntu windows macos macos-arm mingw64 ucrt64'``
:Possible Values: A space separated list of system names.
:Description: The list of space-separated systems used for unit testing.
.. include:: ../SystemList.rst
.. _JOBTMPL/CompletePipeline/Input/unittest_include_list:
unittest_include_list
=====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be included into the list of unittest
variants.
For more details see :ref:`JOBTMPL/Parameters/Input/include_list`.
.. _JOBTMPL/CompletePipeline/Input/unittest_exclude_list:
unittest_exclude_list
=====================
:Type: string
:Required: no
:Default Value: ``'windows-arm:3.9 windows-arm:3.10'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be excluded from the list of unittest
variants.
For more details see :ref:`JOBTMPL/Parameters/Input/exclude_list`.
.. _JOBTMPL/CompletePipeline/Input/unittest_disable_list:
unittest_disable_list
=====================
:Type: string
:Required: no
:Default Value: ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be temporarily disabled from the list
of unittest variants. |br|
Each disabled item creates a warning in the workflow log.
For more details see :ref:`JOBTMPL/Parameters/Input/disable_list`.
.. _JOBTMPL/CompletePipeline/Input/apptest_python_version:
apptest_python_version
======================
:Type: string
:Required: no
:Default Value: ``'3.13'``
:Possible Values: Any valid Python version conforming to the pattern ``<major>.<minor>`` or ``pypy-<major>.<minor>``. |br|
See `actions/python-versions - available Python versions <https://github.com/actions/python-versions>`__
and `actions/setup-python - configurable Python versions <https://github.com/actions/setup-python>`__.
:Description: The default Python version used for intermediate jobs using Python tools.
In case :ref:`JOBTMPL/CompletePipeline/Input/apptest_python_version_list` is empty, this default
version is used to populate the :ref:`JOBTMPL/CompletePipeline/Input/apptest_python_version_list`
parameter.
.. _JOBTMPL/CompletePipeline/Input/apptest_python_version_list:
apptest_python_version_list
===========================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: A space separated list of valid Python versions conforming to the pattern ``<major>.<minor>`` or
``pypy-<major>.<minor>```.
:Description: The list of space-separated Python versions used for application testing.
As this list is empty by default, the value is derived from
:ref:`JOBTMPL/CompletePipeline/Input/apptest_python_version`.
.. include:: ../PythonVersionList.rst
.. _JOBTMPL/CompletePipeline/Input/apptest_system_list:
apptest_system_list
===================
:Type: string
:Required: no
:Default Value: ``'ubuntu windows macos macos-arm mingw64 ucrt64'``
:Possible Values: A space separated list of system names.
:Description: The list of space-separated systems used for application testing.
.. include:: ../SystemList.rst
.. _JOBTMPL/CompletePipeline/Input/apptest_include_list:
apptest_include_list
====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be included into the list of
application test variants.
For more details see :ref:`JOBTMPL/Parameters/Input/include_list`.
.. _JOBTMPL/CompletePipeline/Input/apptest_exclude_list:
apptest_exclude_list
====================
:Type: string
:Required: no
:Default Value: ``'windows-arm:3.9 windows-arm:3.10'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be excluded from the list of
application test variants.
For more details see :ref:`JOBTMPL/Parameters/Input/exclude_list`.
.. _JOBTMPL/CompletePipeline/Input/apptest_disable_list:
apptest_disable_list
====================
:Type: string
:Required: no
:Default Value: ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be temporarily disabled from the list
of application test variants. |br|
Each disabled item creates a warning in the workflow log.
For more details see :ref:`JOBTMPL/Parameters/Input/disable_list`.
.. _JOBTMPL/CompletePipeline/Input/codecov:
codecov
=======
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *true*, publish merged code coverage results and a merged unit test summary to CodeCov. |br|
Secret :ref:`JOBTMPL/CompletePipeline/Secret/CODECOV_TOKEN` must be set.
.. _JOBTMPL/CompletePipeline/Input/codacy:
codacy
======
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *true*, publish merged code coverage results to Codacy. |br|
Secret :ref:`JOBTMPL/CompletePipeline/Secret/CODACY_TOKEN` must be set.
.. _JOBTMPL/CompletePipeline/Input/dorny:
dorny
=====
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *true*, publish a merged unit test summary as pipeline result.
.. _JOBTMPL/CompletePipeline/Input/cleanup:
cleanup
=======
:Type: string
:Required: no
:Default Value: ``'true'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *false*, do not remove intermediate artifacts. |br|
This might help debugging artifact handovers between jobs.
.. _JOBTMPL/CompletePipeline/Secrets:
Secrets
*******
The workflow template uses the following secrets to publish results to other services.
.. _JOBTMPL/CompletePipeline/Secret/PYPI_TOKEN:
PYPI_TOKEN
==========
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish and upload packages on :term:`PyPI`.
.. _JOBTMPL/CompletePipeline/Secret/CODECOV_TOKEN:
CODECOV_TOKEN
=============
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish code coverage and unit test results to :term:`CodeCov`.
.. _JOBTMPL/CompletePipeline/Secret/CODACY_TOKEN:
CODACY_TOKEN
============
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish code coverage results to :term:`Codacy`.
.. _JOBTMPL/CompletePipeline/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/CompletePipeline/Optimizations:
Optimizations
*************
The following optimizations can be used to reduce the template's runtime.
.. todo::
CompletePipeline::Optimizations Needs a list of optimizations.

View File

@@ -0,0 +1,16 @@
1. Infer information from ``${{ github.ref }}`` variable.
2. Extract Python project settings from :file:`pyproject.toml`.
3. Compute job matrix based on system, Python version, environment, ... for job variants.
4. Run unit tests using pytest and collect code coverage.
5. Run platform tests using pytest and collect code coverage.
6. Run application tests using pytest.
7. Package code as wheel.
8. Check documentation coverage using docstr_coverage and interrogate.
9. Verify type annotation using static typing analysis using mypy.
10. Merge unit test results and code coverage results.
11. Generate HTML and LaTeX documentations using Sphinx.
12. Translate LaTeX documentation to PDF using MikTeX.
13. Publish unit test and code coverage results to cloud services.
14. Publish documentation to GitHub Pages.
15. Publish wheel to PyPI.
16. Create a GitHub release page and upload release assets.

View File

@@ -0,0 +1,32 @@
.. _JOBTMPL/AllInOne:
All-In-One
##########
The category *all-in-one* provides workflow templates implementing all necessary steps (jobs) for testing and publishing
a Python project. It utilizes allmost all of ``pyTooling/Acion``'s workflow templates.
Such a all-in-one workflow template covers:
* unit testing
* code coverage collections
* documentation checking
* pulishing of unit test and code coverage results
* merging of test reports
* packaging as wheel
* publishing wheels to PyPI
* documentation generation via Sphinx and Miktex
* automatic tagging of release commits
* releasing
.. topic:: Provides *all-in-one* workflow templates
* :ref:`JOBTMPL/CompletePipeline` - Use all of ``pyTooling/Acion``'s workflow templates by instantiation of a single
workflow template.
.. image:: ../../_static/pyTooling-Actions-SimplePackage.png
.. toctree::
:hidden:
CompletePipeline

View File

@@ -1,90 +0,0 @@
.. _JOBTMPL/ArtifactCleanup:
ArtifactCleanUp
###############
This job removes artifacts used to exchange data from job to job.
**Behavior:**
1. Delete the package artifact if the current pipeline run was not a tagged run.
2. Delete all remaining artifacts if given as a parameter.
**Dependencies:**
* :gh:`geekyeggo/delete-artifact`
Instantiation
*************
Simple Example
==============
The simplest variant just uses the artifact name for the package.
.. code-block:: yaml
jobs:
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0
with:
package: Package
Complex Example
===============
.. code-block:: yaml
jobs:
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0
needs:
- Params
- UnitTesting
- BuildTheDocs
- PublishToGitHubPages
- PublishTestResults
with:
package: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
remaining: |
${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-*
Parameters
**********
package
=======
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| package | yes | string | — — — — |
+----------------+----------+----------+----------+
Artifacts to be removed on not tagged runs.
remaining
=========
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| remaining | optional | string | ``""`` |
+----------------+----------+----------+----------+
Artifacts to be removed unconditionally.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -1,75 +0,0 @@
.. _JOBTMPL/BuildTheDocs:
BuildTheDocs
############
This jobs compiles the documentation written in ReStructured Text with Sphinx using BuildTheDocs.
**Behavior:**
1. Checkout repository.
2. Build the documentation.
3. Upload the HTML documentation as an artifact.
4. Publish the HTML documentation to GitHub Pages.
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`buildthedocs/btd`
* :gh:`actions/upload-artifact`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0
Complex Example
===============
.. code-block:: yaml
jobs:
BuildTheDocs:
uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0
needs:
- Params
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}
Parameters
**********
artifact
========
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| artifact | optional | string | ``""`` |
+----------------+----------+----------+--------------+
Name of the documentation artifact.
If no artifact name is given, the job directly publishes the documentation's HTML content to GitHub Pages.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,157 @@
.. _JOBTMPL/ArtifactCleanup:
.. index::
single: delete-artifact; ArtifactCleanUp Template
single: GitHub Action Reusable Workflow; ArtifactCleanUp Template
ArtifactCleanUp
###############
This job removes artifacts which were used to exchange data between jobs.
.. topic:: Features
* Delete artifacts from pipeline.
.. topic:: Behavior
1. Delete the package artifact if the current pipeline run was not a tagged run.
2. Delete all remaining artifacts if given as a parameter.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-ArtifactCleanup.png
:width: 350px
.. topic:: Dependencies
* :gh:`geekyeggo/delete-artifact`
.. _JOBTMPL/ArtifactCleanup/Instantiation:
Instantiation
*************
Simple Example
==============
The simplest variant just uses the artifact name for the package.
.. code-block:: yaml
jobs:
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r6
with:
package: Package
Complex Example
===============
.. code-block:: yaml
jobs:
ArtifactCleanUp:
uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r6
needs:
- Params
- UnitTesting
- BuildTheDocs
- PublishToGitHubPages
- PublishTestResults
with:
package: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
remaining: |
${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-*
.. seealso::
:ref:`JOBTMPL/IntermediateCleanUp`
``IntermediateCleanUp`` is used to remove intermediate artifacts like unit test artifacts for each job variant
after test results have been merged into a single file.
.. _JOBTMPL/ArtifactCleanup/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/ArtifactCleanup/Inputs>`
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================+
| :ref:`JOBTMPL/ArtifactCleanup/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/ArtifactCleanup/Input/package` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/ArtifactCleanup/Input/remaining` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/ArtifactCleanup/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/ArtifactCleanup/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/ArtifactCleanup/Inputs:
Input Parameters
****************
.. _JOBTMPL/ArtifactCleanup/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/ArtifactCleanup/Input/package:
package
=======
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Multi-line string accepting any valid artifact name per line.
:Description: Artifacts to be removed on not tagged runs.
.. _JOBTMPL/ArtifactCleanup/Input/remaining:
remaining
=========
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Multi-line string accepting any valid artifact name per line.
:Description: Versi
.. _JOBTMPL/ArtifactCleanup/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/ArtifactCleanup/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/ArtifactCleanup/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,141 @@
.. _JOBTMPL/IntermediateCleanUp:
.. index::
single: delete-artifact; IntermediateCleanUp Template
single: GitHub Action Reusable Workflow; IntermediateCleanUp Template
IntermediateCleanUp
###################
The ``IntermediateCleanUp`` job template is used to remove intermediate artifacts like unit test artifacts for each job
variant after test results have been merged into a single file.
.. topic:: Features
* Delete artifacts from pipeline.
.. topic:: Behavior
1. Delete all SQLite code coverage artifacts if given as a parameter.
2. Delete all JUnit XML report artifacts if given as a parameter.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-IntermediateCleanUp.png
:width: 400px
.. topic:: Dependencies
* :gh:`geekyeggo/delete-artifact`
.. _JOBTMPL/IntermediateCleanUp/Instantiation:
Instantiation
*************
The following instantiation example creates a ``Params`` job derived from job template ``Parameters`` version ``@r6``. It only
requires a `name` parameter to create the artifact names.
.. code-block:: yaml
jobs:
IntermediateCleanUp:
uses: pyTooling/Actions/.github/workflows/IntermediateCleanUp.yml@r6
needs:
- UnitTestingParams
- PublishCoverageResults
- PublishTestResults
if: success() || failure()
with:
sqlite_coverage_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}-
xml_unittest_artifacts_prefix: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}-
.. seealso::
:ref:`JOBTMPL/ArtifactCleanup`
``ArtifactCleanup`` is used to remove artifacts like unit test report artifacts after artifact's content has been
(post-)processed or published.
.. _JOBTMPL/IntermediateCleanUp/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/IntermediateCleanUp/Inputs>`
+----------------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| Parameter Name | Required | Type | Default |
+============================================================================+==========+==========+===================================================+
| :ref:`JOBTMPL/IntermediateCleanUp/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+----------------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/IntermediateCleanUp/Input/sqlite_coverage_artifacts_prefix` | no | string | ``''`` |
+----------------------------------------------------------------------------+----------+----------+---------------------------------------------------+
| :ref:`JOBTMPL/IntermediateCleanUp/Input/xml_unittest_artifacts_prefix` | no | string | ``''`` |
+----------------------------------------------------------------------------+----------+----------+---------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/IntermediateCleanUp/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/IntermediateCleanUp/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/IntermediateCleanUp/Inputs:
Input Parameters
****************
.. _JOBTMPL/IntermediateCleanUp/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/IntermediateCleanUp/Input/sqlite_coverage_artifacts_prefix:
sqlite_coverage_artifacts_prefix
================================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name including ``*``.
:Description: Prefix for SQLite coverage artifacts to be removed.
.. _JOBTMPL/IntermediateCleanUp/Input/xml_unittest_artifacts_prefix:
xml_unittest_artifacts_prefix
=============================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name including ``*``.
:Description: Prefix for XML unittest artifacts to be removed.
.. _JOBTMPL/IntermediateCleanUp/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/IntermediateCleanUp/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/IntermediateCleanUp/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,28 @@
.. _JOBTMPL/Cleanup:
Cleanup
#######
The category *cleanup* provides workflow templates implementing artifact cleanups (removals) from pipelines.
Running lots of unit testing, platform testing and application testing variants (operating system |times| Python version
|times| environment) creates dozens to hundrets of artifacts (unit test report, code coverage report, ...). This
consumes pipeline storage which can be freed up. Moreover, it's unclear which artifacts contain the final unit test and
code coverage reports. Thus, it's benefitial, to remove intermediate artifacts after merging reports into one summary
report.
.. topic:: Intermediate cleanups
* :ref:`JOBTMPL/IntermediateCleanup` - remove intermediate artifacts after merging reports into one summary report.
.. topic:: Final cleanups
* :ref:`JOBTMPL/ArtifactCleanup` - remove artifacts after publising results and creating release assets.
.. toctree::
:hidden:
IntermediateCleanup
ArtifactCleanup

View File

@@ -1,170 +0,0 @@
.. _JOBTMPL/CodeCoverage:
CoverageCollection
##################
This jobs runs the specified unit tests with activated code coverage collection (incl. branch coverage).
It uses pytest, pytest-cov and coverage.py in a single job run, thus it uses one fixed Python version (usually latest).
It generates HTML and Cobertura reports (XML), then it uploads the HTML report as an artifact and the jUnit test results
(XML) to `Codecov <https://about.codecov.io/>`__ and `Codacy <https://www.codacy.com/>`__.
Configuration options to ``pytest`` and ``coverage.py`` should be given via sections ``[tool.pytest.ini_options]`` and
``[tool.coverage.*]`` in a ``pyproject.toml`` file.
**Behavior:**
1. Checkout repository
2. Setup Python and install dependencies
3. Extract configuration from ``pyproject.toml`` or ``.coveragerc``.
4. Run unit tests and collect code coverage
5. Convert coverage data to a Cobertura XML file
6. Convert coverage data to a HTML report
7. Upload HTML report as an artifact
8. Publish Cobertura file to CodeCov
9. Publish Cobertura file to Codacy
**Preconditions:**
* A CodeCov account was created.
* A Codacy account was created.
**Requirements:**
Setup a secret (e.g. ``codacy_token``) in GitHub to handover the Codacy project token to the job.
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`actions/upload-artifact`
* :gh:`codecov/codecov-action`
* :gh:`codacy/codacy-coverage-reporter-action`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0
with:
artifact: Coverage
secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
Complex Example
===============
.. code-block:: yaml
jobs:
Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0
needs:
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
secrets:
codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }}
Parameters
**********
python_version
==============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| python_version | optional | string | 3.11 |
+----------------+----------+----------+----------+
Python version used for running unit tests.
requirements
============
+----------------+----------+----------+-------------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+===============================+
| requirements | optional | string | ``-r tests/requirements.txt`` |
+----------------+----------+----------+-------------------------------+
Python dependencies to be installed through pip.
tests_directory
===============
+-----------------+----------+----------+-----------+
| Parameter Name | Required | Type | Default |
+=================+==========+==========+===========+
| tests_directory | optional | string | ``tests`` |
+-----------------+----------+----------+-----------+
Path to the directory containing tests (test working directory).
unittest_directory
==================
+--------------------+----------+----------+-----------+
| Parameter Name | Required | Type | Default |
+====================+==========+==========+===========+
| unittest_directory | optional | string | ``unit`` |
+--------------------+----------+----------+-----------+
Path to the directory containing unit tests (relative to tests_directory).
coverage_config
===============
+-----------------+----------+----------+--------------------+
| Parameter Name | Required | Type | Default |
+=================+==========+==========+====================+
| coverage_config | optional | string | ``pyproject.toml`` |
+-----------------+----------+----------+--------------------+
Path to the ``.coveragerc`` file. Use ``pyproject.toml`` by default.
artifact
========
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| artifact | yes | string | — — — — |
+----------------+----------+----------+--------------+
Name of the coverage artifact.
Secrets
*******
codacy_token
============
+----------------+----------+----------+--------------+
| Secret Name | Required | Type | Default |
+================+==========+==========+==============+
| codacy_token | yes | string | — — — — |
+----------------+----------+----------+--------------+
Token to push result to codacy.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,10 @@
.. _JOBTMPL/BuildTheDocs:
BuildTheDocs
############
.. attention::
The ``BuildTheDocs`` job template is deprecated.
See :ref:`JOBTMPL/SphinxDocumentation` and :ref:`JOBTMPL/LaTeXDocumentation`.

View File

@@ -0,0 +1,10 @@
.. _JOBTMPL/CoverageCollection:
CoverageCollection
##################
.. attention::
The ``CoverageCollection`` job template is deprecated.
See :ref:`JOBTMPL/UnitTesting` and :ref:`JOBTMPL/PublishCoverageResults`.

View File

@@ -0,0 +1,10 @@
.. _JOBTMPL/NightlyRelease:
NightlyRelease
##############
.. attention::
The ``NightlyRelease`` job template is deprecated.
See :ref:`JOBTMPL/PublishReleaseNotes`.

View File

@@ -0,0 +1,20 @@
.. _JOBTMPL/Deprecated:
Deprecated
##########
The category *deprecated* collects outdated job templates:
:ref:`JOBTMPL/CoverageCollection`
replaced by :ref:`JOBTMPL/UnitTesting`
:ref:`JOBTMPL/NightlyRelease`
replaced by :ref:`JOBTMPL/PublishReleaseNotes`
:ref:`JOBTMPL/BuildTheDocs`
replaced by :ref:`JOBTMPL/SphinxDocumentation` and :ref:`JOBTMPL/LaTeXDocumentation`
.. toctree::
:hidden:
CoverageCollection
NightlyRelease
BuildTheDocs

View File

@@ -0,0 +1,188 @@
.. _JOBTMPL/LaTeXDocumentation:
.. index::
single: MikTeX; LaTeXDocumentation Template
single: GitHub Action Reusable Workflow; LaTeXDocumentation Template
LaTeXDocumentation
##################
The ``LaTeXDocumentation`` job template downloads an artifact containing a LaTeX document and translates to a PDF file
using MikTeX.
The translation process uses ``latexmk`` for handling multiple passes. The default LaTeX processor is ``xelatex``, but
can be switched by a parameter.
.. topic:: Features
* Translate a LaTeX document to PDF.
.. topic:: Behavior
1. Download the LaTeX artifact.
2. Build the PDF using ``latexmk``.
3. Upload the generated PDF as an artifact.
.. topic:: Dependencies
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* :gh:`addnab/docker-run-action`
* :dockerhub:`pytooling/miktex <pytooling/miktex:sphinx>`
.. _JOBTMPL/LaTeXDocumentation/Instantiation:
Instantiation
*************
.. code-block:: yaml
jobs:
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r6
needs:
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
PDFDocumentation:
uses: pyTooling/Actions/.github/workflows/LaTeXDocumentation.yml@r6
needs:
- UnitTestingParams
- Documentation
with:
document: pyEDAA.ProjectModel
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
pdf_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_pdf }}
.. _JOBTMPL/LaTeXDocumentation/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/LaTeXDocumentation/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/LaTeXDocumentation/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/LaTeXDocumentation/Input/latex_artifact` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/LaTeXDocumentation/Input/document` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/LaTeXDocumentation/Input/processor` | no | string | ``'xelatex'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/LaTeXDocumentation/Input/pdf_artifact` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/LaTeXDocumentation/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/LaTeXDocumentation/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/LaTeXDocumentation/Inputs:
Input Parameters
****************
.. _JOBTMPL/LaTeXDocumentation/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/LaTeXDocumentation/Input/latex_artifact:
latex_artifact
==============
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the LaTeX document to translate.
.. _JOBTMPL/LaTeXDocumentation/Input/document:
document
========
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid document name.
:Description: Name of the LaTeX document
.. _JOBTMPL/LaTeXDocumentation/Input/processor:
processor
=========
:Type: string
:Required: no
:Default Value: ``'xelatex'``
:Possible Values: Any supported LaTeX processor supported by MikTeX and ``latexmk``.
:Description: Name of the used LaTeX processor.
.. _JOBTMPL/LaTeXDocumentation/Input/pdf_artifact:
pdf_artifact
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the generated PDF document.
:Optimization:
.. hint::
If this parameter is empty, no PDF file will be generated and no artifact will be uploaded.
.. _JOBTMPL/LaTeXDocumentation/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/LaTeXDocumentation/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/LaTeXDocumentation/Optimizations:
Optimizations
*************
The following optimizations can be used to reduce the template's runtime.
Disable PDF generation and PDF artifact
If parameter :ref:`JOBTMPL/LaTeXDocumentation/Input/pdf_artifact` is empty, no PDF will be generated and uploaded.

View File

@@ -0,0 +1,178 @@
.. _JOBTMPL/PublishToGitHubPages:
.. index::
single: GitHub Pages; PublishToGitHubPages Template
single: GitHub Action Reusable Workflow; PublishToGitHubPages Template
PublishToGitHubPages
####################
This job template publishes HTML content from artifacts of other jobs to GitHub Pages.
.. topic:: Features
tbd
.. topic:: Behavior
1. Checkout repository.
2. Download artifacts.
3. Push HTML files to branch ``gh-pages``.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-PublishToGitHubPages.png
:width: 500px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
.. _JOBTMPL/PublishToGitHubPages/Instantiation:
Instantiation
*************
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Simple Example
.. code-block:: yaml
jobs:
BuildTheDocs:
# ...
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r6
needs:
- BuildTheDocs
with:
doc: Documentation
.. grid-item::
:columns: 5
.. rubric:: Complex Example
.. code-block:: yaml
jobs:
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r6
needs:
- Params
- BuildTheDocs
- Coverage
- StaticTypeCheck
with:
doc: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}
coverage: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
typing: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
.. _JOBTMPL/PublishToGitHubPages/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PublishToGitHubPages/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/PublishToGitHubPages/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishToGitHubPages/Input/doc` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishToGitHubPages/Input/coverage` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishToGitHubPages/Input/typing` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PublishToGitHubPages/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PublishToGitHubPages/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/PublishToGitHubPages/Inputs:
Input Parameters
****************
.. _JOBTMPL/PublishToGitHubPages/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/PublishToGitHubPages/Input/doc:
doc
===
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid artifact name.
:Description: Name of the documentation artifact containing the HTML website.
.. _JOBTMPL/PublishToGitHubPages/Input/coverage:
coverage
========
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the coverage artifact containing the HTML coverage report, which will be integrated as a
subdirectory.
.. _JOBTMPL/PublishToGitHubPages/Input/typing:
typing
======
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the type checking artifact containing the HTML type checker report, which will be integrated
as a subdirectory.
.. _JOBTMPL/PublishToGitHubPages/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/PublishToGitHubPages/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/PublishToGitHubPages/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,327 @@
.. _JOBTMPL/SphinxDocumentation:
.. index::
single: Sphinx; SphinxDocumentation Template
single: GitHub Action Reusable Workflow; SphinxDocumentation Template
SphinxDocumentation
###################
The ``SphinxDocumentation`` job template compiles the ReStructured Text documentation using :term:`Sphinx` to an HTML
website and a LaTeX documentation. This LaTeX document can be translated using e.g. :term:`MikTeX` to a PDF file.
.. topic:: Features
* Build documentation using Sphinx as HTML and upload as artifact. |br|
(see :ref:`JOBTMPL/SphinxDocumentation/Input/html_artifact`).
* Build documentation using Sphinx as LaTeX and upload as artifact. |br|
(see :ref:`JOBTMPL/SphinxDocumentation/Input/latex_artifact`).
* Workaround `sphinx-doc/sphinx#13189 <https://github.com/sphinx-doc/sphinx/issues/13189>`__
* Workaround `sphinx-doc/sphinx#13190 <https://github.com/sphinx-doc/sphinx/issues/13190>`__
* Optionally: download code coverage artifact (JSON format) given by :ref:`JOBTMPL/SphinxDocumentation/Input/coverage_json_artifact`.
* Optionally: download unit test report artifact (XML format) given by :ref:`JOBTMPL/SphinxDocumentation/Input/unittest_xml_artifact`.
.. topic:: Behavior
1. Checkout repository.
2. Install system dependencies.
3. Setup Python environment and install Python dependencies.
4. Download optional artifacts for integration of further reports into the documentation.
5. Build the HTML documentation using Sphinx.
6. Build the LaTeX documentation using Sphinx.
1. Apply LaTeX workaround I.
2. Apply LaTeX workaround II.
7. Upload the HTML and LaTeX artifacts.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-SphinxDocumentation.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* apt
* `graphviz <https://graphviz.org/>`__
* pip
* :pypi:`wheel`
* Python packages specified via :ref:`JOBTMPL/SphinxDocumentation/Input/requirements` parameter.
.. _JOBTMPL/SphinxDocumentation/Instantiation:
Instantiation
*************
.. code-block:: yaml
jobs:
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r6
needs:
- UnitTestingParams
with:
python_version: ${{ needs.UnitTestingParams.outputs.python_version }}
html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_html }}
latex_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).documentation_latex }}
.. _JOBTMPL/SphinxDocumentation/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/SphinxDocumentation/Inputs>`
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=========================================================================+==========+================+===================================================================+
| :ref:`JOBTMPL/SphinxDocumentation/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/python_version` | no | string | ``'3.13'`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/requirements` | no | string | ``'-r doc/requirements.txt'`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/doc_directory` | no | string | ``'doc'`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/coverage_json_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/coverage_report_json` | no | string (JSON) | :jsoncode:`{"directory": "report/coverage"}` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/unittest_xml_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/unittest_xml` | no | string (JSON) | :jsoncode:`{"directory": "report/unit"}` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/html_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/SphinxDocumentation/Input/latex_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/SphinxDocumentation/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/SphinxDocumentation/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/SphinxDocumentation/Inputs:
Input Parameters
****************
.. _JOBTMPL/SphinxDocumentation/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/SphinxDocumentation/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/SphinxDocumentation/Input/requirements:
requirements
============
:Type: string
:Required: no
:Default Value: ``'-r doc/requirements.txt'``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'Sphinx sphinx_rtd_theme sphinxcontrib-mermaid'``.
:Description: Python dependencies to be installed through *pip*.
.. _JOBTMPL/SphinxDocumentation/Input/doc_directory:
doc_directory
=============
:Type: string
:Required: no
:Default Value: ``'doc'``
:Possible Values: Any valid directory or sub-directory.
:Description: Directory where the Sphinx documentation is located.
.. attention::
When this parameter gets changed, :ref:`JOBTMPL/SphinxDocumentation/Input/requirements` needs to be
adjusted as well.
.. _JOBTMPL/SphinxDocumentation/Input/coverage_json_artifact:
coverage_json_artifact
======================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the code coverage report in JSON format.
.. _JOBTMPL/SphinxDocumentation/Input/coverage_report_json:
coverage_report_json
====================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage",
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage JSON report will be extracted from
:ref:`artifact <JOBTMPL/SphinxDocumentation/Input/coverage_json_artifact>`.
:Description: Directory as JSON object where the code coverage JSON report will be extracted. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
.. _JOBTMPL/SphinxDocumentation/Input/unittest_xml_artifact:
unittest_xml_artifact
=====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the unittest XML report summary in XML format.
.. _JOBTMPL/SphinxDocumentation/Input/unittest_xml:
unittest_xml
============
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/unit",
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the unittest JUnit XML report will be extracted from
:ref:`artifact <JOBTMPL/SphinxDocumentation/Input/unittest_xml_artifact>`.
:Description: Directory as JSON object where the unittest JUnit XML report will be extracted. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
Documentation:
uses: pyTooling/Actions/.github/workflows/SphinxDocumentation.yml@r6
needs:
- ConfigParams
with:
...
unittest_xml: ${{ needs.ConfigParams.outputs.unittest_xml }}
.. _JOBTMPL/SphinxDocumentation/Input/html_artifact:
html_artifact
=============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the generated HTML website.
:Optimization:
.. hint::
If this parameter is empty, no HTML website will be generated and no artifact will be uploaded.
.. _JOBTMPL/SphinxDocumentation/Input/latex_artifact:
latex_artifact
==============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the generated LaTeX document and resource files (e.g., images).
:Optimization:
.. hint::
If this parameter is empty, no LaTeX document will be generated and no artifact will be uploaded.
.. _JOBTMPL/SphinxDocumentation/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/SphinxDocumentation/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/SphinxDocumentation/Optimizations:
Optimizations
*************
The following optimizations can be used to reduce the template's runtime.
Disable HTML website generation and HTML artifact
If parameter :ref:`JOBTMPL/SphinxDocumentation/Input/html_artifact` is empty, no HTML website will be generated and
uploaded.
Disable LaTeX document generation and LaTeX artifact
If parameter :ref:`JOBTMPL/SphinxDocumentation/Input/latex_artifact` is empty, no LaTeX document will be generated and
uploaded.

View File

@@ -0,0 +1,17 @@
.. _JOBTMPL/Documentation:
Documentation
#############
The category *documentation* provides workflow templates implementing
* :ref:`JOBTMPL/SphinxDocumentation` - Translate ReStructured Text to HTML and LaTeX using Sphinx.
* :ref:`JOBTMPL/LaTeXDocumentation` - Translate LaTeX to PDF using MikTeX.
* :ref:`JOBTMPL/PublishToGitHubPages` - Upload HTML content to GitHub Pages.
.. toctree::
:hidden:
SphinxDocumentation
LaTeXDocumentation
PublishToGitHubPages

View File

@@ -1,106 +0,0 @@
.. _JOBTMPL/Package:
Package
#######
This job packages the Python source code as a source package (``*.tar.gz``) and wheel package (``*.whl``) and uploads it
as an artifact.
**Behavior:**
1. Checkout repository
2. Setup Python and install dependencies
3. Package Python sources:
* If parameter ``requirements`` is empty, use ``build`` package and run ``python build``.
* If parameter ``requirements`` is ``no-isolation``, use ``build`` package in *no-isolation* mode and run
``python build``.
* If parameter ``requirements`` is non-empty, use ``setuptools`` package and run ``python setup.py``.
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`actions/upload-artifact`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r0
with:
artifact: Package
Complex Example
===============
.. code-block:: yaml
jobs:
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r0
needs:
- Params
- Coverage
with:
python_version: ${{ needs.Params.outputs.python_version }}
requirements: -r build/requirements.txt
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
Parameters
**********
python_version
==============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| python_version | optional | string | 3.11 |
+----------------+----------+----------+----------+
Python version.
requirements
============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| requirements | optional | string | ``""`` |
+----------------+----------+----------+----------+
Python dependencies to be installed through pip; if empty, use pyproject.toml through build.
artifact
========
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| artifact | yes | string | — — — — |
+----------------+----------+----------+----------+
Name of the package artifact.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,184 @@
.. _JOBTMPL/InstallPackage:
.. index::
single: pip; InstallPackage Template
single: GitHub Action Reusable Workflow; InstallPackage Template
InstallPackage (beta)
#####################
The ``InstallPackage`` job template takes a generated Python package and installs it on the target platform. Afterwards
the installation is verified. This aims for packaging and dependency mistakes in the package.
.. topic:: Features
* Install generated Python package on the target platform.
* Verify the installed package's version.
.. topic:: Behavior
* Download Python package as artifact.
* Prepare the Python environment.
* Install the Python package using :term:`pip`.
* Read out and verify the package version.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-InstallPackage.png
:width: 500px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`msys2/setup-msys2`
* :gh:`actions/setup-python`
* pip
* :pypi:`pip`
* :pypi:`wheel`
.. _JOBTMPL/InstallPackage/Instantiation:
Instantiation
*************
The following instantiation example creates a ``Install`` job derived from job template ``InstallPackage`` version
`@r6`. It installs the Python package on various platforms using a precomputed job-matrix created by job
``InstallParams``. This job uses the same ``Parameters`` job template as job ``UnitTestingParams``, which was used to
define parameters for the packaging job ``Package``.
.. code-block:: yaml
jobs:
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
InstallParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
python_version_list: ''
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r6
needs:
- UnitTestingParams
with:
artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
Install:
uses: pyTooling/Actions/.github/workflows/InstallPackage.yml@r6
needs:
- UnitTestingParams
- InstallParams
- Package
with:
jobs: ${{ needs.InstallParams.outputs.python_jobs }}
wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }}
package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }}
.. _JOBTMPL/InstallPackage/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/InstallPackage/Inputs>`
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=========================================================================+==========+==========+===================================================================================================================================+
| :ref:`JOBTMPL/InstallPackage/Input/jobs` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/InstallPackage/Input/wheel` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/InstallPackage/Input/package_name` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/InstallPackage/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/InstallPackage/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/InstallPackage/Inputs:
Input Parameters
****************
.. _JOBTMPL/InstallPackage/Input/jobs:
jobs
====
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: A JSON string with an array of dictionaries with the following key-value pairs:
:sysicon: icon to display
:system: name of the system
:runs-on: virtual machine image and base operating system
:runtime: name of the runtime environment if not running natively on the VM image
:shell: name of the shell
:pyicon: icon for CPython or pypy
:python: Python version
:envname: full name of the selected environment
:Description: A JSON encoded job matrix to run multiple Python job variations.
.. _JOBTMPL/InstallPackage/Input/wheel:
wheel
=====
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid artifact name.
:Description: The artifact containing the packaged Python code as wheel.
.. _JOBTMPL/InstallPackage/Input/package_name:
package_name
============
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid Python package, subpackage or module name.
:Description: The package or module containing the version information as a string in ``__version__``.
.. _JOBTMPL/InstallPackage/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/InstallPackage/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/InstallPackage/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,175 @@
.. _JOBTMPL/Package:
.. index::
single: build; Package Template
single: GitHub Action Reusable Workflow; Package Template
Package
#######
This job packages the Python source code as a source package (``*.tar.gz``) and wheel package (``*.whl``) and uploads it
as an artifact.
.. topic:: Features
* Package source code as wheel and source distribution.
* Support packaging using :pypi:`build` (recommended) or :pypi:`setuptools`.
.. topic:: Behavior
1. Checkout repository.
2. Setup Python and install dependencies.
3. Package Python sources:
* If parameter :ref:`JOBTMPL/Package/Input/requirements` is empty, use :pypi:`build` for packaging and execute
``python -m build ...``.
* If parameter :ref:`JOBTMPL/Package/Input/requirements` is ``no-isolation``, use :pypi:`build` for packaging in
*no-isolation* mode executing ``python -m build --no-isolation ...``.
* If parameter :ref:`JOBTMPL/Package/Input/requirements` is non-empty, use :pypi:`setuptools` for package and
execute ``python setup.py ...``.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-Package.png
:width: 500px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* pip
* :pypi:`build`
* :pypi:`wheel`
.. _JOBTMPL/Package/Instantiation:
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r6
with:
artifact: Package
Complex Example
===============
.. code-block:: yaml
jobs:
Package:
uses: pyTooling/Actions/.github/workflows/Package.yml@r6
needs:
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
requirements: -r build/requirements.txt
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
.. _JOBTMPL/Package/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/Package/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/Package/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Package/Input/python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Package/Input/requirements` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Package/Input/artifact` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/Package/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/Package/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/Package/Inputs:
Input Parameters
****************
.. _JOBTMPL/Package/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/Package/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/Package/Input/requirements:
requirements
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'build wheel'``.
:Behavior: If the value is an empty string, :pypi:`build` is used for packaging. |br|
if the value is ``no-isolation``, :pypi:`build` is used in *no-isolation* mode for packaging. |br|
otherwise, a list of requirements is assumed and :pypi:`setuptools` is used for packaging.
:Description: Python dependencies to be installed through *pip*.
.. _JOBTMPL/Package/Input/artifact:
artifact
========
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the packaged Python code.
.. _JOBTMPL/Package/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/Package/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/Package/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,210 @@
.. _JOBTMPL/PublishOnPyPI:
.. index::
single: PyPI; PublishOnPyPI Template
single: twine; PublishOnPyPI Template
single: GitHub Action Reusable Workflow; PublishOnPyPI Template
PublishOnPyPI
#############
Publish a wheel (``*.whl``) packages and/or source (``*.tar.gz``) package to :term:`PyPI`.
.. topic:: Features
* Publish a Python package to :term:`PyPI`.
.. topic:: Behavior
1. Download package artifact
2. Publish source package(s) (``*.tar.gz``)
3. Publish wheel package(s) (``*.whl``)
4. Delete the artifact
.. topic:: Preconditions
1. A PyPI account was created and the package name is either not occupied or the user has access rights for that
package.
2. An access token was generated at PyPI, which can be used for uploading packages.
3. A secret (e.g. ``PYPI_TOKEN``) was setup in GitHub Actions to handover the PyPI token to the pipeline.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-PublishOnPyPI.png
:width: 500px
.. topic:: Dependencies
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`actions/setup-python`
* :gh:`geekyeggo/delete-artifact`
* pip
* :pypi:`wheel`
* :pypi:`twine`
.. _JOBTMPL/PublishOnPyPI/Instantiation:
Instantiation
*************
Simple Example
==============
The following example demonstrates how to publish the artifact named ``Package`` to PyPI on every pipeline run triggered
by a Git tag. A secret is forwarded from GitHub secrets to a job secret.
.. code-block:: yaml
jobs:
# ...
PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r6
if: startsWith(github.ref, 'refs/tags')
with:
artifact: Package
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
Complex Example
===============
In this more complex example, the job depends on a parameter creation (``Params``) and packaging job (``Package``). The
used Python version is overwritten by a parameter calculated in the ``Params`` jobs. Also the artifact name is managed
by that job. Finally, the list of requirements is overwritten to load a list of requirements from ``dist/requirements.txt``.
.. code-block:: yaml
jobs:
Params:
# ...
Package:
# ...
PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r6
if: startsWith(github.ref, 'refs/tags')
needs:
- Params
- Package
with:
python_version: ${{ needs.Params.outputs.python_version }}
requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
.. seealso::
:ref:`JOBTMPL/Package`
.. _JOBTMPL/PublishOnPyPI/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PublishOnPyPI/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/PublishOnPyPI/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishOnPyPI/Input/python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishOnPyPI/Input/requirements` | no | string | ``'wheel twine'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishOnPyPI/Input/artifact` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PublishOnPyPI/Secrets>`
+-----------------------------------------------------------+----------+----------+--------------+
| Token Name | Required | Type | Default |
+===========================================================+==========+==========+==============+
| :ref:`JOBTMPL/PublishOnPyPI/Secret/PYPI_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PublishOnPyPI/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/PublishOnPyPI/Inputs:
Input Parameters
****************
.. _JOBTMPL/PublishOnPyPI/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/PublishOnPyPI/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/PublishOnPyPI/Input/requirements:
requirements
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'wheel twine'``.
:Description: Python dependencies to be installed through *pip*.
.. _JOBTMPL/PublishOnPyPI/Input/artifact:
artifact
========
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the packaged Python package(s).
.. _JOBTMPL/PublishOnPyPI/Secrets:
Secrets
*******
.. _JOBTMPL/PublishOnPyPI/Secret/PYPI_TOKEN:
PYPI_TOKEN
==========
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish and upload packages on :term:`PyPI`.
.. _JOBTMPL/PublishOnPyPI/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/PublishOnPyPI/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,17 @@
.. _JOBTMPL/Packaging:
Packaging
#########
The category *packaging* provides workflow templates implementing
* :ref:`JOBTMPL/Package` - Package Python packages and modules as wheel.
* :ref:`JOBTMPL/InstallPackage` - Install Python package (wheel) on target platforms.
* :ref:`JOBTMPL/PublishOnPyPI` - Upload Python package (wheel) to PyPI.
.. toctree::
:hidden:
Package
InstallPackage
PublishOnPyPI

View File

@@ -1,413 +0,0 @@
.. _JOBTMPL/Parameters:
Parameters
##########
The ``Parameters`` job template is a workaround for the limitations of GitHub Actions to handle global variables in
GitHub Actions workflows (see `actions/runner#480 <https://github.com/actions/runner/issues/480>`__.
It generates output parameters with artifact names and a job matrix to be used in later running jobs.
**Behavior:**
.. todo:: Parameters:Behavior Needs documentation.
**Dependencies:**
*None*
Instantiation
*************
Simple Example
==============
The following instantiation example creates a job `Params` derived from job template `Parameters` version `r0`. It only
requires a `name` parameter to create the artifact names.
.. code-block:: yaml
name: Pipeline
on:
push:
workflow_dispatch:
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
Complex Example
===============
The following instantiation example creates 3 jobs from the same template, but with differing input parameters. The
first job `UnitTestingParams` might be used to create a job matrix of unit tests. It creates the cross of default
systems (Windows, Ubuntu, macOS, MinGW64, UCRT64) and the given list of Python versions including some mypy versions. In
addition a list of excludes (marked as :deletion:`deletions`) and includes (marked as :addition:`additions`) is handed
over resulting in the following combinations:
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| Version | 3.8 🔴 | 3.9 🟠 | 3.10 🟡 | 3.11 🟢 | 3.12 🟢 | 3.13.a1 🟣 | pypy-3.8 🔴 | pypy-3.9 🟠 | pypy-3.10 🟡 |
+============+=============+=============+==============+==============+=========================+============+=============+==============================+===============================+
| Windows 🧊 | windows:3.8 | windows:3.9 | windows:3.10 | windows:3.11 | | | | :deletion:`windows:pypy-3.9` | :deletion:`windows:pypy-3.10` |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| Ubuntu 🐧 | ubuntu:3.8 | ubuntu:3.9 | ubuntu:3.10 | ubuntu:3.11 | :addition:`ubuntu:3.12` | | | ubuntu:pypy-3.9 | ubuntu:pypy-3.10 |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| macOS 🍎 | macos:3.8 | macos:3.9 | macos:3.10 | macos:3.11 | :addition:`macos:3.12` | | | macos:pypy-3.9 | macos:pypy-3.10 |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| MSYS 🟪 | | | | | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| MinGW32 ⬛ | | | | | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| MinGW64 🟦 | | | | mingw64:3.11 | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| Clang32 🟫 | | | | | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| Clang64 🟧 | | | | | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
| UCRT64 🟨 | | | | | | | | | |
+------------+-------------+-------------+--------------+--------------+-------------------------+------------+-------------+------------------------------+-------------------------------+
.. code-block:: yaml
name: Pipeline
on:
push:
workflow_dispatch:
jobs:
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
python_version_list: "3.8 3.9 3.10 3.11 pypy-3.9 pypy-3.10"
include_list: "ubuntu:3.12 macos:3.12"
exclude_list: "windows:pypy-3.9 windows:pypy-3.10"
PerformanceTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
python_version_list: "3.11 3.12"
system_list: "ubuntu windows macos"
PlatformTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev
with:
name: pyTooling
python_version_list: "3.12"
system_list: "ubuntu windows macos mingw32 mingw64 clang64 ucrt64"
Parameters
**********
name
====
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| name | yes | string | — — — — |
+----------------+----------+----------+--------------+
The name of the library or package.
It's used to create artifact names.
python_version
==============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| python_version | optional | string | ``3.12`` |
+----------------+----------+----------+----------+
Python version to be used for all jobs requiring a single Python version.
python_version_list
===================
+----------------------+----------+----------+----------------------------+
| Parameter Name | Required | Type | Default |
+======================+==========+==========+============================+
| python_version_list | optional | string | ``3.8 3.9 3.10 3.11 3.12`` |
+----------------------+----------+----------+----------------------------+
Space separated list of CPython versions and/or mypy version to run tests with.
**Possible values:**
* ``3.7``, ``3.8``, ``3.9``, ``3.10`` , ``3.11``, ``3.12``, ``3.13``
* ``pypy-3.7``, ``pypy-3.8``, ``pypy-3.9``, ``pypy-3.10``
+------+-----------+------------------+-----------------------------------------+
| Icon | Version | Maintained until | Comments |
+======+===========+==================+=========================================+
| ⚫ | 3.7 | 2023.06.27 | :red:`outdated` |
+------+-----------+------------------+-----------------------------------------+
| 🔴 | 3.8 | 2024.10 | |
+------+-----------+------------------+-----------------------------------------+
| 🟠 | 3.9 | 2025.10 | |
+------+-----------+------------------+-----------------------------------------+
| 🟡 | 3.10 | 2026.10 | |
+------+-----------+------------------+-----------------------------------------+
| 🟢 | 3.11 | 2027.10 | |
+------+-----------+------------------+-----------------------------------------+
| 🟢 | 3.12 | 2028.10 | :green:`latest` |
+------+-----------+------------------+-----------------------------------------+
| 🟣 | 3.13 | 2029.10 | Python 3.13 alpha (or RC) will be used. |
+------+-----------+------------------+-----------------------------------------+
| ⟲⚫ | pypy-3.7 | ????.?? | |
+------+-----------+------------------+-----------------------------------------+
| ⟲🔴 | pypy-3.8 | ????.?? | |
+------+-----------+------------------+-----------------------------------------+
| ⟲🟠 | pypy-3.9 | ????.?? | |
+------+-----------+------------------+-----------------------------------------+
| ⟲🟡 | pypy-3.10 | ????.?? | |
+------+-----------+------------------+-----------------------------------------+
system_list
===========
+----------------+----------+----------+-----------------------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=========================================+
| system_list | optional | string | ``ubuntu windows macos mingw64 ucrt64`` |
+----------------+----------+----------+-----------------------------------------+
Space separated list of systems to run tests on.
**Possible values:**
* Native systems: ``ubuntu``, ``windows``, ``macos``
* MSYS2: ``msys``, ``mingw32``, ``mingw64``, ``clang32``, ``clang64``, ``ucrt64``
+------+-----------+------------------------------+-----------------------------------------------------------------+
| Icon | System | Used version | Comments |
+======+===========+==============================+=================================================================+
| 🧊 | Windows | Windows Server 2022 (latest) | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🐧 | Ubuntu | Ubuntu 22.04 (LTS) (latest) | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🍎 | macOS | macOS Monterey 12 (latest) | While this marked latest, macOS Ventura 13 is already provided. |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟪 | MSYS | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| ⬛ | MinGW32 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟦 | MinGW64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟫 | Clang32 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟧 | Clang64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟨 | UCRT64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
Source: `Images provided by GitHub <https://github.com/actions/runner-images>`__
include_list
============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| include_list | optional | string | ``""`` |
+----------------+----------+----------+----------+
Space separated list of ``system:python`` items to be included into the list of test.
**Example:**
.. code-block:: yaml
include_list: "ubuntu:3.11 macos:3.11"
exclude_list
============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| exclude_list | optional | string | ``""`` |
+----------------+----------+----------+----------+
Space separated list of ``system:python`` items to be excluded from the list of test.
**Example:**
.. code-block:: yaml
exclude_list: "windows:pypy-3.8 windows:pypy-3.9"
disable_list
============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| disable_list | optional | string | ``""`` |
+----------------+----------+----------+----------+
Space separated list of ``system:python`` items to be temporarily disabled from the list of test.
Each disabled item creates a warning in the workflow log:
.. image:: /_static/GH_Workflow_DisabledJobsWarnings.png
:scale: 80 %
**Example:**
.. code-block:: yaml
disable_list: "windows:3.10 windows:3.11"
Secrets
*******
This job template needs no secrets.
Results
*******
python_version
==============
A single string parameter representing the default Python version that should be used across multiple jobs in the same
pipeline.
Such a parameter is needed as a workaround, because GitHub Actions doesn't support proper handling of global pipeline
variables. Thus, this job is used to compute an output parameter that can be reused in other jobs.
**Usage Example:**
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
CodeCoverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0
needs:
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
python_jobs
===========
A list of dictionaries containing a job description.
A job description contains the following key-value pairs:
* ``sysicon`` - icon to display
* ``system`` - name of the system
* ``runs-on`` - virtual machine image and base operating system
* ``runtime`` - name of the runtime environment if not running natively on the VM image
* ``shell`` - name of the shell
* ``pyicon`` - icon for CPython or pypy
* ``python`` - Python version
* ``envname`` - full name of the selected environment
**Usage Example:**
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev
needs:
- Params
with:
jobs: ${{ needs.Params.outputs.python_jobs }}
This list can be unpacked with ``fromJson(...)`` in a job ``strategy:matrix:include``:
.. code-block:: yaml
UnitTesting:
name: ${{ matrix.sysicon }} ${{ matrix.pyicon }} Unit Tests using Python ${{ matrix.python }}
runs-on: ${{ matrix.runs-on }}
strategy:
matrix:
include: ${{ fromJson(inputs.jobs) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: 🐍 Setup Python ${{ matrix.python }}
if: matrix.system != 'msys2'
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
artifact_names
==============
A dictionary of artifact names sharing a common prefix.
The supported artifacts are:
* ``unittesting_xml`` - UnitTesting XML summary report
* ``unittesting_html`` - UnitTesting HTML summary report
* ``codecoverage_sqlite`` - Code Coverage internal database (SQLite)
* ``codecoverage_json`` - Code Coverage JSON report
* ``codecoverage_xml`` - Code Coverage XML report
* ``codecoverage_html`` - Code Coverage HTML report
* ``statictyping_html`` - Static Type Checking HTML report
* ``package_all`` - Packaged Python project (multiple formats)
* ``documentation_pdf`` - Documentation in PDF format
* ``documentation_html`` - Documentation in HTML format
**Usage Example:**
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0
with:
name: pyTooling
Coverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@dev
needs:
- Params
with:
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
Params
======
.. attention:: ``Params`` is deprecated.
* ``params['unittesting']`` |rarr| ``artifact_names['unittesting_xml']``
* ``params['coverage']`` |rarr| ``artifact_names['codecoverage_xml']``
* ``params['typing']`` |rarr| ``artifact_names['statictyping_html']``
* ``params['package']`` |rarr| ``artifact_names['package_all']``
* ``params['doc']`` |rarr| ``artifact_names['documentation_html']``

View File

@@ -0,0 +1,462 @@
.. _JOBTMPL/PublishCoverageResults:
.. index::
single: CodeCov; PublishCoverageResults Template
single: Codacy; PublishCoverageResults Template
single: Coverage.py; PublishCoverageResults Template
single: GitHub Action Reusable Workflow; PublishCoverageResults Template
PublishCoverageResults
######################
The ``PublishCoverageResults`` job template downloads artifacts provided by :ref:`JOBTMPL/UnitTesting` containing code
coverage databases created by :term:`Coverage.py`. These databases are then merged and converted into various output
formats like Cobertura XML, JSON or HTML. These outputs are either uploaded as a new artifact or can be published to
cloud services like :term:`CodeCov` or :term:`Codacy`.
.. topic:: Features
* Merge SQLite code coverage databases generated by Coverage.py into a single
SQLite database.
* Convert SQLite database to Cobertura XML format.
* Convert SQLite database to Coverage.py JSON format.
* Convert SQLite database to HTML code coverage report.
* Publish code coverage at CodeCov.
* Publish code coverage as Codacy.
.. topic:: Behavior
1. Checkout repository.
2. Download artifact matching the :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_artifacts_pattern`.
3. Install Python dependencies especially :pypi:`coverage`.
4. Rename SQLite database files within artifact download directory to match the required filename pattern for
Coverage.py's merge operation.
5. Report code coverage as table into job log.
6. Convert code coverage to Cobertura XML format.
7. Convert code coverage to JSON format.
8. Convert code coverage to HTML report (website).
9. Upload merged SQLite database as artifact.
10. Upload Cobertura XML file as artifact.
11. Upload JSON file as artifact.
12. Upload HTML report as artifact.
13. Publish Cobertura report to CodeCov.
14. Publish Cobertura report to Codacy.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-PublishCoverageResults.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* pip
* :pypi:`coverage`
* :pypi:`tomli`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* :gh:`codecov/codecov-action`
* :gh:`codacy/codacy-coverage-reporter-action`
.. _JOBTMPL/PublishCoverageResults/Instantiation:
Instantiation
*************
The following
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
with:
package_name: myPackage
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
- UnitTestingParams
with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r6
needs:
- ConfigParams
- UnitTestingParams
- UnitTesting
with:
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }}
coverage_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_html }}
codecov: 'true'
codacy: 'true'
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }}
.. _JOBTMPL/PublishCoverageResults/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PublishCoverageResults/Inputs>`
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=============================================================================+==========+================+==========================================================================================================================+
| :ref:`JOBTMPL/PublishCoverageResults/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_artifacts_pattern` | no | string | ``'*-CodeCoverage-SQLite-*'`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_config` | no | string | ``'pyproject.toml'`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_report_xml` | no | string (JSON) | :jsoncode:`{"directory": "report/coverage", "filename": "coverage.xml", "fullpath": "report/coverage/coverage.xml"}` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_report_json` | no | string (JSON) | :jsoncode:`{"directory": "report/coverage", "filename": "coverage.json", "fullpath": "report/coverage/coverage.json"}` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_report_html` | no | string (JSON) | :jsoncode:`{"directory": "report/coverage/html"}` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_sqlite_artifact` | no | string | ``''`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_xml_artifact` | no | string | ``''`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_json_artifact` | no | string | ``''`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_html_artifact` | no | string | ``''`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/codecov` | no | string | ``'false'`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishCoverageResults/Input/codacy` | no | string | ``'false'`` |
+-----------------------------------------------------------------------------+----------+----------------+--------------------------------------------------------------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PublishCoverageResults/Secrets>`
+-----------------------------------------------------------+----------+----------+--------------+
| Token Name | Required | Type | Default |
+===========================================================+==========+==========+==============+
| :ref:`JOBTMPL/PublishCoverageResults/Secret/CODECOV_TOKEN`| no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
| :ref:`JOBTMPL/PublishCoverageResults/Secret/CODACY_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PublishCoverageResults/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/PublishCoverageResults/Inputs:
Input Parameters
****************
.. _JOBTMPL/PublishCoverageResults/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/PublishCoverageResults/Input/coverage_artifacts_pattern:
coverage_artifacts_pattern
==========================
:Type: string
:Required: no
:Default Value: ``'*-CodeCoverage-SQLite-*'``
:Possible Values: Any valid artifact matching pattern using fixed text and ``*`` characters.
:Description: tbd
.. _JOBTMPL/PublishCoverageResults/Input/coverage_config:
coverage_config
===============
:Type: string
:Required: no
:Default Value: ``'pyproject.toml'``
:Possible Values: Any valid path to a :file:`pyproject.toml` file. |br|
Alternatively, path to a :file:`.coveragerc` file (deprecated).
:Description: Path to a Python project configuration file for extraction of project settings.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Extracted values from :file:`pyproject.toml`
.. code-block:: toml
[tool.pytest]
junit_xml = "report/unit/UnittestReportSummary.xml"
[tool.pyedaa-reports]
junit_xml = "report/unit/unittest.xml"
[tool.coverage.xml]
output = "report/coverage/coverage.xml"
[tool.coverage.json]
output = "report/coverage/coverage.json"
[tool.coverage.html]
directory = "report/coverage/html"
title="Code Coverage of pyDummy"
.. _JOBTMPL/PublishCoverageResults/Input/coverage_report_xml:
coverage_report_xml
===================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.xml",
"fullpath": "reports/coverage/coverage.xml"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in Cobertura XML format will be
saved.
:filename: Filename of the generated Cobertura XML report. |br|
Any valid XML filename.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the code coverage report in Cobertura XML format
will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
.. _JOBTMPL/PublishCoverageResults/Input/coverage_report_json:
coverage_report_json
====================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.json",
"fullpath": "reports/coverage/coverage.json"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in Coverage.py's JSON format
will be saved.
:filename: Filename of the generated Coverage.py JSON report. |br|
Any valid JSON filename.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the code coverage report in Coverage.py's JSON
format will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
.. _JOBTMPL/PublishCoverageResults/Input/coverage_report_html:
coverage_report_html
====================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage/html"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in HTML format will be saved.
:Description: Directory as JSON object where the code coverage report in HTML format will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
PublishCoverageResults:
uses: pyTooling/Actions/.github/workflows/PublishCoverageResults.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
.. _JOBTMPL/PublishCoverageResults/Input/coverage_sqlite_artifact:
coverage_sqlite_artifact
========================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the merged SQLite code coverage database.
.. _JOBTMPL/PublishCoverageResults/Input/coverage_xml_artifact:
coverage_xml_artifact
=====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the merged code coverage report as Cobertura XML file.
.. _JOBTMPL/PublishCoverageResults/Input/coverage_json_artifact:
coverage_json_artifact
======================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the merged code coverage report as Coverage.py JSON file.
.. _JOBTMPL/PublishCoverageResults/Input/coverage_html_artifact:
coverage_html_artifact
======================
:Type: string
:Required: no
:Default Value: ``'report/coverage/html'``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the merged code coverage report as HTML report.
.. _JOBTMPL/PublishCoverageResults/Input/codecov:
codecov
=======
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *true*, publish code coverage results to CodeCov.
.. _JOBTMPL/PublishCoverageResults/Input/codacy:
codacy
======
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: If *true*, publish code coverage results to Codacy.
.. _JOBTMPL/PublishCoverageResults/Secrets:
Secrets
*******
The workflow template uses the following secrets to publish results to other services.
.. _JOBTMPL/PublishCoverageResults/Secret/CODECOV_TOKEN:
CODECOV_TOKEN
=============
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish code coverage and unit test results to :term:`CodeCov`.
.. _JOBTMPL/PublishCoverageResults/Secret/CODACY_TOKEN:
CODACY_TOKEN
============
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish code coverage results to :term:`Codacy`.
.. _JOBTMPL/PublishCoverageResults/Outputs:
Outputs
*******
The following optimizations can be used to reduce the template's runtime.
Disable code coverage SQLite database artifact upload
If parameter :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_sqlite_artifact` is empty, the collected code coverage database
(SQLlite format) wont be uploaded as an artifact.
Disable code coverage report conversion to the Cobertura XML format.
If parameter :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_xml_artifact` is empty, no Cobertura XML file will be generated
from code coverage report. As no Cobertura XML file exists, no code coverage XML artifact will be uploaded.
Disable code coverage report conversion to the *Coverage.py* JSON format.
If parameter :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_json_artifact` is empty, no *Coverage.py* JSON file will be
generated from code coverage report. As no JSON file exists, no code coverage JSON artifact will be uploaded.
Disable code coverage report conversion to an HTML website.
If parameter :ref:`JOBTMPL/PublishCoverageResults/Input/coverage_html_artifact` is empty, no coverage report HTML report will be
generated from code coverage report. As no HTML report exists, no code coverage HTML artifact will be uploaded.
.. todo:: PublishCoverageResults:: Disable publishing to codecov and codacy. |br| If upload is enabled, XML will be always generated.

View File

@@ -0,0 +1,344 @@
.. _JOBTMPL/PublishTestResults:
.. index::
single: CodeCov; PublishTestResults Template
single: pyEDAA.Reports; PublishTestResults Template
single: Test Reporter; PublishTestResults Template
single: GitHub Action Reusable Workflow; PublishTestResults Template
PublishTestResults
##################
This job downloads all matching JUnit report artifacts, merged the reports into one single report and then publishes the
JUnit test summary reports to multiple services for visualization and longterm statistical tracking.
Supported services are:
* GitHub Actions job results using :term:`Test Reporter`
* :term:`Codecov`
.. topic:: Features
* Merge multiple JUnit XML reports generated by pytest into a single JUnit XML report.
* Publish unit test results to currently running pipeline as job result.
* Publish unit test results to CodeCov.
.. topic:: Behavior
1. Checkout repository
2. Download multiple artifacts containing test report summaries in JUnit XML format conforming to an artifact name
pattern (see :ref:`JOBTMPL/PublishTestResults/Input/unittest_artifacts_pattern`) for limiting the number of
downloaded artifacts and the hereby generated traffic.
3. Rename the found JUnit XML files.
4. Merge all found JUnit XML files using :term:`pyEDAA.Reports` into a new JUnit XML file. |br|
Optionally, apply certain transformation and cleanup operations to the JUnit report structure.
5. Publish test results as a markdown report page to GitHub Actions using :term:`Test Reporter`.
6. Publish test results to :term:`Codecov` using :gh:`codecov/test-results-action`.
.. topic:: Job Execution
.. grid:: 2
.. grid-item::
:columns: 4
.. rubric:: Job Steps
.. image:: ../../_static/pyTooling-Actions-PublishTestResults.png
:width: 500px
.. grid-item::
:columns: 4
.. rubric:: Generated Dorny Report below Pipeline Graph
.. image:: ../../_static/pyTooling-Actions-PublishTestResults-Dorny.png
:width: 500px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* pip
* :pypi:`pyEDAA.Reports`
* :gh:`dorny/test-reporter`
* :gh:`codecov/test-results-action`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
.. _JOBTMPL/PublishTestResults/Instantiation:
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r6
Complex Example
===============
.. code-block:: yaml
jobs:
CodeCoverage:
# ...
UnitTesting:
# ...
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r6
needs:
- CodeCoverage
- UnitTesting
.. _JOBTMPL/PublishTestResults/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PublishTestResults/Inputs>`
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+=====================================================================+
| :ref:`JOBTMPL/PublishTestResults/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/unittest_artifacts_pattern` | no | string | ``'*-UnitTestReportSummary-XML-*'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/merged_junit_filename` | no | string | ``'Unittesting.xml'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/merged_junit_artifact` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/merge-input-dialect` | no | string | ``'pyTest-JUnit'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/merge-output-dialect` | no | string | ``'pyTest-JUnit'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/additional_merge_args` | no | string | ``'"--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit"'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/testsuite-summary-name` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/publish` | no | string | ``'true'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/report_title` | no | string | ``'Unit Test Results'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/dorny` | no | string | ``'true'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/codecov` | no | string | ``'false'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishTestResults/Input/codecov_flags` | no | string | ``'unittest'`` |
+---------------------------------------------------------------------+----------+----------+---------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PublishTestResults/Secrets>`
+-----------------------------------------------------------+----------+----------+--------------+
| Token Name | Required | Type | Default |
+===========================================================+==========+==========+==============+
| :ref:`JOBTMPL/PublishTestResults/Secret/CODECOV_TOKEN` | no | string | — — — — |
+-----------------------------------------------------------+----------+----------+--------------+
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PublishTestResults/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/PublishTestResults/Inputs:
Input Parameters
****************
.. _JOBTMPL/PublishTestResults/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/PublishTestResults/Input/unittest_artifacts_pattern:
unittest_artifacts_pattern
==========================
:Type: string
:Required: no
:Default Value: ``'*-UnitTestReportSummary-XML-*'``
:Possible Values: Any valid artifact matching pattern using fixed text and ``*`` characters.
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/merged_junit_filename:
merged_junit_filename
=====================
:Type: string
:Required: no
:Default Value: ``'Unittesting.xml'``
:Possible Values: Any valid filename suitable for a JUnit XML report.
:Description: The filename for the merged JUnit report in XML format. |br|
See :ref:`JOBTMPL/PublishTestResults/Input/merge-output-dialect` for the used JUnit dialect in the
merged report file.
.. _JOBTMPL/PublishTestResults/Input/merged_junit_artifact:
merged_junit_artifact
=====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description:
.. _JOBTMPL/PublishTestResults/Input/merge-input-dialect:
merge-input-dialect
===================
:Type: string
:Required: no
:Default Value: ``'pyTest-JUnit'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/merge-output-dialect:
merge-output-dialect
====================
:Type: string
:Required: no
:Default Value: ``'pyTest-JUnit'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/additional_merge_args:
additional_merge_args
=====================
:Type: string
:Required: no
:Default Value: ``'"--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit"'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/testsuite-summary-name:
testsuite-summary-name
======================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/publish:
publish
=======
:Type: string
:Required: no
:Default Value: ``'true'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/report_title:
report_title
============
:Type: string
:Required: no
:Default Value: ``'Unit Test Results'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/dorny:
dorny
=====
:Type: string
:Required: no
:Default Value: ``'true'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/codecov:
codecov
=======
:Type: string
:Required: no
:Default Value: ``'false'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Input/codecov_flags:
codecov_flags
=============
:Type: string
:Required: no
:Default Value: ``'unittest'``
:Possible Values: tbd
:Description: tbd
.. _JOBTMPL/PublishTestResults/Secrets:
Secrets
*******
.. _JOBTMPL/PublishTestResults/Secret/CODECOV_TOKEN:
CODECOV_TOKEN
=============
:Type: string
:Required: no
:Default Value: — — — —
:Description: The token to publish unit test results on :term:`CodeCov`.
.. _JOBTMPL/PublishTestResults/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/PublishTestResults/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,15 @@
.. _JOBTMPL/Publish:
Publish
#######
The category *publish* provides workflow templates implementing
* :ref:`JOBTMPL/PublishTestResults`- Merge and publish unit test results.
* :ref:`JOBTMPL/PublishCoverageResults` - Merge and publish code coverage results.
.. toctree::
:hidden:
PublishTestResults
PublishCoverageResults

View File

@@ -1,139 +0,0 @@
.. _JOBTMPL/PyPI:
PublishOnPyPI
#############
Publish a source (``*.tar.gz``) package and/or wheel (``*.whl``) packages to `PyPI <https://pypi.org/>`__.
**Behavior:**
1. Download package artifact
2. Publish source package(s) (``*.tar.gz``)
3. Publish wheel package(s) (``*.whl``)
4. Delete the artifact
**Preconditions:**
A PyPI account was created and the package name is either not occupied or the user has access rights for that package.
**Requirements:**
Setup a secret (e.g. ``PYPI_TOKEN``) in GitHub to handover the PyPI token to the job.
**Dependencies:**
* :gh:`actions/download-artifact`
* :gh:`actions/setup-python`
* :gh:`geekyeggo/delete-artifact`
Instantiation
*************
Simple Example
==============
The following example demonstrates how to publish the artifact named ``Package`` to PyPI on every pipeline run triggered
by a Git tag. A secret is forwarded from GitHub secrets to a job secret.
.. code-block:: yaml
jobs:
# ...
PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0
if: startsWith(github.ref, 'refs/tags')
with:
artifact: Package
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
Complex Example
===============
In this more complex example, the job depends on a parameter creation (``Params``) and packaging job (``Package``). The
used Python version is overwritten by a parameter calculated in the ``Params`` jobs. Also the artifact name is managed
by that job. Finally, the list of requirements is overwritten to load a list of requirements from ``dist/requirements.txt``.
.. code-block:: yaml
jobs:
Params:
# ...
Package:
# ...
PublishOnPyPI:
uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0
if: startsWith(github.ref, 'refs/tags')
needs:
- Params
- Package
with:
python_version: ${{ needs.Params.outputs.python_version }}
requirements: -r dist/requirements.txt
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }}
secrets:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
Parameters
**********
python_version
==============
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| python_version | optional | string | ``3.11`` |
+----------------+----------+----------+----------+
Python version used for uploading the package contents via `twine` to PyPI.
requirements
============
+----------------+----------+----------+-----------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================+
| requirements | optional | string | ``wheel twine`` |
+----------------+----------+----------+-----------------+
List of requirements to be installed for uploading the package contents to PyPI.
artifact
========
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| artifact | yes | string | — — — — |
+----------------+----------+----------+--------------+
Name of the artifact containing the package(s).
Secrets
*******
PYPI_TOKEN
==========
+----------------+----------+----------+--------------+
| Secret Name | Required | Type | Default |
+================+==========+==========+==============+
| PYPI_TOKEN | yes | string | — — — — |
+----------------+----------+----------+--------------+
The token to access the package at PyPI for uploading new data.
Results
*******
This job template has no output parameters.

View File

@@ -1,88 +0,0 @@
.. _JOBTMPL/PublishTestResults:
PublishTestResults
##################
This job downloads all artifacts and uploads jUnit XML reports as a Markdown page to GitHub Actions to visualize the
results a an item in the job list. For publishing, :gh:`dorny/test-reporter` is used.
**Behavior:**
1. Checkout repository
2. Download (all) artifacts
3. Publish test results as a markdown report page to GitHub Actions.
.. note::
The :gh:`actions/download-artifact` does not support wildcards to specify a subset of artifacts for downloading.
Thus, all artifacts need to be downloaded.
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`actions/download-artifact`
* :gh:`dorny/test-reporter`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0
Complex Example
===============
.. code-block:: yaml
jobs:
CodeCoverage:
# ...
UnitTesting:
# ...
PublishTestResults:
uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0
needs:
- CodeCoverage
- UnitTesting
Parameters
**********
report_files
============
+----------------+----------+----------+---------------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================================+
| report_files | optional | string | ``artifacts/**/*.xml`` |
+----------------+----------+----------+---------------------------------+
Pattern of jUnit report files to publish as Markdown.
The parameter can be a comma separated list. Wildcards are supported.
.. hint::
All artifacts are downloaded into directory ``artifacts``, thus the pattern should include this directory as a
prefix.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -1,106 +0,0 @@
.. _JOBTMPL/PublishToGitHubPages:
PublishToGitHubPages
####################
This job publishes HTML content from artifacts of other jobs to GitHub Pages.
**Behavior:**
1. Checkout repository.
2. Download artifacts.
3. Push HTML files to branch ``gh-pages``.
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`actions/download-artifact`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
BuildTheDocs:
# ...
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0
needs:
- BuildTheDocs
with:
doc: Documentation
Complex Example
===============
.. code-block:: yaml
jobs:
PublishToGitHubPages:
uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0
needs:
- Params
- BuildTheDocs
- Coverage
- StaticTypeCheck
with:
doc: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }}
coverage: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }}
typing: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
Parameters
**********
doc
===
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| doc | yes | string | — — — — |
+----------------+----------+----------+--------------+
Name of the documentation artifact.
coverage
========
+----------------+----------+----------+-----------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================+
| coverage | optional | string | ``""`` |
+----------------+----------+----------+-----------------+
Name of the coverage artifact.
typing
======
+----------------+----------+----------+-----------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================+
| typing | optional | string | ``""`` |
+----------------+----------+----------+-----------------+
Name of the typing artifact.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,32 @@
.. rubric:: Possible values
* ``3.8``, ``3.9``, ``3.10`` , ``3.11``, ``3.12``, ``3.13``, ``3.14``
* ``pypy-3.7``, ``pypy-3.8``, ``pypy-3.9``, ``pypy-3.10``, ``pypy-3.11``
+------+-----------+------------------+-----------------------------------------------+
| Icon | Version | Maintained until | Comments |
+======+===========+==================+===============================================+
| ⚫ | 3.8 | 2024.10 | :red:`outdated` |
+------+-----------+------------------+-----------------------------------------------+
| 🔴 | 3.9 | 2025.10 | |
+------+-----------+------------------+-----------------------------------------------+
| 🟠 | 3.10 | 2026.10 | |
+------+-----------+------------------+-----------------------------------------------+
| 🟡 | 3.11 | 2027.10 | |
+------+-----------+------------------+-----------------------------------------------+
| 🟢 | 3.12 | 2028.10 | |
+------+-----------+------------------+-----------------------------------------------+
| 🟢 | 3.13 | 2029.10 | :green:`latest CPython` |
+------+-----------+------------------+-----------------------------------------------+
| 🟣 | 3.14 | 2030.10 | Python 3.14 alpha, beta (or RC) will be used. |
+------+-----------+------------------+-----------------------------------------------+
| ⟲⚫ | pypy-3.7 | ????.?? | |
+------+-----------+------------------+-----------------------------------------------+
| ⟲⚫ | pypy-3.8 | ????.?? | |
+------+-----------+------------------+-----------------------------------------------+
| ⟲🔴 | pypy-3.9 | ????.?? | |
+------+-----------+------------------+-----------------------------------------------+
| ⟲🟠 | pypy-3.10 | ????.?? | |
+------+-----------+------------------+-----------------------------------------------+
| ⟲🟡 | pypy-3.11 | ????.?? | :green:`latest PyPy` |
+------+-----------+------------------+-----------------------------------------------+

View File

@@ -0,0 +1,150 @@
.. _JOBTMPL/CheckDocumentation:
.. index::
single: docstr_coverage; CheckDocumentation Template
single: interrogate; CheckDocumentation Template
single: GitHub Action Reusable Workflow; CheckDocumentation Template
CheckDocumentation
##################
The ``CheckDocumentation`` job checks the level of documentation coverage for Python files.
.. topic:: Features
* Check documentation coverage in Python code using :pypi:`docstr_coverage`.
* Check documentation coverage in Python code using :pypi:`interrogate`.
.. topic:: Behavior
1. Checkout repository.
2. Setup Python environment and install dependencies.
3. Run ``docstr_coverage``.
4. Run ``interrogate``.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-CheckDocumentation.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* pip
* :pypi:`docstr_coverage`
* :pypi:`interrogate`
.. _JOBTMPL/CheckDocumentation/Instantiation:
Instantiation
*************
The following instantiation example creates a ``Params`` job derived from job template ``Parameters`` version ``@r6``. It only
requires a `name` parameter to create the artifact names.
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
with:
package_name: myPackage
DocCoverage:
uses: pyTooling/Actions/.github/workflows/CheckDocumentation.yml@r6
needs:
- ConfigParams
with:
directory: ${{ needs.ConfigParams.outputs.package_directory }}
.. seealso::
:ref:`JOBTMPL/ExtractConfiguration`
``ExtractConfiguration`` is usually used to compute the path to the package's source code directory.
.. _JOBTMPL/CheckDocumentation/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/CheckDocumentation/Inputs>`
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=========================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/CheckDocumentation/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/CheckDocumentation/Input/python_version` | no | string | ``'3.13'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/CheckDocumentation/Input/directory` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/CheckDocumentation/Input/fail_under` | no | string | ``'80'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/CheckDocumentation/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/CheckDocumentation/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/CheckDocumentation/Inputs:
Input Parameters
****************
.. _JOBTMPL/CheckDocumentation/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/CheckDocumentation/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/CheckDocumentation/Input/directory:
directory
=========
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid directory or sub-directory.
:Description: Directory where the Python code is located.
.. _JOBTMPL/CheckDocumentation/Input/fail_under:
fail_under
==========
:Type: string
:Required: no
:Default Value: ``'80'``
:Possible Values: Any valid percentage from 0 to 100 encoded as string.
:Description: A minimum percentage level for good documentation. If the documentation coverage is below this level,
the coverage is considered a fail.
.. _JOBTMPL/CheckDocumentation/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/CheckDocumentation/Outputs:
Outputs
*******
This job template has no output parameters.

View File

@@ -0,0 +1,365 @@
.. _JOBTMPL/StaticTypeCheck:
.. index::
single: mypy; StaticTypeCheck Template
single: GitHub Action Reusable Workflow; StaticTypeCheck Template
StaticTypeCheck
###############
This job template runs a static type check using :term:`mypy` and collects the results. These results can be converted
to a HTML report and uploaded as an artifact.
.. topic:: Features
* Run static type check using :term:`mypy`.
.. topic:: Behavior
1. Checkout repository
2. Setup Python and install dependencies
3. Run type checking.
4. Upload type checking report as an artifact
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-StaticTypeCheck.png
:width: 400px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* pip
* Python packages specified via :ref:`JOBTMPL/StaticTypeCheck/Input/requirements`.
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
.. _JOBTMPL/StaticTypeCheck/Instantiation:
Instantiation
*************
Simple Example
==============
This example runs mypy for the Python package ``myPackage`` according to the configuration stored in
:file:`pyproject.toml`. It prints a report into the job's log. In addition is generates a report in HTML format into the
directory ``report/typing``.
.. grid:: 2
.. grid-item::
:columns: 6
.. code-block:: yaml
jobs:
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r6
with:
cobertura_artifact: 'TypeChecking-Cobertura'
junit_artifact: 'TypeChecking-JUnit'
html_artifact: 'TypeChecking-HTML'
.. grid-item::
:columns: 6
.. code-block:: toml
[tool.mypy]
packages = ["myPackage"]
strict = true
pretty = true
html_report = "report/typing/html"
junit_xml = "report/typing/StaticTypingSummary.xml"
cobertura_xml_report = "report/typing"
Complex Example
===============
To ease the handling of mypy parameters stored in :file:`pyproject.toml`, the :ref:`JOBTMPL/ExtractConfiguration` is
used to extract the set configuration parameters for later usage. Similarly, :ref:`JOBTMPL/Parameters` is used to
precompute the artifact's name.
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
with:
package_name: myPackage
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r6
needs:
- ConfigParams
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
junit_report: ${{ needs.ConfigParams.outputs.typing_report_junit }}
junit_artifact: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_junit }}
.. _JOBTMPL/StaticTypeCheck/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/StaticTypeCheck/Inputs>`
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+================+==========================================================================================================================================+
| :ref:`JOBTMPL/StaticTypeCheck/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/requirements` | no | string | ``'-r tests/requirements.txt'`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/mypy_options` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/cobertura_report` | no | string (JSON) | :jsoncode:`{"fullpath": "report/typing/cobertura.xml", "directory": "report/typing", "filename": "cobertura.xml"}` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/junit_report` | no | string (JSON) | :jsoncode:`{"fullpath": "report/typing/StaticTypingSummary.xml", "directory": "report/typing", "filename": "StaticTypingSummary.xml"}` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/html_report` | no | string (JSON) | :jsoncode:`{"directory": "report/typing/html"}` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/cobertura_artifact` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/junit_artifact` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/StaticTypeCheck/Input/html_artifact` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------------+------------------------------------------------------------------------------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/StaticTypeCheck/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/StaticTypeCheck/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/StaticTypeCheck/Inputs:
Input Parameters
****************
.. _JOBTMPL/StaticTypeCheck/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/StaticTypeCheck/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/StaticTypeCheck/Input/requirements:
requirements
============
:Type: string
:Required: no
:Default Value: ``'-r tests/requirements.txt'``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'mypy lxml'``.
:Description: Python dependencies to be installed through *pip*.
.. _JOBTMPL/StaticTypeCheck/Input/mypy_options:
mypy_options
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid command line options for :term:`mypy`.
:Description: Additional options handed over to mypy as ``mypy ${mypy_options}``.
.. _JOBTMPL/StaticTypeCheck/Input/cobertura_report:
cobertura_report
================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/typing",
"filename": "cobertura.xml",
"fullpath": "reports/typing/cobertura.xml"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the type checking report in Cobertura XML format will be
saved.
:filename: Filename of the generated type checking report in Cobertura XML format. |br|
Currently, this filename is hardcoded within :term:`mypy` as :file:`cobertura.xml`.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the type checking report in Cobertura XML format
will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r6
needs:
- ConfigParams
with:
...
cobertura_report: ${{ needs.ConfigParams.outputs.statictyping_cobertura }}
.. _JOBTMPL/StaticTypeCheck/Input/junit_report:
junit_report
============
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/typing",
"filename": "StaticTypingSummary.xml",
"fullpath": "reports/typing/StaticTypingSummary.xml"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the type checking report in JUnit XML format will be
saved.
:filename: Filename of the generated type checking report in JUnit XML format. |br|
Any valid file name for mypy's JUnit XML report.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the type checking report in JUnit XML format
will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r6
needs:
- ConfigParams
with:
...
junit_report: ${{ needs.ConfigParams.outputs.statictyping_junit }}
.. _JOBTMPL/StaticTypeCheck/Input/html_report:
html_report
===========
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/typing/html"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the type checking report in HTML format will be saved.
:Description: Directory as JSON object where the type checking report in HTML format will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r6
needs:
- ConfigParams
with:
...
html_report: ${{ needs.ConfigParams.outputs.statictyping_html }}
.. _JOBTMPL/StaticTypeCheck/Input/cobertura_artifact:
cobertura_artifact
==================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the Cobertura XML report.
.. _JOBTMPL/StaticTypeCheck/Input/junit_artifact:
junit_artifact
==============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the JUnit XML report.
.. _JOBTMPL/StaticTypeCheck/Input/html_artifact:
html_artifact
=============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the HTML report.
.. _JOBTMPL/StaticTypeCheck/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/StaticTypeCheck/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/StaticTypeCheck/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,6 @@
.. _JOBTMPL/VerifyDocs:
VerifyDocs (idea)
#################
.. todo:: VerifyDocs:: Needs documentation.

View File

@@ -0,0 +1,17 @@
.. _JOBTMPL/Quality:
Quality
#######
The category *quality* provides workflow templates implementing
* :ref:`JOBTMPL/VerifyDocs` - Verify code snippets in documentations for correctness.
* :ref:`JOBTMPL/CheckDocumentation` - Check documentation coverage in Python modules.
* :ref:`JOBTMPL/StaticTypeCheck` - Check type annotations using mypy.
.. toctree::
:hidden:
VerifyDocs
StaticTypeCheck
CheckDocumentation

View File

@@ -1,96 +0,0 @@
.. _JOBTMPL/GitHubReleasePage:
Release
#######
This job creates a Release Page on GitHub.
**Release Template in Markdown**:
.. parsed-literal::
**Automated Release created on: ${{ steps.getVariables.outputs.datetime }}**
# New Features
* tbd
* tbd
# Changes
* tbd
* tbd
# Bug Fixes
* tbd
* tbd
# Documentation
* tbd
* tbd
# Unit Tests
* tbd
* tbd
----------
# Related Issues and Pull-Requests
* tbd
* tbd
**Behavior:**
1. Extract information from environment variables provided by GitHub Actions.
2. Create a Release Page on GitHub
**Dependencies:**
* :gh:`actions/create-release` (unmaintained)
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
Release:
uses: pyTooling/Actions/.github/workflows/Release.yml@r0
Complex Example
===============
.. code-block:: yaml
jobs:
Release:
uses: pyTooling/Actions/.github/workflows/Release.yml@r0
if: startsWith(github.ref, 'refs/tags')
needs:
- Package
Parameters
**********
This job template needs no input parameters.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,580 @@
.. _JOBTMPL/PublishReleaseNotes:
.. index::
single: gh; PublishReleaseNotes Template
single: GitHub Action Reusable Workflow; PublishReleaseNotes Template
PublishReleaseNotes
###################
This template creates a GitHub Release Page and uploads assets to that page.
.. topic:: Features
* Assembly a release description from various sources:
* Description file in the repository.
* Description via job template parameter.
* Description from associated pull-request.
* Download artifact and upload selected files as assets to the release page.
* Add an inventory file in JSON format as asset to each release.
* Replace placeholders with variable contents.
* Override the release's title.
* Create draft releases.
* Create pre-release release.
* Create nightly/rolling releases.
.. topic:: Behavior
1. Checkout repository.
2. Install dependencies.
3. Check if it's a full release or nightly release (rolling release).
4. Delete old release.
5. Assemble release notes.
6. Create a new or recreate the release page as draft.
7. Attach files from artifacts as assets:
1. Download artifact
2. Optionally, create compressed archives of that content.
3. Upload assets to release page.
8. Remove draft state from new release page.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-PublishReleaseNotes.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* ``gh`` (GitHub command line interface)
* ``jq`` (JSON processing)
* apt
* zstd
.. _JOBTMPL/PublishReleaseNotes/Instantiation:
Instantiation
*************
.. code-block:: yaml
jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r6
Release:
uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@r6
needs:
- Prepare
if: needs.Prepare.outputs.is_release_tag == 'true'
permissions:
contents: write
actions: write
with:
tag: ${{ needs.Prepare.outputs.version }}
secrets: inherit
.. _JOBTMPL/PublishReleaseNotes/ReleaseNotes:
Release Notes
*************
Providing a release description (a.k.a release page content) can be achieved from various sources. These sources can
also be combined to a single description. Moreover, the resulting description can contain placeholders which can be
replaced by values provided via parameter :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements`.
Description text from file in the repository
The job template's parameter :ref:`JOBTMPL/PublishReleaseNotes/Input/description_file` provides a way to read a
predefined content from a file within the repository. This allows sharing the same text between nightly releases and
full releases.
.. note::
This file can't be computed/modified at pipeline runtime, because a fixed Git commit is checked out for this job
template run.
Descriptions text from pipeline parameter
The job template's parameter :ref:`JOBTMPL/PublishReleaseNotes/Input/description` provides a way to either hard code
a release description in YAML code, or connect a GitHub Action variable ``${{ ... }}`` to that parameter.
The content is available in replacement variable ``%%DESCRIPTION%%``.
Description text from associated PullRequest
If an associated pull-request can be identified for a merge-commit, the pull-requests description can be used as a
release description.
The content is available in replacement variable ``%%PULLREQUEST%%``.
Additional text from :ref:`JOBTMPL/PublishReleaseNotes/Input/description_footer`
Additionally, a footer text is provided.
The content is available in replacement variable ``%%FOOTER%%``.
.. topic:: Order of Processing
1. If :ref:`JOBTMPL/PublishReleaseNotes/Input/description_file` exists and is not empty, it will serve as the main
description. If the description contains ``%%...%%`` placeholders, these placeholders will be replaced
accordingly. If description contains ``%...%`` placeholders, replacement rules provided by
:ref:`JOBTMPL/PublishReleaseNotes/Input/replacements` will be applied.
2. If :ref:`JOBTMPL/PublishReleaseNotes/Input/description` is not empty, it will serve as the main description. If
the description contains ``%%...%%`` placeholders, these placeholders will be replaced accordingly. If description
contains ``%...%`` placeholders, replacement rules provided by :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements`
will be applied.
3. If the associated pull-request exists and is not empty, it's description will serve as the main description. If
the description contains ``%%...%%`` placeholders, these placeholders will be replaced accordingly. If description
contains ``%...%`` placeholders, replacement rules provided by :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements`
will be applied.
4. Otherwise, an error is raised.
.. topic:: Replacements
``%%DESCRIPTION%%``
Replaces the placeholder with the content from :ref:`JOBTMPL/PublishReleaseNotes/Input/description`.
``%%PULLREQUEST%%``, ``%%PULLREQUEST+0%%``, ``%%PULLREQUEST+1%%``, ``%%PULLREQUEST+2%%``, ``%%PULLREQUEST+3%%``
Replaces the content by the associated pull-requests description text.
If an indentation level +N (``+1``, ``+2``, ``+3``) is specified, headlines in the pull-request description will be
moved by N levels down.
``%%FOOTER%%``
Replaces the placeholder with the content from :ref:`JOBTMPL/PublishReleaseNotes/Input/description_footer`.
``%%gh_server%%``
Replaced by the GitHub server URL. |br|
The value is derived from ``${{ github.server_url }}``.
``%%gh_workflow_name%%``
Replaced by the workflow name. |br|
The value is derived from ``${{ github.workflow }}``.
``%%gh_owner%%``
Replaced by the repository owner, which is either the name of a GitHub organisation or a GitHub user account. |br|
The value is derived from ``${{ github.repository_owner }}``.
``%%gh_repo%%``
Replaced by the repository name. |br|
The value is derived from ``${{ github.repository }}`` by splitting namespace and repository name into the
``${repo}`` variable.
``%%gh_owner_repo%%``
Replaced by the repository slug, which is either the name of a GitHub organisation or a GitHub user account
followed by the repository name concatenated by the slash character. |br|
The value is derived from ``${{ github.repository }}``.
``%%gh_pages%%``
Replaced by the URL to the associated GitHub Pages webspace. |br|
The value is formatted as ``https://${{ github.repository_owner }}.github.io/${repo}``.
``%%gh_runid%%``
Replaced by the pipelines ID. |br|
The value is derived from ``${{ github.run_id }}``
``%%gh_actor%%``
Replaced by the actor (user or bot), who launched the pipeline. |br|
The value is derived from ``${{ github.actor }}``.
``%%gh_sha%%``
Replaced by the associated commit's SHA. |br|
The value is derived from ``${{ github.sha }}``
``%%date%%``
Replaced by the current date. |br|
The value is formatted as ``$(date '+%Y-%m-%d')``.
``%%time%%``
Replaced by the current date. |br|
The value is formatted as ``$(date '+%H:%M:%S %Z')``.
``%%datetime%%``
Replaced by the current date. |br|
The value is formatted as ``$(date '+%Y-%m-%d %H:%M:%S %Z')``.
Examples
========
.. todo::
* GHDL - uses description_file and description
* pyTooling - uses pullrequest
.. _JOBTMPL/PublishReleaseNotes/Assets:
Assets
******
.. todo::
PublishReleaseNotes::Assets Describe artifact to asset transformation
Format: ``artifact:file:title``
See also: :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements`
.. _JOBTMPL/PublishReleaseNotes/Inventory:
Inventory
*********
.. todo::
PublishReleaseNotes::Inventory Describe how inventory files are created.
.. _JOBTMPL/PublishReleaseNotes/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PublishReleaseNotes/Inputs>`
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=========================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/ubuntu_image` | no | string | ``'ubuntu-24.04'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/release_branch` | no | string | ``'main'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/mode` | no | string | ``'release'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/tag` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/title` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/description` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/description_file` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/description_footer` | no | string | see parameter details |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/draft` | no | boolean | ``false`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/prerelease` | no | boolean | ``false`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/latest` | no | boolean | ``false`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/assets` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/inventory-json` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/inventory-version` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/inventory-categories` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/tarball-name` | no | string | ``'__pyTooling_upload_artifact__.tar'`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PublishReleaseNotes/Input/can-fail` | no | boolean | ``false`` |
+-------------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PublishReleaseNotes/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PublishReleaseNotes/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/PublishReleaseNotes/Inputs:
Input Parameters
****************
.. _JOBTMPL/PublishReleaseNotes/Input/ubuntu_image:
ubuntu_image
============
:Type: string
:Required: usually no
:Default Value: ``'ubuntu-24.04'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
for available Ubuntu image versions.
:Description: Name of the Ubuntu image used to run a job.
.. _JOBTMPL/PublishReleaseNotes/Input/release_branch:
release_branch
==============
:Type: string
:Required: no
:Default Value: ``'main'``
:Possible Values: Any valid Git branch name.
:Description: Name of the branch containing releases.
.. _JOBTMPL/PublishReleaseNotes/Input/mode:
mode
====
:Type: string
:Required: no
:Default Value: ``'release'``
:Possible Values: ``'release'``, ``'nightly'``
:Description: The release mode, which is either *nightly* (a.k.a *rolling* release) or *release*.
.. _JOBTMPL/PublishReleaseNotes/Input/tag:
tag
===
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid Git tag name.
:Description: Name of the release (tag).
:Condition: It must match an existing tag name in the repository.
.. _JOBTMPL/PublishReleaseNotes/Input/title:
title
=====
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid string suitable for a release title (headline).
:Description: If this parameter is not empty, the releases title is set, which overrides the default title inferred
from the associated tag name.
.. _JOBTMPL/PublishReleaseNotes/Input/description:
description
===========
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid (multi-line) Markdown string.
:Description: The description of the release usually used to render the *release notes*. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/ReleaseNotes` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/description_file:
description_file
================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid Markdown file. |br|
Suggested value: :file:`.github/ReleaseDescription.md`.
:Description: Path to a Markdown file used for the release description. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/ReleaseNotes` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/description_footer:
description_footer
==================
:Type: string
:Required: no
:Default Value:
.. code-block::
--------
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
:Possible Values: Any valid (multi-line) Markdown text.
:Description: A footer added to the description. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/ReleaseNotes` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/draft:
draft
=====
:Type: :red:`boolean`
:Required: no
:Default Value: ``false``
:Possible Values: ``false``, ``true``
:Description: If *true*, the release is kept in *draft* state.
.. note::
GitHub doesn't send e-mail notifications to subscribed users for draft releases.
.. _JOBTMPL/PublishReleaseNotes/Input/prerelease:
prerelease
==========
:Type: :red:`boolean`
:Required: no
:Default Value: ``false``
:Possible Values: ``false``, ``true``
:Description: If *true*, the release is marked as a *pre-release*.
.. _JOBTMPL/PublishReleaseNotes/Input/latest:
latest
======
:Type: :red:`boolean`
:Required: no
:Default Value: ``false``
:Possible Values: ``false``, ``true``
:Description: If *true*, the release is marked as *latest release*.
.. _JOBTMPL/PublishReleaseNotes/Input/replacements:
replacements
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid multi-line string of format ``search=replace`` patterns.
:Description: The given replacements are used to replace placeholders in :ref:`JOBTMPL/PublishReleaseNotes/Input/description`,
:ref:`JOBTMPL/PublishReleaseNotes/Input/description_file`, :ref:`JOBTMPL/PublishReleaseNotes/Input/description_footer`. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/ReleaseNotes` for more details.
:Example: The following example replaces the placeholder ``%version%`` with the actual version number (inferred
from tag name by :ref:`JOBTMPL/PrepareJob`.
.. code-block:: yaml
ReleasePage:
uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@r6
needs:
- Prepare
if: needs.Prepare.outputs.is_release_tag == 'true'
permissions:
contents: write
actions: write
with:
tag: ${{ needs.Prepare.outputs.version }}
description: |
# myPackage %version%
This is the latest release of myPackage.
replacements: |
version=${{ needs.Prepare.outputs.version }}
.. _JOBTMPL/PublishReleaseNotes/Input/assets:
assets
======
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid multi-line string containing artifact to asset transformations. |br|
The ``artifact:file:title`` format is explained at :ref:`JOBTMPL/PublishReleaseNotes/Assets`
:Description: Each line describes which artifacts to download and extract as well as which extracted file to upload
as a release asset. The files title can be changed. |br|
Replacement rules from parameter :ref:`JOBTMPL/PublishReleaseNotes/Input/replacements` can be used,
too. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/Assets` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/inventory-json:
inventory-json
==============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid JSON filename. |br|
Suggested value: :file:`inventory.json`.
:Description: If this parameter is not empty, an inventory of all assets will be created and attached as a JSON file
to the release. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/Inventory` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/inventory-version:
inventory-version
=================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid version string.
:Description: If this parameter is not empty, the version field in the inventory JSON is set to this value. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/Inventory` for more details.
.. hint::
Especially for *nightly*/*rolling* releases, the used Git tag is a name rather then a version
number. Therefore, a version number must be provided thus a nightly release can be identified as
``vX.Y.Z``.
.. _JOBTMPL/PublishReleaseNotes/Input/inventory-categories:
inventory-categories
====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: A colon separated list of identifiers used as category names in an inventory JSON.
:Description: For decoding hierarchy levels (categories) in an inventory JSON, the hierarchy of categories can be
added to the inventoy JSON. |br|
See :ref:`JOBTMPL/PublishReleaseNotes/Inventory` for more details.
.. _JOBTMPL/PublishReleaseNotes/Input/tarball-name:
tarball-name
============
:Type: string
:Required: no
:Default Value: ``'__pyTooling_upload_artifact__.tar'``
:Possible Values: Any valid name for a tarball file.
:Description:
.. todo:: PublishReleaseNotes::tarball-name Needs documentation.
.. _JOBTMPL/PublishReleaseNotes/Input/can-fail:
can-fail
========
:Type: :red:`boolean`
:Required: no
:Default Value: ``false``
:Possible Values: ``false``, ``true``
:Description:
.. todo:: PublishReleaseNotes::can-fail Needs documentation.
.. _JOBTMPL/PublishReleaseNotes/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/PublishReleaseNotes/Outputs:
Outputs
*******
.. _JOBTMPL/PublishReleaseNotes/Output/release-page:
release-page
============
:Type: string
:Description: Returns the URL to the release page.
:Example: ``tbd``
.. _JOBTMPL/PublishReleaseNotes/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,198 @@
.. _JOBTMPL/TagReleaseCommit:
.. index::
single: gh; TagReleaseCommit Template
single: GitHub Action Reusable Workflow; TagReleaseCommit Template
TagReleaseCommit
################
The ``TagReleaseCommit`` job template creates a tag at the commit currently used by the pipeline run and then it
triggers a new pipeline run for that tag, a.k.a *tag pipeline* or *release pipeline*.
.. note::
When the *tag pipeline* is launched, the pipeline is displayed in GitHub Actions with the name in the YAML file. In
contrast, when a tag is manually added and pushed via Git on command line, the tag name is displayed.
Currently, no command, API or similar is known to add a tag and trigger a matching pipeline run, where the pipeline
is named like the used tag. Thus, currently this job template has a slightly different behavior compared to manual
tagging and pushing a tag to GitHub.
In addition, GitHub doesn't support *project access token*, thus there is no solution to create a user independent
token to emulate a manual push operation.
.. topic:: Features
* Tag the current pipeline's commit.
* Trigger a new pipeline run for this new tag.
.. topic:: Behavior
1. Tag the current commit with a tag named like :ref:`JOBTMPL/TagReleaseCommit/Input/version`.
2. Trigger a pipeline run for the new tag.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-TagReleaseCommit.png
:width: 350px
.. topic:: Dependencies
* :gh:`actions/github-script`
.. _JOBTMPL/TagReleaseCommit/Instantiation:
Instantiation
*************
The following instantiation example depicts three jobs within a bigger pipeline. The ``prepare`` job derived from job
template ``PrepareJob`` version ``@r6`` figures out if a pipeline runs for a release merge-commit, for a tag or any
other reason. Its outputs are used to either run a ``TriggerTaggedRelease`` job derived from job template
``TagReleaseCommit`` version ``@r6``, or alternatively run the ``ReleasePage`` job derived from job template
``PublishReleaseNotes`` version ``@r6``.
.. code-block:: yaml
jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r6
# Other pipeline jobs
TriggerTaggedRelease:
uses: pyTooling/Actions/.github/workflows/TagReleaseCommit.yml@r6
needs:
- Prepare
if: needs.Prepare.outputs.is_release_commit == 'true' && github.event_name != 'schedule'
permissions:
contents: write # required for create tag
actions: write # required for trigger workflow
with:
version: ${{ needs.Prepare.outputs.version }}
auto_tag: ${{ needs.Prepare.outputs.is_release_commit }}
secrets: inherit
ReleasePage:
uses: pyTooling/Actions/.github/workflows/PublishReleaseNotes.yml@r6
needs:
- Prepare
if: needs.Prepare.outputs.is_release_tag == 'true'
permissions:
contents: write
actions: write
with:
tag: ${{ needs.Prepare.outputs.version }}
secrets: inherit
.. _JOBTMPL/TagReleaseCommit/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/TagReleaseCommit/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/TagReleaseCommit/Input/ubuntu_image` | no | string | ``'ubuntu-24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/TagReleaseCommit/Input/version` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/TagReleaseCommit/Input/auto_tag` | yes | string | — — — — |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/TagReleaseCommit/Input/workflow` | no | string | ``'Pipeline.yml'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/TagReleaseCommit/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/TagReleaseCommit/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/TagReleaseCommit/Inputs:
Input Parameters
****************
.. _JOBTMPL/TagReleaseCommit/Input/ubuntu_image:
ubuntu_image
============
:Type: string
:Required: no
:Default Value: ``'ubuntu-24.04'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
for available Ubuntu image versions.
:Description: Name of the Ubuntu image used to run this job.
.. _JOBTMPL/TagReleaseCommit/Input/version:
version
=======
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: Any valid Git tag name.
:Description: The version string to be used for tagging.
.. _JOBTMPL/TagReleaseCommit/Input/auto_tag:
auto_tag
========
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: ``'false'``, ``'true'```
:Description: If *true*, tag the current commit.
.. _JOBTMPL/TagReleaseCommit/Input/workflow:
workflow
========
:Type: string
:Required: no
:Default Value: ``'Pipeline.yml'``
:Possible Values: Any valid GitHub Action pipeline filename.
:Description: Github Action pipeline (workflow) to trigger after tag creation.
.. note::
Compared to manual tagging and pushing a tag, where a pipeline is triggered automatically, here a
pipeline must be trigger separately by API. Therefore the pipeline doesn't run with the name of the
tag, but with the name specified within the workflow YAML file.
.. _JOBTMPL/TagReleaseCommit/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/TagReleaseCommit/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/TagReleaseCommit/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,15 @@
.. _JOBTMPL/Release:
Release
#######
The category *release* provides workflow templates implementing
* :ref:`JOBTMPL/TagReleaseCommit` - Automatically tag current commit in Git using the associate pull-requests title.
* :ref:`JOBTMPL/PublishReleaseNotes` - Create GitHub release page and upload release assets.
.. toctree::
:hidden:
TagReleaseCommit
PublishReleaseNotes

View File

@@ -0,0 +1,877 @@
.. _JOBTMPL/ExtractConfiguration:
.. index::
single: GitHub Action Reusable Workflow; ExtractConfiguration Template
ExtractConfiguration
####################
The ``ExtractConfiguration`` job template extracts Python project settings from :file:`pyproject.toml` and shares the
values via output parameters with other jobs. Thus, only a single centralized implementation is needed to avoid code
duplications within jobs.
.. topic:: Features
* Extract the unittest report file in pytest's JUnit XML format as directory name, filename and full path from
:file:`pyproject.toml`.
* Extract the merged unittest XML report file as directory name, filename and full path from :file:`pyproject.toml`.
* Extract code coverage report in HTML format as directory from :file:`pyproject.toml`.
* Extract code coverage report file in Cobertura XML format as directory name, filename and full path from
:file:`pyproject.toml`.
* Extract code coverage report file in Coverage.py's JSON format as directory name, filename and full path from
:file:`pyproject.toml`.
* Extract static typing report file in Cobertura XML format as directory name, filename and full path from
:file:`pyproject.toml`.
* Extract static typing report file in JUnit XML format as directory name, filename and full path from
:file:`pyproject.toml`.
* Extract static typing report in HTML format as directory name from :file:`pyproject.toml`.
.. topic:: Behavior
1. Checkout repository.
2. Install Python dependencies.
3. Compute the full package name and the package source directory.
4. Read :file:`pyproject.toml` and extract settings for:
* :term:`Coverage.py`
* :term:`mypy`
* :term:`pyEDAA.Reports`
* :term:`pytest`
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-ExtractConfiguration.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :pypi:`wheel`
* :pypi:`tomli`
.. _JOBTMPL/ExtractConfiguration/Instantiation:
Instantiation
*************
The following instantiation example creates a ``ConfigParams`` job derived from job template ``ExtractConfiguration``
version ``@r6``. It requires no special parameters to extract unit test (pytest) and code coverage (Coverage.py)
settings.
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
with:
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
.. seealso::
:ref:`JOBTMPL/UnitTesting`
``UnitTesting`` is usually
:ref:`JOBTMPL/StaticTypeCheck`
xxx
:ref:`JOBTMPL/CheckDocumentation`
xxx
:ref:`JOBTMPL/InstallPackage`
xxx
:ref:`JOBTMPL/PublishCoverageResults`
xxx
:ref:`JOBTMPL/PublishTestResults`
xxx
:ref:`JOBTMPL/SphinxDocumentation`
xxx
.. _JOBTMPL/ExtractConfiguration/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/ExtractConfiguration/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/ExtractConfiguration/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Input/python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Input/coverage_config` | no | string | ``'pyproject.toml'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/ExtractConfiguration/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/ExtractConfiguration/Outputs>`
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| Result Name | Type | Description |
+=================================================================================+================+===================================================================+
| :ref:`JOBTMPL/ExtractConfiguration/Output/unittest_report_xml` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/unittest_merged_report_xml` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/coverage_report_html` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/coverage_report_xml` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/coverage_report_json` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/typing_report_cobertura` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/typing_report_junit` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/ExtractConfiguration/Output/typing_report_html` | string (JSON) | |
+---------------------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
.. _JOBTMPL/ExtractConfiguration/Inputs:
Input Parameters
****************
.. _JOBTMPL/ExtractConfiguration/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/ExtractConfiguration/Input/python_version:
.. include:: ../_python_version.rst
.. _JOBTMPL/ExtractConfiguration/Input/coverage_config:
coverage_config
===============
:Type: string
:Required: no
:Default Value: ``'pyproject.toml'``
:Possible Values: Any valid path to a :file:`pyproject.toml` file. |br|
Alternatively, path to a :file:`.coveragerc` file (deprecated).
:Description: Path to a Python project configuration file for extraction of project settings.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Extracted values from :file:`pyproject.toml`
.. code-block:: toml
[tool.coverage.xml]
output = "report/coverage/coverage.xml"
[tool.coverage.json]
output = "report/coverage/coverage.json"
[tool.coverage.html]
directory = "report/coverage/html"
title="Code Coverage of myPackage"
[tool.mypy]
html_report = "report/typing/html"
junit_xml = "report/typing/StaticTypingSummary.xml"
cobertura_xml_report = "report/typing"
[tool.pyedaa-reports]
junit_xml = "report/unit/unittest.xml"
[tool.pytest]
junit_xml = "report/unit/UnittestReportSummary.xml"
.. _JOBTMPL/ExtractConfiguration/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/ExtractConfiguration/Outputs:
Outputs
*******
.. _JOBTMPL/ExtractConfiguration/Output/unittest_report_xml:
unittest_report_xml
===================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the unit
test's report XML file created by :term:`pytest` in JUnit XML format.
The JSON object contains these fields:
:directory: Contains the directory where the unittest XML report file will be created. |br|
Example: :file:`reports/unit`
:filename: Contains the filename of the unittest XML report file. |br|
Example: :file:`UnittestReportSummary.xml`
:fullpath: Contains the path where the unittest XML report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/unit/UnittestReportSummary.xml`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`pytest`.
.. code-block:: toml
[tool.pytest]
junit_xml = "report/unit/UnittestReportSummary.xml"
:Example:
.. code-block:: json
{ "directory": "reports/unit",
"filename": "UnittestReportSummary.xml",
"fullpath": "reports/unit/UnittestReportSummary.xml"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.unittest_report_xml).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.unittest_report_xml).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.unittest_report_xml).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/unittest_merged_report_xml:
unittest_merged_report_xml
==========================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the merged
unittest report file in JUnit XML format created by :term:`pyEDAA.Reports`.
The JSON object contains these fields:
:directory: Contains the directory where the merged unittest XML report file will be created. |br|
Example: :file:`reports/unit`
:filename: Contains the filename of the merged unittest XML report file. |br|
Example: :file:`unittest.xml`
:fullpath: Contains the path where the merged unittest XML report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/unit/unittest.xml`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`pyEDAA.Reports`.
.. code-block:: toml
[tool.pyedaa-reports]
junit_xml = "report/unit/unittest.xml"
:Example:
.. code-block:: json
{ "directory": "reports/unit",
"filename": "unittest.xml",
"fullpath": "reports/unit/unittest.xml"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.unittest_merged_report_xml }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.unittest_merged_report_xml).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.unittest_merged_report_xml).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.unittest_merged_report_xml).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/coverage_report_html:
coverage_report_html
====================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory and the full path, where the code coverage
HTML report will be generated by :term:`Coverage.py`.
The JSON object contains these fields:
:directory: Contains the directory where the code coverage report in HTML format will be created. |br|
Example: :file:`report/coverage/html`
:fullpath: Contains the path where the code coverage report in HTML format will be created. |br|
This is the same as the previous JSON field. |br|
Example: :file:`report/coverage/html`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`Coverage.py`.
.. code-block:: toml
[tool.coverage.html]
directory = "report/coverage/html"
title="Code Coverage of pyDummy"
:Example:
.. code-block:: json
{ "directory": "report/coverage/html",
"fullpath": "report/coverage/html"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.coverage_report_html }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_html).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_html).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.coverage_report_html).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/coverage_report_xml:
coverage_report_xml
===================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the code
coverage XML report file in Cobertura XML format created by :term:`Coverage.py`.
The JSON object contains these fields:
:directory: Contains the directory where the code coverage XML report file will be created. |br|
Example: :file:`reports/unit`
:filename: Contains the filename of the code coverage XML report file. |br|
Example: :file:`unittest.xml`
:fullpath: Contains the path where the code coverage XML report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/unit/unittest.xml`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`Coverage.py`.
.. code-block:: toml
[tool.coverage.xml]
output = "report/coverage/coverage.xml"
:Example:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.xml",
"fullpath": "reports/coverage/coverage.xml"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_xml).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_xml).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.coverage_report_xml).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/coverage_report_json:
coverage_report_json
====================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the code
coverage JSON report file created by :term:`Coverage.py`.
The JSON object contains these fields:
:directory: Contains the directory where the code coverage JSON report file will be created. |br|
Example: :file:`reports/unit`
:filename: Contains the filename of the code coverage JSON report file. |br|
Example: :file:`unittest.json`
:fullpath: Contains the path where the code coverage JSON report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/unit/unittest.json`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`Coverage.py`.
.. code-block:: toml
[tool.coverage.json]
output = "report/coverage/coverage.json"
:Example:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.json",
"fullpath": "reports/coverage/coverage.json"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.coverage_report_json }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_json).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.coverage_report_json).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.coverage_report_json).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/typing_report_cobertura:
typing_report_cobertura
=======================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the static
type checking report file in Cobertura format created by :term:`mypy`.
The JSON object contains these fields:
:directory: Contains the directory where the type checking XML report file will be created. |br|
Example: :file:`reports/typing`
:filename: Contains the filename of the type checking XML report file. |br|
Example: :file:`cobertura.xml`
:fullpath: Contains the path where the type checking XML report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/typing/cobertura.xml`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`mypy`.
.. code-block:: toml
[tool.mypy]
cobertura_xml_report = "report/typing"
:Example:
.. code-block:: json
{ "directory": "reports/typing",
"filename": "cobertura.xml",
"fullpath": "reports/typing/cobertura.xml"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.typing_report_cobertura }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.typing_report_cobertura).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.typing_report_cobertura).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.typing_report_cobertura).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/typing_report_junit:
typing_report_junit
===================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the static
type checking report file in JUnit XML format created by :term:`mypy`.
The JSON object contains these fields:
:directory: Contains the directory where the static typing JUnit XML report file will be created. |br|
Example: :file:`reports/typing`
:filename: Contains the filename of the static typing JUnit XML report file. |br|
Example: :file:`StaticTypingSummary.xml`
:fullpath: Contains the path where the cstatic typing JUnit XML report file will be created. |br|
This is the concatenation of both previous JSON fields. |br|
Example: :file:`reports/typing/StaticTypingSummary.xml`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`mypy`.
.. code-block:: toml
[tool.mypy]
junit_xml = "report/typing/StaticTypingSummary.xml"
:Example:
.. code-block:: json
{ "directory": "reports/typing",
"filename": "StaticTypingSummary.xml",
"fullpath": "reports/typing/StaticTypingSummary.xml"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.typing_report_junit }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.typing_report_junit).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.typing_report_junit).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.typing_report_junit).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Output/typing_report_html:
typing_report_html
==================
:Type: string (JSON)
:Description: Returns a string in JSON format containing the directory, the filename and the fullpath to the static
type checking report in HTML format created by :term:`mypy`.
The JSON object contains these fields:
:directory: Contains the directory where the static typing HTML report will be created. |br|
Example: :file:`reports/typing/html`
:fullpath: Contains the path where the static typing HTML report will be created. |br|
This is the same as the previous JSON field. |br|
Example: :file:`reports/typing/html`
:pyproject.toml: Matching :file:`pyproject.toml` configuration for tool :term:`mypy`.
.. code-block:: toml
[tool.mypy]
html_report = "report/typing/html"
:Example:
.. code-block:: json
{ "directory": "reports/typing/html",
"fullpath": "reports/typing/html"
}
:Usage:
.. tab-set::
.. tab-item:: Forwarding complete JSON object
:sync: ForwardParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: ${{ needs.ConfigParams.outputs.typing_report_html }}
.. tab-item:: Assembling new JSON object
:sync: AssembleParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
report: >-
{ "dir": "${{ fromJson(needs.ConfigParams.outputs.typing_report_html).directory }}",
"file": "${{ fromJson(needs.ConfigParams.outputs.typing_report_html).filename }}"
}
.. tab-item:: Using single field from JSON object
:sync: SingleFieldFromParam
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
OtherJob:
uses: some/path/to/a/template@r6
needs:
- ConfigParams
with:
fullpath: ${{ fromJson(needs.ConfigParams.outputs.typing_report_html).fullpath }}
.. _JOBTMPL/ExtractConfiguration/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,780 @@
.. _JOBTMPL/Parameters:
.. index::
single: GitHub Action Reusable Workflow; Parameters Template
Parameters
##########
The ``Parameters`` job template is a workaround for the limitations of GitHub Actions to handle global variables in
GitHub Actions workflows (see `actions/runner#480 <https://github.com/actions/runner/issues/480>`__).
It generates output parameters containing a list of artifact names and a job matrix to be used in later-running jobs.
.. topic:: Features
* Generate artifact names for various artifacts.
* Generate a matrix of job combinations as a JSON string made from:
* systems (Ubuntu, macOS, Windows)
* architecture (x64-64, aarch64)
* Python versions (3.9, 3.10, ..., 3.13),
* Python implementation (CPython, PyPy), and
* environments (Native, MinGW64, UCRT64, ...).
* Provide a (default) Python version for other jobs.
.. topic:: Behavior
1. Delay job execution by :ref:`JOBTMPL/Parameters/Input/pipeline-delay` seconds.
2. Compute job matrix using an embedded Python script.
3. Assemble artifact names using a common prefix derived from Python namespace and package name.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-Parameters.png
:width: 1000px
.. topic:: Dependencies
* Python from base-system.
.. _JOBTMPL/Parameters/Instantiation:
Instantiation
*************
Simple Example
==============
.. grid:: 2
.. grid-item::
:columns: 5
The following instantiation example creates a ``Params`` job derived from job template ``Parameters`` version
``@r6``. It only requires a :ref:`JOBTMPL/Parameters/Input/package_name` parameter to create the artifact names.
.. grid-item::
:columns: 7
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- Params
with:
jobs: ${{ needs.Params.outputs.python_jobs }}
Complex Example
===============
.. grid:: 2
.. grid-item::
:columns: 5
The following instantiation example creates 3 jobs from the same template, but with differing input parameters.
The first ``UnitTestingParams`` job might be used to create a job matrix of unit tests. It creates the cross of
default systems (Windows, Ubuntu, macOS, macOS-ARM, MinGW64, UCRT64) and the given list of Python versions
including some mypy versions. In addition a list of excludes (marked as :deletion:`deletions`) and includes
(marked as :addition:`additions`) is handed over resulting in the following combinations.
The second ``PerformanceTestingParams`` job might be used to create a job matrix for performance tests. Here a
pipeline might be limited to the latest two Python versions on a selected list of platforms.
The third ``PlatformTestingParams`` job might be used to create a job matrix for platform compatibility tests.
Here a pipeline might be limited to the latest Python version, but all available platforms.
.. grid-item::
:columns: 7
.. code-block:: yaml
jobs:
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_namespace: myFramework
package_name: Extension
python_version_list: '3.9 3.10 3.11 3.12 pypy-3.10 pypy-3.11'
system_list: 'ubuntu windows macos macos-arm mingw64 ucrt64'
include_list: 'ubuntu:3.13 macos:3.13 macos-arm:3.13'
exclude_list: 'windows:pypy-3.10 windows:pypy-3.11'
PerformanceTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_namespace: myFramework
package_name: Extension
python_version_list: '3.12 3.13'
system_list: 'ubuntu windows macos macos-arm'
PlatformTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_namespace: myFramework
package_name: Extension
python_version_list: '3.13'
system_list: 'ubuntu windows macos macos-arm mingw32 mingw64 clang64 ucrt64'
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Version | 3.9 🔴 | 3.10 🟠 | 3.11 🟡 | 3.12 🟢 | 3.13 🟢 | 3.14.b1 🟣 | pypy-3.9 🔴 | pypy-3.10 🟠 | pypy-3.11 🟡 |
+================================+================+=================+=================+=================+============================+============+=============+===============================+===============================+
| Ubuntu 🐧 | ubuntu:3.9 | ubuntu:3.10 | ubuntu:3.11 | ubuntu:3.12 | :addition:`ubuntu:3.13` | | | ubuntu:pypy-3.10 | ubuntu:pypy-3.11 |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| macOS (x86-64) 🍎 | macos:3.9 | macos:3.10 | macos:3.11 | macos:3.12 | :addition:`macos:3.13` | | | macos:pypy-3.10 | macos:pypy-3.11 |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| macOS (aarch64) 🍏 | macos-arm:3.9 | macos-arm:3.10 | macos-arm:3.11 | macos-arm:3.12 | :addition:`macos-arm:3.13` | | | macos:pypy-3.10 | macos:pypy-3.11 |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 | windows:3.9 | windows:3.10 | windows:3.11 | windows:3.12 | | | | :deletion:`windows:pypy-3.10` | :deletion:`windows:pypy-3.11` |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + MSYS 🟪 | | | | | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + MinGW32 ⬛ | | | | | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + MinGW64 🟦 | | | | mingw64:3.12 | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + Clang32 🟫 | | | | | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + Clang64 🟧 | | | | | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
| Windows Server 🪟 + UCRT64 🟨 | | | | ucrt64:3.12 | | | | | |
+--------------------------------+----------------+-----------------+-----------------+-----------------+----------------------------+------------+-------------+-------------------------------+-------------------------------+
.. _JOBTMPL/Parameters/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/Parameters/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/Parameters/Input/ubuntu_image_version` | no | string | ``'24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/name` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/package_namespace` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/package_name` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/python_version` | no | string | ``'3.13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/python_version_list` | no | string | ``'3.9 3.10 3.11 3.12 3.13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/system_list` | no | string | ``'ubuntu windows macos macos-arm mingw64 ucrt64'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/include_list` | no | string | ``''`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/exclude_list` | no | string | ``'windows-arm:3.9 windows-arm:3.10'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/disable_list` | no | string | ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/ubuntu_image` | no | string | ``'ubuntu-24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/ubuntu_arm_image` | no | string | ``'ubuntu-24.04-arm'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/windows_image` | no | string | ``'windows-2025'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/windows_arm_image` | no | string | ``'windows-11-arm'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/macos_intel_image` | no | string | ``'macos-13'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/macos_arm_image` | no | string | ``'macos-15'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Input/pipeline-delay` | no | number | ``0`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/Parameters/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/Parameters/Outputs>`
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| Result Name | Type | Description |
+=====================================================================+================+===================================================================+
| :ref:`JOBTMPL/Parameters/Output/python_version` | string | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Output/package_fullname` | string | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Output/package_directory` | string | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Output/artifact_basename` | string | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Output/artifact_names` | string (JSON) | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/Parameters/Output/python_jobs` | string (JSON) | |
+---------------------------------------------------------------------+----------------+-------------------------------------------------------------------+
.. _JOBTMPL/Parameters/Inputs:
Input Parameters
****************
.. _JOBTMPL/Parameters/Input/ubuntu_image_version:
.. include:: ../_ubuntu_image_version.rst
.. _JOBTMPL/Parameters/Input/name:
name
====
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid artifact name.
:Description: Prefix used to generate artifact names. Usually, the name of the Python package. |br|
In case this parameter is an empty string, the artifact prefix is derived from :ref:`JOBTMPL/Parameters/Input/package_name`
if the package is a simple Python package, **or** from :ref:`JOBTMPL/Parameters/Input/package_namespace`
and :ref:`JOBTMPL/Parameters/Input/package_name`, if the package is a Python namespace package.
.. _JOBTMPL/Parameters/Input/package_namespace:
package_namespace
=================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid Python namespace.
:Description: In case the package is a Python namespace package, the name of the library's or package's namespace
needs to be specified using this parameter. |br|
In case of a simple Python package, this parameter must be specified as an empty string (``''``),
which is the default. |br|
This parameter is used to derive :ref:`JOBTMPL/Parameters/Input/name`, if it's an empty string.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Example Instantiation
.. code-block:: yaml
name: Pipeline
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_namespace: myFramework
package_name: Extension
.. grid-item::
:columns: 4
.. rubric:: Example Directory Structure
.. code-block::
📂ProjectRoot/
📂myFramework/
📂Extension/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. _JOBTMPL/Parameters/Input/package_name:
package_name
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid Python package name.
:Description: In case of a simple Python package, this package's name is specified using this parameter. |br|
In case the package is a Python namespace package, the parameter
:ref:`JOBTMPL/Parameters/Input/package_namespace` must be specified, too. |br|
This parameter is used to derive :ref:`JOBTMPL/Parameters/Input/name`, if it's an empty string.
:Example:
.. grid:: 2
.. grid-item::
:columns: 5
.. rubric:: Example Instantiation
.. code-block:: yaml
name: Pipeline
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
.. grid-item::
:columns: 4
.. rubric:: Example Directory Structure
.. code-block::
📂ProjectRoot/
📂myFramework/
📦SubPackage/
🐍__init__.py
🐍SubModuleA.py
🐍__init__.py
🐍ModuleB.py
.. _JOBTMPL/Parameters/Input/python_version:
python_version
==============
:Type: string
:Required: no
:Default Value: ``'3.13'``
:Possible Values: Any valid Python version conforming to the pattern ``<major>.<minor>`` or ``pypy-<major>.<minor>``. |br|
See `actions/python-versions - available Python versions <https://github.com/actions/python-versions>`__
and `actions/setup-python - configurable Python versions <https://github.com/actions/setup-python>`__.
:Description: Python version used as default for other jobs requiring a single Python version. |br|
In case :ref:`JOBTMPL/Parameters/Input/python_version_list` is an empty string, this version is used
to populate the version list.
.. _JOBTMPL/Parameters/Input/python_version_list:
python_version_list
===================
:Type: string
:Required: no
:Default Value: ``'3.9 3.10 3.11 3.12 3.13'``
:Possible Values: A space separated list of valid Python versions conforming to the pattern ``<major>.<minor>`` or
``pypy-<major>.<minor>``. |br|
See `actions/python-versions - available Python versions <https://github.com/actions/python-versions>`__
and `actions/setup-python - configurable Python versions <https://github.com/actions/setup-python>`__.
:Description: The list of space-separated Python versions used for Python variation testing.
.. include:: ../PythonVersionList.rst
.. _JOBTMPL/Parameters/Input/system_list:
system_list
===========
:Type: string
:Required: no
:Default Value: ``'ubuntu windows macos macos-arm mingw64 ucrt64'``
:Possible Values: A space separated list of system names.
:Description: The list of space-separated systems used for application testing.
.. include:: ../SystemList.rst
.. _JOBTMPL/Parameters/Input/include_list:
include_list
============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be included into the list of test
variants.
:Example:
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
include_list: "ubuntu:3.11 macos:3.11"
.. _JOBTMPL/Parameters/Input/exclude_list:
exclude_list
============
:Type: string
:Required: no
:Default Value: ``'windows-arm:3.9 windows-arm:3.10'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be excluded from the list of test
variants.
:Example:
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
exclude_list: "windows:pypy-3.8 windows:pypy-3.9"
.. _JOBTMPL/Parameters/Input/disable_list:
disable_list
============
:Type: string
:Required: no
:Default Value: ``'windows-arm:pypy-3.10 windows-arm:pypy-3.11'``
:Possible Values: A space separated list of ``<system>:<python_version>`` tuples.
:Description: List of space-separated ``<system>:<python_version>`` tuples to be temporarily disabled from the list
of test variants. |br|
Each disabled item creates a warning in the workflow log.
:Warning Example:
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
disable_list: "windows:3.10 windows:3.11"
.. image:: ../../_static/GH_Workflow_DisabledJobsWarnings.png
:width: 400px
.. _JOBTMPL/Parameters/Input/ubuntu_image:
ubuntu_image
============
:Type: string
:Required: no
:Default Value: ``'ubuntu-24.04'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
for available Ubuntu image versions.
:Description: Name of the Ubuntu x86-64 image and version used to run a Ubuntu jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/ubuntu_arm_image:
ubuntu_arm_image
================
:Type: string
:Required: no
:Default Value: ``'ubuntu-24.04-arm'``
:Possible Values: See `actions/partner-runner-images - Available Images <https://github.com/actions/partner-runner-images?tab=readme-ov-file#available-images>`__
for available Ubuntu ARM image versions.
:Description: Name of the Ubuntu aarch64 image and version used to run a Ubuntu ARM jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/windows_image:
windows_image
=============
:Type: string
:Required: no
:Default Value: ``'windows-2025'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
:Description: Name of the Windows Server x86-64 image and version used to run a Windows jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/windows_arm_image:
windows_arm_image
=================
:Type: string
:Required: no
:Default Value: ``'windows-11-arm'``
:Possible Values: See `actions/partner-runner-images - Available Images <https://github.com/actions/partner-runner-images?tab=readme-ov-file#available-images>`__
:Description: Name of the Windows aarch64 image and version used to run a Windows ARM jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/macos_intel_image:
macos_intel_image
=================
:Type: string
:Required: no
:Default Value: ``'macos-13'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
:Description: Name of the macOS x86-64 image and version used to run a macOS Intel jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/macos_arm_image:
macos_arm_image
===============
:Type: string
:Required: no
:Default Value: ``'macos-15'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
:Description: Name of the macOS aarch64 image and version used to run a macOS ARM jobs when selected via :ref:`JOBTMPL/Parameters/Input/system_list`.
.. _JOBTMPL/Parameters/Input/pipeline-delay:
pipeline-delay
==============
:Type: number
:Required: no
:Default Value: ``0``
:Possible Values: Any integer number.
:Description: Slow down this job, to delay the startup of the GitHub Action pipline.
.. _JOBTMPL/Parameters/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/Parameters/Outputs:
Outputs
*******
.. _JOBTMPL/Parameters/Output/python_version:
python_version
==============
:Type: string
:Default Value: ``'3.13'``
:Possible Values: Any valid Python version conforming to the pattern ``<major>.<minor>`` or ``pypy-<major>.<minor>``.
:Description: Returns
A single string parameter representing the default Python version that should be used across multiple jobs in the same
pipeline.
Such a parameter is needed as a workaround, because GitHub Actions doesn't support proper handling of global pipeline
variables. Thus, this job is used to compute an output parameter that can be reused in other jobs.
:Example:
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
name: pyTooling
CodeCoverage:
uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r6
needs:
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
.. _JOBTMPL/Parameters/Output/package_fullname:
package_fullname
================
:Type: string
:Description: Returns the full package name composed from :ref:`JOBTMPL/Parameters/Input/package_namespace`
and :ref:`JOBTMPL/Parameters/Input/package_name`.
:Example: ``myFramework.Extension``
.. _JOBTMPL/Parameters/Output/package_directory:
package_directory
=================
:Type: string
:Description: Returns the full package path composed from :ref:`JOBTMPL/Parameters/Input/package_namespace`
and :ref:`JOBTMPL/Parameters/Input/package_name`.
:Example: ``myFramework/Extension``
.. _JOBTMPL/Parameters/Output/artifact_basename:
artifact_basename
=================
:Type: string
:Description: Returns the basename (prefix) of all :ref:`artifact names <JOBTMPL/Parameters/Output/artifact_names>` |br|.
The basename is either :ref:`JOBTMPL/Parameters/Input/name` if set, otherwise its
:ref:`JOBTMPL/Parameters/Output/package_fullname`.
:Example: ``myFramework.Extension``
.. _JOBTMPL/Parameters/Output/artifact_names:
artifact_names
==============
:Type: string (JSON)
:Description: Returns a JSON dictionary of artifact names sharing a common prefix (see :ref:`JOBTMPL/Parameters/Input/name`). |br|
As artifacts are handed from jo to job, a consistent naming scheme is advised to avoid duplications
and naming artifacts by hand. This technique solves again the problem of global variables in GitHub
Action YAMl files and the need for assigning the same value (here artifact name) to multiple jobs
templates.
The supported artifacts are:
:unittesting_xml: UnitTesting XML summary report
:unittesting_html: UnitTesting HTML summary report
:perftesting_xml: PerformanceTesting XML summary report
:benchtesting_xml: Benchmarking XML summary report
:apptesting_xml: ApplicationTesting XML summary report
:codecoverage_sqlite: Code Coverage internal database (SQLite)
:codecoverage_xml: Code Coverage Cobertura XML report
:codecoverage_json: Code Coverage Coverage.py JSON report
:codecoverage_html: Code Coverage HTML report
:statictyping_cobertura: Static Type Checking Cobertura XML report
:statictyping_junit: Static Type Checking JUnit XML report
:statictyping_html: Static Type Checking HTML report
:package_all: Packaged Python project (multiple formats)
:documentation_html: Documentation in HTML format
:documentation_latex: Documentation in LaTeX format
:documentation_pdf: Documentation in PDF format
:Example:
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
name: pyTooling
Coverage:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- Params
with:
unittest_xml_artifact: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}
.. _JOBTMPL/Parameters/Output/python_jobs:
python_jobs
===========
:Type: string (JSON)
:Description: Returns a JSON array of job descriptions, wherein each job description is a dictionary providing the
following key-value pairs:
:sysicon: icon to display
:system: name of the system
:runs-on: virtual machine image and base operating system
:runtime: name of the runtime environment if not running natively on the VM image
:shell: name of the shell
:pyicon: icon for CPython or pypy
:python: Python version
:envname: full name of the selected environment
:Example:
.. code-block:: yaml
jobs:
Params:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
name: pyDummy
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- Params
with:
jobs: ${{ needs.Params.outputs.python_jobs }}
:Usage: The generated JSON array can be unpacked using the ``fromJson(...)`` function in a job's matrix
``strategy:matrix:include`` like this:
.. code-block:: yaml
name: Unit Testing (Matrix)
on:
workflow_call:
inputs:
jobs:
required: true
type: string
jobs:
UnitTesting:
name: ${{ matrix.sysicon }} ${{ matrix.pyicon }} Unit Tests using Python ${{ matrix.python }}
runs-on: ${{ matrix.runs-on }}
strategy:
matrix:
include: ${{ fromJson(inputs.jobs) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: 🐍 Setup Python ${{ matrix.python }}
if: matrix.system != 'msys2'
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
:Debugging: The job prints debugging information like system |times| Python version |times| environment
combinations as well as the generated JSON array in the job's log.
.. code-block:: json
[
{"sysicon": "🐧", "system": "ubuntu", "runs-on": "ubuntu-24.04", "runtime": "native", "shell": "bash", "pyicon": "🔴", "python": "3.9", "envname": "Linux (x86-64)" },
{"sysicon": "🐧", "system": "ubuntu", "runs-on": "ubuntu-24.04", "runtime": "native", "shell": "bash", "pyicon": "🟠", "python": "3.10", "envname": "Linux (x86-64)" },
{"sysicon": "🐧", "system": "ubuntu", "runs-on": "ubuntu-24.04", "runtime": "native", "shell": "bash", "pyicon": "🟡", "python": "3.11", "envname": "Linux (x86-64)" },
{"sysicon": "🐧", "system": "ubuntu", "runs-on": "ubuntu-24.04", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.12", "envname": "Linux (x86-64)" },
{"sysicon": "🐧", "system": "ubuntu", "runs-on": "ubuntu-24.04", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.13", "envname": "Linux (x86-64)" },
{"sysicon": "🪟", "system": "windows", "runs-on": "windows-2025", "runtime": "native", "shell": "pwsh", "pyicon": "🔴", "python": "3.9", "envname": "Windows (x86-64)" },
{"sysicon": "🪟", "system": "windows", "runs-on": "windows-2025", "runtime": "native", "shell": "pwsh", "pyicon": "🟠", "python": "3.10", "envname": "Windows (x86-64)" },
{"sysicon": "🪟", "system": "windows", "runs-on": "windows-2025", "runtime": "native", "shell": "pwsh", "pyicon": "🟡", "python": "3.11", "envname": "Windows (x86-64)" },
{"sysicon": "🪟", "system": "windows", "runs-on": "windows-2025", "runtime": "native", "shell": "pwsh", "pyicon": "🟢", "python": "3.12", "envname": "Windows (x86-64)" },
{"sysicon": "🪟", "system": "windows", "runs-on": "windows-2025", "runtime": "native", "shell": "pwsh", "pyicon": "🟢", "python": "3.13", "envname": "Windows (x86-64)" },
{"sysicon": "🍎", "system": "macos", "runs-on": "macos-13", "runtime": "native", "shell": "bash", "pyicon": "🔴", "python": "3.9", "envname": "macOS (x86-64)" },
{"sysicon": "🍎", "system": "macos", "runs-on": "macos-13", "runtime": "native", "shell": "bash", "pyicon": "🟠", "python": "3.10", "envname": "macOS (x86-64)" },
{"sysicon": "🍎", "system": "macos", "runs-on": "macos-13", "runtime": "native", "shell": "bash", "pyicon": "🟡", "python": "3.11", "envname": "macOS (x86-64)" },
{"sysicon": "🍎", "system": "macos", "runs-on": "macos-13", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.12", "envname": "macOS (x86-64)" },
{"sysicon": "🍎", "system": "macos", "runs-on": "macos-13", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.13", "envname": "macOS (x86-64)" },
{"sysicon": "🍏", "system": "macos-arm", "runs-on": "macos-15", "runtime": "native", "shell": "bash", "pyicon": "🔴", "python": "3.9", "envname": "macOS (aarch64)" },
{"sysicon": "🍏", "system": "macos-arm", "runs-on": "macos-15", "runtime": "native", "shell": "bash", "pyicon": "🟠", "python": "3.10", "envname": "macOS (aarch64)" },
{"sysicon": "🍏", "system": "macos-arm", "runs-on": "macos-15", "runtime": "native", "shell": "bash", "pyicon": "🟡", "python": "3.11", "envname": "macOS (aarch64)" },
{"sysicon": "🍏", "system": "macos-arm", "runs-on": "macos-15", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.12", "envname": "macOS (aarch64)" },
{"sysicon": "🍏", "system": "macos-arm", "runs-on": "macos-15", "runtime": "native", "shell": "bash", "pyicon": "🟢", "python": "3.13", "envname": "macOS (aarch64)" },
{"sysicon": "🪟🟦", "system": "msys2", "runs-on": "windows-2025", "runtime": "MINGW64", "shell": "msys2 {0}", "pyicon": "🟢", "python": "3.12", "envname": "Windows+MSYS2 (x86-64) - MinGW64"},
{"sysicon": "🪟🟨", "system": "msys2", "runs-on": "windows-2025", "runtime": "UCRT64", "shell": "msys2 {0}", "pyicon": "🟢", "python": "3.12", "envname": "Windows+MSYS2 (x86-64) - UCRT64" }
]
.. _JOBTMPL/Parameters/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).
Nontheless, the generated output :ref:`JOBTMPL/Parameters/Output/python_jobs` is influenced by many input parameters
generating :math:`N^2` many Python job combinations, which in turn will influence the overall pipeline runtime and how many
jobs (parallel VMs) are needed to execute the matrix.
.. hint::
Some VM images (macOS, Windows) have parallelism limitations and run slower then Ubuntu-based jobs. Additionally,
environments like MSYS2 require an additional setup time increasing a jobs runtime significantly.

View File

@@ -0,0 +1,464 @@
.. _JOBTMPL/PrepareJob:
.. index::
single: GitHub Action Reusable Workflow; PrepareJob Template
PrepareJob
##########
The ``PrepareJob`` template is a workaround for the limitations of GitHub Actions to handle global variables in GitHub
Actions workflows (see `actions/runner#480 <https://github.com/actions/runner/issues/480>`__) as well as providing basic
string operations (see GitHub Action's `limited set of functions <https://docs.github.com/en/actions/reference/workflows-and-actions/expressions#functions>`__).
The job template generates various output parameters derived from
`${{ github }} context <https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#github-context>`__.
.. topic:: Features
* Provide branch name or tag name from ``${{ github.ref }}`` variable.
* Check if pipeline is a branch, tag or pull-request pipeline.
* Check if a commit is a merge commit.
* Check if a commit is a release commit.
* Check if a tag is a nightly tag (rolling release tag).
* Check if a tag is a release tag.
* Find associated pull-request (PR) for a merge commit/release commit.
* Provide a version from tag name or pull-request name.
.. note::
Due to GitHub Action's broken type system and missing implicit type conversions in YAML files, *boolean* values need
to be returned as *string* values otherwise type compatibility and comparison are broken. This also requires all
inputs to be *string* parameters, otherwise step's, job's or template's output cannot be assigned to a template's
input.
**Problems:**
1. Scripts (Bash, Python, ...) return results as strings. There is no option or operation provided by GitHub Actions
to convert outputs of ``printf`` to a ``boolean`` in the YAML file.
2. Unlike job template inputs, outputs have no type information. These are all considered *string* values. Again no
implicit or explicit type conversion is provided.
3. While inputs might be defined as ``boolean`` and a matching default can be set as a *boolean* value (e.g.,
``false``), a connected variable from ``${{ needs }}`` context will either cause a typing error or a later
comparison will not work as expected. Either the comparison works with ``inputs.param == false`` for the default
value, **or** it works with a value from ``${{ needs }}`` context, which is a string ``inputs.param == 'false'``.
.. topic:: Behavior
1. Checkout repository.
2. Classify ``${{ github.ref }}`` into branch, tag or pull-request.
3. Compute output parameters.
4. Find associated pull-request.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-PrepareJob.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`GitHub command line tool 'gh' <cli/cli>`
.. _JOBTMPL/PrepareJob/Instantiation:
Instantiation
*************
The following instantiation example creates a ``Prepare`` job derived from job template ``PrepareJob`` version ``@r6``.
In a default usecase, no input parameters need to be specified for the job template assuming a main-branch and
release-branch called ``main``, a development-branch called ``dev``, as well as semantic versioning for tags and
pull-request titles.
.. code-block:: yaml
jobs:
Prepare:
uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r6
<ReleaseJob>:
needs:
- Prepare
if: needs.Prepare.outputs.is_release_tag == 'true'
...
with:
version: ${{ needs.Prepare.outputs.version }}
.. seealso::
:ref:`JOBTMPL/TagReleaseCommit`
``PrepareJob`` is usually used to identify if a pipeline's commit is a merge commit created by a pull-request. If
so, this commit can be tagged automatically to trigger a release pipeline (tag pipeline) for the same commit
resulting in a full release (PyPI, GitHub Pages, GitHub Release, ...).
:ref:`JOBTMPL/PublishReleaseNotes`
``PrepareJob`` is usually used to identify if a tag pipeline is a release pipeline.
.. _JOBTMPL/PrepareJob/Parameters:
Parameter Summary
*****************
.. rubric:: Goto :ref:`input parameters <JOBTMPL/PrepareJob/Inputs>`
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=====================================================================+==========+==========+===================================================================+
| :ref:`JOBTMPL/PrepareJob/Input/ubuntu_image` | no | string | ``'ubuntu-24.04'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Input/main_branch` | no | string | ``'main'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Input/development_branch` | no | string | ``'dev'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Input/release_branch` | no | string | ``'main'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Input/nightly_tag_pattern` | no | string | ``'nightly'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern` | no | string | ``'(v|r)?[0-9]+(\.[0-9]+){0,2}(-(dev|alpha|beta|rc)([0-9]*))?'`` |
+---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/PrepareJob/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/PrepareJob/Outputs>`
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| Result Name | Type | Description |
+=====================================================================+==========+===================================================================+
| :ref:`JOBTMPL/PrepareJob/Output/on_main_branch` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/on_dev_branch` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/on_release_branch` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/is_regular_commit` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/is_merge_commit` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/is_release_commit` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/is_nightly_tag` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/is_release_tag` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/ref_kind` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/branch` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/tag` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/version` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/pr_title` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
| :ref:`JOBTMPL/PrepareJob/Output/pr_number` | string | |
+---------------------------------------------------------------------+----------+-------------------------------------------------------------------+
.. _JOBTMPL/PrepareJob/Inputs:
Input Parameters
****************
.. _JOBTMPL/PrepareJob/Input/ubuntu_image:
ubuntu_image
============
:Type: string
:Required: no
:Default Value: ``'ubuntu-24.04'``
:Possible Values: See `actions/runner-images - Available Images <https://github.com/actions/runner-images?tab=readme-ov-file#available-images>`__
for available Ubuntu image versions.
:Description: Name of the Ubuntu image used to run this job.
.. _JOBTMPL/PrepareJob/Input/main_branch:
main_branch
===========
:Type: string
:Required: no
:Default Value: ``'main'``
:Possible Values: Any valid branch name.
:Description: Name of the main branch.
.. _JOBTMPL/PrepareJob/Input/development_branch:
development_branch
==================
:Type: string
:Required: no
:Default Value: ``'dev'``
:Possible Values: Any valid branch name.
:Description: Name of the development branch.
.. _JOBTMPL/PrepareJob/Input/release_branch:
release_branch
==============
:Type: string
:Required: no
:Default Value: ``'main'``
:Possible Values: Any valid branch name.
:Description: Name of the branch containing releases.
.. _JOBTMPL/PrepareJob/Input/nightly_tag_pattern:
nightly_tag_pattern
===================
:Type: string
:Required: no
:Default Value: ``'nightly'``
:Possible Values: Any valid regular expression. |br|
Suggested alternative values: ``latest``, ``rolling``
:Description: Name of the tag used for rolling releases, a.k.a nightly builds.
.. _JOBTMPL/PrepareJob/Input/release_tag_pattern:
release_tag_pattern
===================
:Type: string
:Required: no
:Default Value: ``'(v|r)?[0-9]+(\.[0-9]+){0,2}(-(dev|alpha|beta|rc)([0-9]*))?'``
:Possible Values: Any valid regular expression.
:Description: A regular expression describing a pattern for identifying a release tag.
The default pattern matches on a `semantic version number <https://semver.org/>`__ separated by dots.
It supports up to 3 digit groups. It accepts an optional ``v`` or ``r`` prefix. Optionally, a postfix
of ``dev``, ``alpha``, ``beta`` or ``rc`` separated by a hyphen can be appended. If needed, the
postfix can have a digit group.
**Matching tag names as releases:**
* ``v1``, ``r1``
* ``1``, ``1.1``, ``1.1.1``
* ``v1.2.8-dev``
* ``v3.13.5-alpha2``
* ``v4.7.22-beta3``
* ``v10.2-rc1``
.. _JOBTMPL/PrepareJob/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/PrepareJob/Outputs:
Outputs
*******
.. _JOBTMPL/PrepareJob/Output/on_main_branch:
on_main_branch
==============
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is on :ref:`main branch <JOBTMPL/PrepareJob/Input/main_branch>`,
otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/on_dev_branch:
on_dev_branch
=============
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is on :ref:`development branch <JOBTMPL/PrepareJob/Input/development_branch>`,
otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/on_release_branch:
on_release_branch
=================
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is on :ref:`release branch <JOBTMPL/PrepareJob/Input/release_branch>`,
otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/is_regular_commit:
is_regular_commit
=================
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is not a :ref:`merge commit <JOBTMPL/PrepareJob/Output/is_merge_commit>`
nor :ref:`release commit <JOBTMPL/PrepareJob/Output/is_release_commit>`, otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/is_merge_commit:
is_merge_commit
===============
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is on :ref:`main branch <JOBTMPL/PrepareJob/Input/main_branch>`
or :ref:`development branch <JOBTMPL/PrepareJob/Input/development_branch>` and has more than one
parent (merge commit), otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/is_release_commit:
is_release_commit
=================
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline's commit is on :ref:`release branch <JOBTMPL/PrepareJob/Input/release_branch>`
and has more than one parent (merge commit), otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/is_nightly_tag:
is_nightly_tag
==============
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline is a tag pipeline for a commit on :ref:`release branch <JOBTMPL/PrepareJob/Input/release_branch>`
and the tag's name matches the :ref:`nightly tag pattern <JOBTMPL/PrepareJob/Input/nightly_tag_pattern>`,
otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/is_release_tag:
is_release_tag
==============
:Type: string
:Default Value: ``'false'``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns ``'true'`` if the pipeline is a tag pipeline for a commit on :ref:`release branch <JOBTMPL/PrepareJob/Input/release_branch>`
and the tag's name matches the :ref:`release tag pattern <JOBTMPL/PrepareJob/Input/release_tag_pattern>`,
otherwise return ``'false'``.
.. _JOBTMPL/PrepareJob/Output/ref_kind:
ref_kind
========
:Type: string
:Default Value: ``'unknown'``
:Possible Values: ``'branch'``, ``'tag'``, ``'pullrequest'``, ``'unknown'``
:Description: Returns ``'branch'`` if pipeline's commit is on a branch or returns ``'tag'`` if the pipeline runs for
a tagged commit, otherwise returns ``'unknown'`` in case of an internal error.
If the kind is a branch, the branch name is available in the job's :ref:`JOBTMPL/PrepareJob/Output/branch`
result. |br|
If the kind is a tag, the tags name is available in the job's :ref:`JOBTMPL/PrepareJob/Output/tag`
result. |br|
If the kind is a pull-request, the pull request's id is available in the job's :ref:`JOBTMPL/PrepareJob/Output/pr_number`
result. |br|
Moreover, if the tag matches the :ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`, the extracted
version is available in the job's :ref:`JOBTMPL/PrepareJob/Output/version` result.
.. note::
GitHub doesn't provide standalone branch or tag information, but provides the variable
``${{ github.ref }}`` specifying the currently active reference (branch, tag, pull, ...). This job
template parses the context's variable and derives if a pipeline runs for a commit on a branch or a
tagged commit.
.. _JOBTMPL/PrepareJob/Output/branch:
branch
======
:Type: string
:Default Value: ``''``
:Possible Values: Any valid branch name.
:Description: Returns the branch's name the pipeline's commit is associated to, if :ref:`JOBTMPL/PrepareJob/Output/ref_kind`
is ``'branch'``, otherwise returns an empty string ``''``.
.. _JOBTMPL/PrepareJob/Output/tag:
tag
===
:Type: string
:Default Value: ``''``
:Possible Values: Any valid tag name.
:Description: Returns the tag's name the pipeline's commit is associated to, if :ref:`JOBTMPL/PrepareJob/Output/ref_kind`
is ``'tag'``, otherwise returns an empty string ``''``.
.. _JOBTMPL/PrepareJob/Output/version:
version
=======
:Type: string
:Default Value: ``''``
:Possible Values: Any valid version matching :ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`.
:Description: In case the pipeline runs for a tag, it returns the tag's name, if the name matches
:ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`, otherwise returns an empty string ``''``. |br|
In case the pipeline runs for a branch, then the commit is checked if it's a
:ref:`merge commit <JOBTMPL/PrepareJob/Output/is_merge_commit>` and corresponding pull-request (PR) is
searched. When a matching PR can be located and it's title matches
:ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`, then this title is returned as a version,
otherwise it returns an empty string ``''``.
.. _JOBTMPL/PrepareJob/Output/pr_title:
pr_title
========
:Type: string
:Default Value: ``''``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns the associated pull-request's title, if the pipeline's commit is a
:ref:`merge commit <JOBTMPL/PrepareJob/Output/is_merge_commit>` and the located pull-request's title
for this commit matches :ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`, otherwise returns an
empty string ``''``.
.. _JOBTMPL/PrepareJob/Output/pr_number:
pr_number
=========
:Type: string
:Default Value: ``''``
:Possible Values: ``'true'``, ``'false'``
:Description: Returns the associated pull-request's number, if the pipeline's commit is a
:ref:`merge commit <JOBTMPL/PrepareJob/Output/is_merge_commit>` and the located pull-request's title
for this commit matches :ref:`JOBTMPL/PrepareJob/Input/release_tag_pattern`, otherwise returns an
empty string ``''``.
.. _JOBTMPL/PrepareJob/Optimizations:
Optimizations
*************
This template offers no optimizations (reduced job runtime).

View File

@@ -0,0 +1,21 @@
.. _JOBTMPL/Setup:
Pipeline Setup
##############
The category *pipeline setup* provides workflow templates implementing preparation steps suitable for every pipeline.
* :ref:`JOBTMPL/Parameters` - Compute a matrix of (operating system |times| Python version |times| environment)
combinations for unit testing, platform testing or application testing and provide the result as a JSON string via
pipeline outputs.
* :ref:`JOBTMPL/PrepareJob` - Check GitHub Action environment variables and commits to provide precomputed variables as
pipeline outputs.
* :ref:`JOBTMPL/ExtractConfiguration` - Extract configuration settings from :file:`pyproject.toml` and provide settings
as pipeline outputs.
.. toctree::
:hidden:
PrepareJob
Parameters
ExtractConfiguration

View File

@@ -1,182 +0,0 @@
.. _JOBTMPL/StaticTypeChecking:
StaticTypeCheck
###############
This job runs a static type check using mypy and collects the results. These results can be converted to a HTML report
and then uploaded as an artifact.
**Behavior:**
1. Checkout repository
2. Setup Python and install dependencies
3. Run type checking command(s).
4. Upload type checking report as an artifact
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`actions/setup-python`
* :gh:`actions/upload-artifact`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0
with:
commands: |
touch pyTooling/__init__.py
mypy --html-report htmlmypy -p pyTooling
report: 'htmlmypy'
artifact: TypeChecking
Complex Example
===============
.. code-block:: yaml
jobs:
StaticTypeCheck:
uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0
needs:
- Params
with:
python_version: ${{ needs.Params.outputs.python_version }}
commands: |
touch pyTooling/__init__.py
mypy --html-report htmlmypy -p pyTooling
report: 'htmlmypy'
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }}
Commands
========
Example ``commands``:
1. Regular package
.. code-block:: yaml
commands: mypy --html-report htmlmypy -p ToolName
2. Parent namespace package
.. code-block:: yaml
commands: |
touch Parent/__init__.py
mypy --html-report htmlmypy -p ToolName
3. Child namespace package
.. code-block:: yaml
commands: |
cd Parent
mypy --html-report ../htmlmypy -p ToolName
Parameters
**********
python_version
==============
+----------------+----------+----------+-----------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================+
| python_version | optional | string | ``3.11`` |
+----------------+----------+----------+-----------------+
Python version.
requirements
============
+----------------+----------+----------+-------------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+===============================+
| requirements | optional | string | ``-r tests/requirements.txt`` |
+----------------+----------+----------+-------------------------------+
Python dependencies to be installed through pip.
commands
========
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| commands | yes | string | — — — — |
+----------------+----------+----------+--------------+
Commands to run the static type checks.
html_report
===========
+----------------+----------+----------+-----------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================+
| report | optional | string | ``htmlmypy`` |
+----------------+----------+----------+-----------------+
HTML output directory to upload as an artifact.
junit_report
============
+----------------+----------+----------+-----------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=============================+
| report | optional | string | ``StaticTypingSummary.xml`` |
+----------------+----------+----------+-----------------------------+
junit file to upload as an artifact.
html_artifact
=============
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| html_artifact | yes | string | — — — — |
+----------------+----------+----------+--------------+
Name of the typing artifact (HTML report).
junit_artifact
==============
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| junit_artifact | optional | string | ``""`` |
+----------------+----------+----------+--------------+
Name of the typing junit artifact (junit XML).
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -0,0 +1,30 @@
.. rubric:: Possible values
* Native systems: ``ubuntu``, ``windows``, ``macos``
* MSYS2: ``msys``, ``mingw32``, ``mingw64``, ``clang32``, ``clang64``, ``ucrt64``
+------+-----------+------------------------------+-----------------------------------------------------------------+
| Icon | System | Used version | Comments |
+======+===========+==============================+=================================================================+
| 🪟 | Windows | Windows Server 2025 (latest) | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🐧 | Ubuntu | Ubuntu 24.04 (LTS) (latest) | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🍎 | macOS | macOS Ventura 13 (latest) | While this marked latest, macOS Ventura 13 is already provided. |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🍏 | macOS-arm | macOS Sonoma 14 (latest) | While this marked latest, macOS Ventura 13 is already provided. |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟪 | MSYS | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| ⬛ | MinGW32 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟦 | MinGW64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟫 | Clang32 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟧 | Clang64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
| 🟨 | UCRT64 | | |
+------+-----------+------------------------------+-----------------------------------------------------------------+
Source: `Images provided by GitHub <https://github.com/actions/runner-images>`__

View File

@@ -0,0 +1,77 @@
.. grid:: 5
.. grid-item::
:columns: 2
.. rubric:: All-In-One
* :ref:`JOBTMPL/CompletePipeline` |br| |br| |br| |br|
.. rubric:: Pipeline Setup
* :ref:`JOBTMPL/Parameters`
* :ref:`JOBTMPL/PrepareJob`
* :ref:`JOBTMPL/ExtractConfiguration`
.. grid-item::
:columns: 2
.. rubric:: Documentation
* :ref:`JOBTMPL/CheckDocumentation`
* :ref:`JOBTMPL/SphinxDocumentation`
* :ref:`JOBTMPL/LaTeXDocumentation`
.. #* :ref:`JOBTMPL/VerifyDocs`
.. rubric:: Unit Tests, Code Coverage
* :ref:`JOBTMPL/ApplicationTesting`
* :ref:`JOBTMPL/UnitTesting`
.. grid-item::
:columns: 2
.. rubric:: Code Quality
* :ref:`JOBTMPL/StaticTypeCheck`
* *code formatting (planned)*
* *coding style (planned)*
* *code linting (planned)*
.. rubric:: Build and Packaging
* :ref:`JOBTMPL/Package`
* :ref:`JOBTMPL/InstallPackage`
.. grid-item::
:columns: 2
.. rubric:: Publishing
* :ref:`JOBTMPL/PublishOnPyPI`
* :ref:`JOBTMPL/PublishTestResults`
* :ref:`JOBTMPL/PublishCoverageResults`
* :ref:`JOBTMPL/PublishToGitHubPages`
.. rubric:: Releasing
* :ref:`JOBTMPL/PublishReleaseNotes`
* :ref:`JOBTMPL/TagReleaseCommit`
.. grid-item::
:columns: 2
.. rubric:: Cleanup
* :ref:`JOBTMPL/IntermediateCleanup`
* :ref:`JOBTMPL/ArtifactCleanup`
.. grid-item::
:columns: 2
.. rubric:: :ref:`JOBTMPL/Deprecated`
* :ref:`JOBTMPL/CoverageCollection`
* :ref:`JOBTMPL/NightlyRelease`
* :ref:`JOBTMPL/BuildTheDocs`

View File

@@ -0,0 +1,6 @@
.. _JOBTMPL/ApplicationTesting:
ApplicationTesting (idea)
#########################
.. todo:: ApplicationTesting:: Needs documentation.

View File

@@ -0,0 +1,756 @@
.. _JOBTMPL/UnitTesting:
.. index::
single: pytest; UnitTesting Template
single: Coverage.py; UnitTesting Template
single: GitHub Action Reusable Workflow; UnitTesting Template
UnitTesting
###########
This template runs multiple jobs from a matrix as a cross of Python versions and systems. The summary report in junit
XML format is optionally uploaded as an artifact.
Configuration options to :term:`pytest` should be given via section ``[tool.pytest.ini_options]`` in a
``pyproject.toml`` file.
.. topic:: Features
* Execute unit tests using :term:`pytest`.
* Provide unit test results as JUnit XML file (pyTest XML dialect).
* Collect code coverage using :term:`Coverage.py`.
* Provide code coverage results as pytest SQLite database.
* Provide code coverage results as Cobertura XML file.
* Provide code coverage results as pytest JSON file.
* Provide code coverage results as HTML report.
.. topic:: Behavior
1. Checkout repository.
2. Setup environment and install dependencies (``apt``, ``homebrew``, ``pacman``, ...).
3. Setup Python and install dependencies (:term:`pip`).
4. Run instructions from ``*_before_script`` parameter.
5. Run unit tests using *pytest* and if enabled in combination with *Coverage.py*.
6. Convert gathered results to other formats.
7. Upload results (test reports, code coverage reports, ...) as an artifacts.
.. topic:: Job Execution
.. image:: ../../_static/pyTooling-Actions-UnitTesting.png
:width: 600px
.. topic:: Dependencies
* :gh:`actions/checkout`
* :gh:`msys2/setup-msys2`
* :gh:`actions/setup-python`
* :gh:`pyTooling/download-artifact`
* :gh:`actions/download-artifact`
* :gh:`pyTooling/upload-artifact`
* :gh:`actions/upload-artifact`
* apt: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/apt` parameter.
* homebrew: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/brew` parameter.
* MSYS2: Packages specified via :ref:`JOBTMPL/UnitTesting/Input/pacboy` parameter.
* pip
* :pypi:`wheel`
* :pypi:`tomli`
* Python packages specified via :ref:`JOBTMPL/UnitTesting/Input/requirements` or
:ref:`JOBTMPL/UnitTesting/Input/mingw_requirements` parameter.
.. _JOBTMPL/UnitTesting/Instantiation:
Instantiation
*************
The following instantiation example creates a ``UnitTesting`` job derived from job template ``UnitTesting`` version
`@r6`. For providing the job matrix as a JSON string, the :ref:`JOBTMPL/Parameters` job template is used. Additionally,
the job needs configuration settings, which are stored in :file:`pyproject.toml`. Instead of duplicating these settings,
the :ref:`JOBTMPL/ExtractConfiguration` job template is used to extract these settings.
.. code-block:: yaml
jobs:
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTestingParams:
uses: pyTooling/Actions/.github/workflows/Parameters.yml@r6
with:
package_name: myPackage
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
- UnitTestingParams
with:
jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }}
requirements: '-r tests/unit/requirements.txt'
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }}
coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }}
.. seealso::
:ref:`JOBTMPL/Parameters`
``Parameters`` is usually used to pre-compute the job matrix as a JSON string with all system |times| environment
|times| Python version combinations.
:ref:`JOBTMPL/PublishTestResults`
``PublishTestResults`` can be used to merge all JUnit test reports into one file.
:ref:`JOBTMPL/PublishCoverageResults`
``PublishCoverageResults`` can be used to merge all code coverage reports into one file.
.. _JOBTMPL/UnitTesting/Parameters:
Parameter Summary
*****************
.. # |unittest_report_xml| code-block:: json
{ "directory": "report/unit",
"filename": "TestReportSummary.xml",
"fullpath": "report/unit/TestReportSummary.xml"
}
.. rubric:: Goto :ref:`input parameters <JOBTMPL/UnitTesting/Inputs>`
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| Parameter Name | Required | Type | Default |
+=========================================================================+==========+==========+===================================================================================================================================+
| :ref:`JOBTMPL/UnitTesting/Input/jobs` | yes | string | — — — — |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/apt` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/brew` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/pacboy` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/requirements` | no | string | ``'-r tests/requirements.txt'`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/mingw_requirements` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/macos_before_script` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/macos_arm_before_script` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/ubuntu_before_script` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/mingw64_before_script` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/ucrt64_before_script` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/root_directory` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/tests_directory` | no | string | ``'tests'`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/unittest_directory` | no | string | ``'unit'`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/unittest_report_xml` | no | string | :jsoncode:`{"directory": "report/unit", "filename": "TestReportSummary.xml", "fullpath": "report/unit/TestReportSummary.xml"}` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_config` | no | string | ``'pyproject.toml'`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_report_xml` | no | string | :jsoncode:`{"directory": "report/coverage", "filename": "coverage.xml", "fullpath": "report/coverage/coverage.xml"}` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_report_json` | no | string | :jsoncode:`{"directory": "report/coverage", "filename": "coverage.json", "fullpath": "report/coverage/coverage.json"}` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_report_html` | no | string | :jsoncode:`{"directory": "report/coverage"}` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/unittest_xml_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/unittest_html_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_sqlite_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_xml_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_json_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| :ref:`JOBTMPL/UnitTesting/Input/coverage_html_artifact` | no | string | ``''`` |
+-------------------------------------------------------------------------+----------+----------+-----------------------------------------------------------------------------------------------------------------------------------+
.. rubric:: Goto :ref:`secrets <JOBTMPL/UnitTesting/Secrets>`
This job template needs no secrets.
.. rubric:: Goto :ref:`output parameters <JOBTMPL/UnitTesting/Outputs>`
This job template has no output parameters.
.. _JOBTMPL/UnitTesting/Inputs:
Input Parameters
****************
.. _JOBTMPL/UnitTesting/Input/jobs:
jobs
====
:Type: string
:Required: yes
:Default Value: — — — —
:Possible Values: A JSON string with an array of dictionaries with the following key-value pairs:
:sysicon: icon to display
:system: name of the system
:runs-on: virtual machine image and base operating system
:runtime: name of the runtime environment if not running natively on the VM image
:shell: name of the shell
:pyicon: icon for CPython or pypy
:python: Python version
:envname: full name of the selected environment
:Description: A JSON encoded job matrix to run multiple Python job variations.
.. _JOBTMPL/UnitTesting/Input/apt:
apt
===
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``apt install``. |br|
Packages are specified as a space separated list like ``'graphviz curl gzip'``.
:Description: Additional Ubuntu system dependencies to be installed through *apt*.
:Example:
.. code-block:: yaml
UnitTests:
...
with:
apt: >-
graphviz
curl
gzip
.. _JOBTMPL/UnitTesting/Input/brew:
brew
====
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``brew install``. |br|
Packages are specified as a space separated list.
:Description: Additional macOS system dependencies to be installed through *brew*.
.. _JOBTMPL/UnitTesting/Input/pacboy:
pacboy
======
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``pacboy``. |br|
Packages are specified as a space separated list like ``'python-lxml:p python-numpy:p'``.
:Description: Additional MSYS2 system dependencies to be installed through *pacboy* (*pacman*). |br|
Usually, Python packages start with ``python-``. The suffix ``:p`` ensures pacboy figures out the
correct package repository prefix for MinGW64, UCRT64, ...
.. note::
Internally, a dedicated workflow step reads the :ref:`JOBTMPL/UnitTesting/Input/requirements` file
for Python and compares requested packages with a list of packages that should be installed through
*pacman*/*pacboy* compared to installation via *pip*. These are mainly core packages or packages
with embedded C code. |br|
The list of identified packages is handed over to *pacboy* for preinstallation. Otherwise *pip*
will later raise an error. |br|
The packages listed by this parameter will be installed in addition to the identified packages.
.. attention::
Ensure your Python requirements match the available version from MSYS2 packages list, otherwise
if your :file:`requirements.txt` requests a newer version then provided by MSYS2, such a dependency
will fail.
:Example:
.. code-block:: yaml
UnitTests:
...
with:
pacboy: >-
python-lxml:p
:Packages: The following list of Python packages is identified to be installed via *pacboy*:
* :ucrt64:`python-coverage` |rarr| :pypi:`coverage`
* :ucrt64:`igraph` |rarr| :pypi:`igraph`
* :ucrt64:`python-lxml` |rarr| :pypi:`lxml`
* :ucrt64:`python-markupsafe` |rarr| :pypi:`markupsafe`
* :ucrt64:`python-numpy` |rarr| :pypi:`numpy`
* :ucrt64:`python-pip` |rarr| :pypi:`pip`
* :ucrt64:`python-pyaml` |rarr| :pypi:`pyaml`
* :ucrt64:`python-ruamel-yaml` |rarr| :pypi:`ruamel-yaml`
* :ucrt64:`python-wheel` |rarr| :pypi:`wheel`
* :ucrt64:`python-tomli` |rarr| :pypi:`tomli`
* :ucrt64:`python-types-pyyaml` |rarr| :pypi:`types.pyyaml`
.. _JOBTMPL/UnitTesting/Input/requirements:
requirements
============
:Type: string
:Required: no
:Default Value: ``'-r tests/requirements.txt'``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'coverage pytest'``.
:Description: Python dependencies to be installed through *pip*.
.. _JOBTMPL/UnitTesting/Input/mingw_requirements:
mingw_requirements
==================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid list of parameters for ``pip install``. |br|
Either a requirements file can be referenced using ``'-r path/to/requirements.txt'``, or a list of
packages can be specified using a space separated list like ``'coverage pytest'``.
:Description: Override Python dependencies to be installed through *pip* in MSYS2 (MinGW64/UCRT64) only.
.. _JOBTMPL/UnitTesting/Input/macos_before_script:
macos_before_script
===================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid *Bash* instructions as single-line or multi-line string suitable for macOS (Intel platform).
:Description: These optional *Bash* instructions for macOS are executed after setting up the environment and
installing the platform specific dependencies and before running the unit test.
.. _JOBTMPL/UnitTesting/Input/macos_arm_before_script:
macos_arm_before_script
=======================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid *Bash* instructions as single-line or multi-line string suitable for macOS (ARM platform).
:Description: These optional *Bash* instructions for macOS are executed after setting up the environment and
installing the platform specific dependencies and before running the unit test.
.. _JOBTMPL/UnitTesting/Input/ubuntu_before_script:
ubuntu_before_script
====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid *Bash* instructions as single-line or multi-line string suitable for Ubuntu.
:Description: These optional *Bash* instructions for Ubuntu are executed after setting up the environment and
installing the platform specific dependencies and before running the unit test.
.. _JOBTMPL/UnitTesting/Input/mingw64_before_script:
mingw64_before_script
=====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid *Bash* instructions as single-line or multi-line string suitable for MinGW64 on Windows.
:Description: These optional *Bash* instructions for MinGW64 on Windows are executed after setting up the
environment and installing the platform specific dependencies and before running the unit test.
.. _JOBTMPL/UnitTesting/Input/ucrt64_before_script:
ucrt64_before_script
====================
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid *Bash* instructions as single-line or multi-line string suitable for UCRT64 on Windows.
:Description: These optional *Bash* instructions for UCRT64 on Windows are executed after setting up the
environment and installing the platform specific dependencies and before running the unit test.
.. hint::
The next parameters allow running different test kinds (unit tests, performance tests, platform tests, ...) with the
same job template, but isolated in sub-directories, thus pytest only discovers a subset of tests. The following code
blocks showcase how the job template uses these parameters and how it relates to a proposed directory structure.
.. grid:: 3
.. grid-item::
:columns: 5
.. card:: Relation between :ref:`JOBTMPL/UnitTesting/Input/root_directory`, :ref:`JOBTMPL/UnitTesting/Input/tests_directory` and :ref:`JOBTMPL/UnitTesting/Input/unittest_directory`
.. code-block:: bash
cd <RepositoryRoot>
cd ${root_directory}
python -m \
pytest -raP \
--color=yes ..... \
"${tests_directory}/${unittest_directory}"
.. grid-item::
:columns: 3
.. card:: Directory Structure
.. code-block::
<RepositoryRoot>/
doc/
myPackage/
__init__.py
tests/
unit/
myTests.py
.. grid-item::
:columns: 3
.. card:: Example for Default Values
.. code-block:: bash
cd <RepositoryRoot>
cd .
python -m \
pytest -raP \
--color=yes ..... \
"tests/unit"
.. _JOBTMPL/UnitTesting/Input/root_directory:
root_directory
==============
:Type: string
:Required: no
:Default Value: ``''``
:Possible Values: Any valid directory or sub-directory.
:Description: Working directory for running tests. |br|
Usually, this is the repository's root directory. Tests are called relatively from here. See
:ref:`JOBTMPL/UnitTesting/Input/tests_directory` and :ref:`JOBTMPL/UnitTesting/Input/unittest_directory`.
.. _JOBTMPL/UnitTesting/Input/tests_directory:
tests_directory
===============
:Type: string
:Required: no
:Default Value: ``'tests'``
:Possible Values: Any valid directory or sub-directory.
:Description: Path to the directory containing tests (relative from :ref:`JOBTMPL/UnitTesting/Input/root_directory`).
.. _JOBTMPL/UnitTesting/Input/unittest_directory:
unittest_directory
==================
:Type: string
:Required: no
:Default Value: ``'unit'``
:Possible Values: Any valid directory or sub-directory.
:Description: Path to the directory containing unit tests (relative from :ref:`JOBTMPL/UnitTesting/Input/tests_directory`).
.. _JOBTMPL/UnitTesting/Input/unittest_report_xml:
unittest_report_xml
===================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/unit",
"filename": "UnittestReportSummary.xml",
"fullpath": "reports/unit/UnittestReportSummary.xml"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the unittest summary report in XML format will be saved.
:filename: Filename of the generated JUnit XML report. |br|
Any valid filename accepted by ``pytest ... --junitxml=${unittest_report_xml}``.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the unittest summary report in XML format will
be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
with:
...
unittest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }}
.. _JOBTMPL/UnitTesting/Input/coverage_config:
coverage_config
===============
:Type: string
:Required: no
:Default Value: ``'pyproject.toml'``
:Possible Values: TBD
.. _JOBTMPL/UnitTesting/Input/coverage_report_xml:
coverage_report_xml
===================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.xml",
"fullpath": "reports/coverage/coverage.xml"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in Cobertura XML format will be
saved.
:filename: Filename of the generated Cobertura XML report. |br|
Any valid XML filename.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the code coverage report in Cobertura XML format
will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_xml: ${{ needs.ConfigParams.outputs.coverage_report_xml }}
.. _JOBTMPL/UnitTesting/Input/coverage_report_json:
coverage_report_json
====================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage",
"filename": "coverage.json",
"fullpath": "reports/coverage/coverage.json"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in Coverage.py's JSON format
will be saved.
:filename: Filename of the generated Coverage.py JSON report. |br|
Any valid JSON filename.
:fullpath: The concatenation of both previous fields using the ``/`` separator.
:Description: Directory, filename and fullpath as JSON object where the code coverage report in Coverage.py's JSON
format will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_json: ${{ needs.ConfigParams.outputs.coverage_report_json }}
.. _JOBTMPL/UnitTesting/Input/coverage_report_html:
coverage_report_html
====================
:Type: string (JSON)
:Required: no
:Default Value:
.. code-block:: json
{ "directory": "reports/coverage/html"
}
:Possible Values: Any valid JSON string containing a JSON object with fields:
:directory: Directory or sub-directory where the code coverage report in HTML format will be saved.
:Description: Directory as JSON object where the code coverage report in HTML format will be saved. |br|
This path is configured in :file:`pyproject.toml` and can be extracted by
:ref:`JOBTMPL/ExtractConfiguration`.
:Example:
.. code-block:: yaml
ConfigParams:
uses: pyTooling/Actions/.github/workflows/ExtractConfiguration.yml@r6
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r6
needs:
- ConfigParams
with:
...
coverage_report_html: ${{ needs.ConfigParams.outputs.coverage_report_html }}
.. _JOBTMPL/UnitTesting/Input/unittest_xml_artifact:
unittest_xml_artifact
=====================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the unittest report summary in XML format.
.. _JOBTMPL/UnitTesting/Input/unittest_html_artifact:
unittest_html_artifact
======================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the unittest report in HTML format.
.. _JOBTMPL/UnitTesting/Input/coverage_sqlite_artifact:
coverage_sqlite_artifact
========================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the code coverage report as SQLite database.
.. _JOBTMPL/UnitTesting/Input/coverage_xml_artifact:
coverage_xml_artifact
=====================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the code coverage report in XML format.
.. _JOBTMPL/UnitTesting/Input/coverage_json_artifact:
coverage_json_artifact
======================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the code coverage report in JSON format.
.. _JOBTMPL/UnitTesting/Input/coverage_html_artifact:
coverage_html_artifact
======================
:Type: string
:Required: no
:Possible Values: Any valid artifact name.
:Description: Name of the artifact containing the code coverage report in HTML format.
.. _JOBTMPL/UnitTesting/Secrets:
Secrets
*******
This job template needs no secrets.
.. _JOBTMPL/UnitTesting/Outputs:
Outputs
*******
This job template has no output parameters.
.. _JOBTMPL/UnitTesting/Optimizations:
Optimizations
*************
The following optimizations can be used to reduce the template's runtime.
Disable unit test XML generation
If parameter :ref:`JOBTMPL/UnitTesting/Input/unittest_xml_artifact` is empty, no unit test summary report will be
generated and no JUnit XML artifact will be uploaded.
Disabled code coverage collection
If parameter :ref:`JOBTMPL/UnitTesting/Input/coverage_config` is empty, no code coverage will be collected.
Disable code coverage SQLite database artifact upload
If parameter :ref:`JOBTMPL/UnitTesting/Input/coverage_sqlite_artifact` is empty, the collected code coverage database
(SQLlite format) wont be uploaded as an artifact.
Disable code coverage report conversion to the Cobertura XML format.
If parameter :ref:`JOBTMPL/UnitTesting/Input/coverage_xml_artifact` is empty, no Cobertura XML file will be generated
from code coverage report. As no Cobertura XML file exists, no code coverage XML artifact will be uploaded.
Disable code coverage report conversion to the *Coverage.py* JSON format.
If parameter :ref:`JOBTMPL/UnitTesting/Input/coverage_json_artifact` is empty, no *Coverage.py* JSON file will be
generated from code coverage report. As no JSON file exists, no code coverage JSON artifact will be uploaded.
Disable code coverage report conversion to an HTML website.
If parameter :ref:`JOBTMPL/UnitTesting/Input/coverage_html_artifact` is empty, no coverage report HTML report will be
generated from code coverage report. As no HTML report exists, no code coverage HTML artifact will be uploaded.

View File

@@ -0,0 +1,15 @@
.. _JOBTMPL/Testing:
Testing
#######
The category *testing* provides workflow templates implementing
* :ref:`JOBTMPL/UnitTesting` - Run unit tests and collect code coverage.
* :ref:`JOBTMPL/ApplicationTesting` - Run application tests.
.. toctree::
:hidden:
UnitTesting
ApplicationTesting

View File

@@ -1,159 +0,0 @@
.. _JOBTMPL/UnitTesting:
UnitTesting
###########
This template runs multiple jobs from a matrix as a cross of Python versions and systems. The summary report in junit
XML format is optionally uploaded as an artifact.
Configuration options to ``pytest`` should be given via section ``[tool.pytest.ini_options]`` in a ``pyproject.toml``
file.
**Behavior:**
1. Checkout repository
2. Setup Python and install dependencies
3. Run unit tests using ``pytest``.
4. Upload junit test summary as an artifact
**Dependencies:**
* :gh:`actions/checkout`
* :gh:`msys2/setup-msys2`
* :gh:`actions/setup-python`
* :gh:`actions/upload-artifact`
Instantiation
*************
Simple Example
==============
.. code-block:: yaml
jobs:
Params:
# ...
UnitTesting:
uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r0
needs:
- Params
with:
jobs: ${{ needs.Params.outputs.python_jobs }}
artifact: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting }}
Complex Example
===============
.. code-block:: yaml
TBD
Parameters
**********
jobs
====
+----------------+----------+----------+--------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==============+
| jobs | yes | string | — — — — |
+----------------+----------+----------+--------------+
JSON list with environment fields, telling the system and Python versions to run tests with.
requirements
============
+----------------+----------+----------+---------------------------------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+=================================+
| requirements | optional | string | ``-r tests/requirements.txt`` |
+----------------+----------+----------+---------------------------------+
Python dependencies to be installed through pip.
pacboy
======
+----------------+----------+----------+-----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+===========+
| pacboy | optional | string | ``""`` |
+----------------+----------+----------+-----------+
Additional MSYS2 dependencies to be installed through pacboy (pacman).
Internally, a workflow step reads the requirements file for Python and compares requested packages with a list of
packages that should be installed through pacman/pacboy compared to installation via pip. These are mainly core packages
or packages with embedded C code.
.. code-block:: yaml
pacboy: >-
python-lxml:p
mingw_requirements
==================
+--------------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+====================+==========+==========+==========+
| mingw_requirements | optional | string | ``""`` |
+--------------------+----------+----------+----------+
Override Python dependencies to be installed through pip on MSYS2 (MINGW64) only.
tests_directory
===============
+-----------------+----------+----------+-----------+
| Parameter Name | Required | Type | Default |
+=================+==========+==========+===========+
| tests_directory | optional | string | ``tests`` |
+-----------------+----------+----------+-----------+
Path to the directory containing tests (test working directory).
unittest_directory
==================
+--------------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+====================+==========+==========+==========+
| unittest_directory | optional | string | ``unit`` |
+--------------------+----------+----------+----------+
Path to the directory containing unit tests (relative to tests_directory).
artifact
========
+----------------+----------+----------+----------+
| Parameter Name | Required | Type | Default |
+================+==========+==========+==========+
| artifact | optional | string | ``""`` |
+----------------+----------+----------+----------+
Generate unit test report with junitxml and upload results as an artifact.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

View File

@@ -1,42 +0,0 @@
.. _JOBTMPL/VerifyDocumentation:
VerifyDocs
##########
This job extracts code examples from the README and tests these code snippets.
**Behavior:**
TBD
**Dependencies:**
TBD
Instantiation
*************
Simple Example
==============
.. todo:: VerifyDocs:SimpleExample Needs documentation.
Complex Example
===============
.. todo:: VerifyDocs:ComplexExample Needs documentation.
Parameters
**********
.. todo:: VerifyDocs:Parameters Needs documentation.
Secrets
*******
This job template needs no secrets.
Results
*******
This job template has no output parameters.

Some files were not shown because too many files have changed in this diff Show More