From 1c99a4a91486d85e5372087c0761944751eebdf9 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Wed, 3 Sep 2025 07:56:18 +0200 Subject: [PATCH] Documented TagReleaseCommit. --- .github/workflows/CompletePipeline.yml | 2 +- .github/workflows/TagReleaseCommit.yml | 2 +- .../workflows/_Checking_AvailableRunners.yml | 2 +- doc/JobTemplate/Package/InstallPackage.rst | 2 +- doc/JobTemplate/Release/TagReleaseCommit.rst | 200 +++++++++++++++++- doc/JobTemplate/Setup/Parameters.rst | 2 + doc/JobTemplate/Setup/PrepareJob.rst | 1 + doc/JobTemplate/index.rst | 1 + .../pyTooling-Actions-TagReleaseCommit.png | Bin 0 -> 19947 bytes 9 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 doc/_static/pyTooling-Actions-TagReleaseCommit.png diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index b50d518..cf9ae86 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -350,7 +350,7 @@ jobs: # - StaticTypeCheck - Package - PublishToGitHubPages - if: needs.Prepare.outputs.is_release_commit && github.event_name != 'schedule' + if: needs.Prepare.outputs.is_release_commit == 'true' && github.event_name != 'schedule' permissions: contents: write # required for create tag actions: write # required for trigger workflow diff --git a/.github/workflows/TagReleaseCommit.yml b/.github/workflows/TagReleaseCommit.yml index d110164..8ccad9d 100644 --- a/.github/workflows/TagReleaseCommit.yml +++ b/.github/workflows/TagReleaseCommit.yml @@ -54,7 +54,7 @@ jobs: permissions: contents: write # required for tag creation - actions: write # required to start a new pipeline + actions: write # required to start a new pipeline steps: - name: 🏷 Create release tag '${{ steps.FindPullRequest.outputs.version }}' diff --git a/.github/workflows/_Checking_AvailableRunners.yml b/.github/workflows/_Checking_AvailableRunners.yml index cd838b0..e651027 100644 --- a/.github/workflows/_Checking_AvailableRunners.yml +++ b/.github/workflows/_Checking_AvailableRunners.yml @@ -22,7 +22,7 @@ jobs: - {icon: '🍏', name: 'macOS-14 (aarch64)', image: 'macos-14', shell: 'bash'} # latest - {icon: '🍏', name: 'macOS-15 (aarch64)', image: 'macos-15', shell: 'bash'} - {icon: '🪟', name: 'Windows Server 2022', image: 'windows-2022', shell: 'bash'} - - {icon: '🪟', name: 'Windows Server 2025', image: 'windows-2022', shell: 'bash'} # latest + - {icon: '🪟', name: 'Windows Server 2025', image: 'windows-2025', shell: 'bash'} # latest # Third party images by ARM for aarch64 - {icon: '🐧', name: 'Ubuntu 22.04 (aarch64)', image: 'ubuntu-22.04-arm', shell: 'bash'} - {icon: '🐧', name: 'Ubuntu 24.04 (aarch64)', image: 'ubuntu-24.04-arm', shell: 'bash'} diff --git a/doc/JobTemplate/Package/InstallPackage.rst b/doc/JobTemplate/Package/InstallPackage.rst index 96887d9..bd8af99 100644 --- a/doc/JobTemplate/Package/InstallPackage.rst +++ b/doc/JobTemplate/Package/InstallPackage.rst @@ -20,7 +20,7 @@ the installation is verified. This aims for packaging and dependency mistakes in .. topic:: Job Execution .. image:: ../../_static/pyTooling-Actions-InstallPackage.png - :width: 600px + :width: 500px .. topic:: Dependencies diff --git a/doc/JobTemplate/Release/TagReleaseCommit.rst b/doc/JobTemplate/Release/TagReleaseCommit.rst index 5371dbe..468737c 100644 --- a/doc/JobTemplate/Release/TagReleaseCommit.rst +++ b/doc/JobTemplate/Release/TagReleaseCommit.rst @@ -3,20 +3,204 @@ TagReleaseCommit ################ -.. todo:: TagReleaseCommit:Needs documentation. +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*. -**Behavior:** +.. note:: -.. todo:: TagReleaseCommit:Behavior needs documentation. + 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. -**Dependencies:** + 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. -.. todo:: TagReleaseCommit:Dependencies needs documentation. + 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/PrepareJob/Instantiation: Instantiation ************* -Simple Example -============== +The following instantiation example depicts three jobs within a bigger pipeline. The ``prepare`` job derived from job +template ``PrepareJob`` version ``@r5`` 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 ``@r5``, or alternatively run the ``ReleasePage`` job derived from job template +``PublishReleaseNotes`` version ``@r5``. -.. todo:: TagReleaseCommit:Simple example needs documentation. +.. code-block:: yaml + + name: Pipeline + + on: + push: + workflow_dispatch: + + jobs: + Prepare: + uses: pyTooling/Actions/.github/workflows/PrepareJob.yml@r5 + + # Other pipeline jobs + + TriggerTaggedRelease: + uses: pyTooling/Actions/.github/workflows/TagReleaseCommit.yml@r5 + 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@r5 + needs: + - Prepare + if: needs.Prepare.outputs.is_release_tag == 'true' + permissions: + contents: write + actions: write + with: + tag: ${{ needs.Prepare.outputs.version }} + secrets: inherit + +.. seealso:: + + :ref:`JOBTMPL/PrepareJob` + ``PrepareJob`` ... + :ref:`JOBTMPL/PublishReleaseNotes` + ``PublishReleaseNotes`` ... + + +.. _JOBTMPL/TagReleaseCommit/Parameters: + +Parameter Summary +***************** + +.. rubric:: Goto :ref:`input parameters ` + ++---------------------------------------------------------------------+----------+----------+-------------------------------------------------------------------+ +| 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 ` + +This job template needs no secrets. + +.. rubric:: Goto :ref:`output parameters ` + +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 `__ + 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 +: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 +: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). diff --git a/doc/JobTemplate/Setup/Parameters.rst b/doc/JobTemplate/Setup/Parameters.rst index b3f9894..114c557 100644 --- a/doc/JobTemplate/Setup/Parameters.rst +++ b/doc/JobTemplate/Setup/Parameters.rst @@ -468,6 +468,7 @@ ubuntu_image :Required: no :Default Value: ``'ubuntu-24.04'`` :Possible Values: See `actions/runner-images - 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`. @@ -480,6 +481,7 @@ ubuntu_arm_image :Required: no :Default Value: ``'ubuntu-24.04-arm'`` :Possible Values: See `actions/partner-runner-images - 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`. diff --git a/doc/JobTemplate/Setup/PrepareJob.rst b/doc/JobTemplate/Setup/PrepareJob.rst index 828e3e9..b11cff4 100644 --- a/doc/JobTemplate/Setup/PrepareJob.rst +++ b/doc/JobTemplate/Setup/PrepareJob.rst @@ -166,6 +166,7 @@ ubuntu_image :Required: no :Default Value: ``'ubuntu-24.04'`` :Possible Values: See `actions/runner-images - Available Images `__ + for available Ubuntu image versions. :Description: Name of the Ubuntu image used to run this job. diff --git a/doc/JobTemplate/index.rst b/doc/JobTemplate/index.rst index 7c2dbbf..e53db4a 100644 --- a/doc/JobTemplate/index.rst +++ b/doc/JobTemplate/index.rst @@ -76,6 +76,7 @@ ubuntu_image :Required: usually no :Default Value: ``'ubuntu-24.04'`` :Possible Values: See `actions/runner-images - Available Images `__ + for available Ubuntu image versions. :Description: Name of the Ubuntu image used to run a job. diff --git a/doc/_static/pyTooling-Actions-TagReleaseCommit.png b/doc/_static/pyTooling-Actions-TagReleaseCommit.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a4636072318ad88517761bca232806b9054728 GIT binary patch literal 19947 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

dGguJi6#5PlO^m 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 WU%<7iqR)V4FnGH9xvX