Automatically update major version tags with new releases on GitHub

This GitHub Action workflow is designed to run whenever a new release is published in your GitHub repository. To enhance flexibility, the workflow_dispatch event is included, allowing manual trigger of the workflow for rollbacks or other administrative tasks. The workflow performs the following actions:

  • Tag Validation: Checks if the release tag adheres to the semantic versioning specification. This validation can be configured to fail the workflow if the tag format is incorrect, ensuring consistency in versioning.
  • Major Tag Update/Create: Identifies and updates or creates a new major version tag based on the latest release. For example, it can automatically move a major version tag from v1.x.x to v2.0.0.
  • Redirect Tag: Ensures the major version tag points to the latest release tag, always maintaining an up-to-date reference.

Events triggering the workflow

As previously mentioned, this workflow is designed to trigger automatically whenever a new release is published in the repository. To ensure specificity and avoid unintended executions, the trigger is set for releases marked as “published” only. Additionally, the workflow_dispatch event is included to allow manual execution of the workflow. This manual trigger is crucial for operational flexibility, such as performing rollbacks or handling special cases.

When manually triggering the workflow, it is essential to validate the inputs received (“Set Dynamic Environment Variables” step in the file). This step ensures that the workflow runs with the correct parameters and maintains consistency regardless of the trigger method.

Tag validation

To ensure the integrity of version tags, you can incorporate a validation step within the workflow that checks tags against the semantic versioning specification. This validation is crucial for maintaining consistency in version numbering during both automated release triggers and manual workflow dispatches.

Here are some examples of the validation script:

your-it-guy@dev-bot:~/update-major-tags/.github/workflows/scripts$ ./validate-tag.sh 
Usage: ./validate-tag.sh <tag>
your-it-guy@dev-bot:~/update-major-tags/.github/workflows/scripts$ ./validate-tag.sh 1.0.2
Tag '1.0.2' is valid according to Semantic Versioning.
your-it-guy@dev-bot:~/update-major-tags/.github/workflows/scripts$ ./validate-tag.sh 1.0.2-beta
Tag '1.0.2-beta' is valid according to Semantic Versioning.
your-it-guy@dev-bot:~/update-major-tags/.github/workflows/scripts$ ./validate-tag.sh 7.0.2-2
Tag '7.0.2-2' is valid according to Semantic Versioning.
your-it-guy@dev-bot:~/update-major-tags/.github/workflows/scripts$ ./validate-tag.sh v7.0.2
Tag 'v7.0.2' is NOT valid according to Semantic Versioning.

Update or create a major tag

This step involves using Git commands to either create a new major version tag or update an existing one. The updated tag will then be force-pushed to point to the minor version tag created during the release. To facilitate this process automatically, we’ve configured the following:

  • GitHub Actions Bot: The workflow uses the github-actions[bot] identity to execute commands, ensuring that actions are performed by an automated entity recognized within the GitHub environment.
  • Personal Access Token (PAT) Permissions: A PAT is configured with specific permissions to ensure it can perform necessary actions across various GitHub features:
    • Contents: Read and write access, allowing the bot to modify repository content and tags.
    • Metadata: Read-only access, sufficient for accessing repository metadata without making changes.
    • Workflows: Read and write access, enabling the bot to manage GitHub Actions workflows.

Using a PAT is generally more reliable for consistent operation across different environments, repositories, and organizations. In some instances, the default GitHub token provided to workflows might suffice, but this depends on the repository’s configuration. Alternatively, SSH keys can be used to authenticate Git operations if preferred.

Here’s the entire yaml of the workflow:

name: Update Major Version Tag After Release

on:
  workflow_dispatch:
    inputs:
      target:
        description: The tag or reference to use
        required: false
        default: "default-tag"
      major_version:
        type: choice
        description: The major version to update
        options:
          - "5"
          - "4"
          - "3"
          - "2"
          - "1"
          - "0"
  release:
    types:
      - published

jobs:
  tag:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
        token: ${{ secrets.TAGS_TOKEN }}
    - name: Set Dynamic Environment Variables
      run: |
        if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
          # Use inputs from workflow_dispatch if provided
          TARGET="${{ github.event.inputs.target }}"
          MAJOR_VERSION="${{ github.event.inputs.major_version }}"
        elif [ "${{ github.event_name }}" == "release" ]; then
          # Use release tag for target, and extract major version
          TARGET="${{ github.event.release.tag_name }}"
          MAJOR_VERSION="$(echo $TARGET | cut -d '.' -f1)"
        fi
        echo "Target tag: $TARGET, Major Version Tag: $MAJOR_VERSION"
        echo "TARGET=$TARGET" >> $GITHUB_ENV
        echo "MAJOR_VERSION=$MAJOR_VERSION" >> $GITHUB_ENV

    - name: Validate tags
      run: |
        # Function to validate a semantic versioning tag
        validate_tag() {
          local tag="$1"

          # Regular expression for validating semantic versioning tag
          local semver_regex="^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$"

          if [[ $tag =~ $semver_regex ]]; then
            echo "Tag '$tag' is valid according to Semantic Versioning."
            return 0
          else
            echo "Tag '$tag' is NOT valid according to Semantic Versioning."
            return 1
          fi
        }

        validate_tag "${{ env.TARGET }}"

    - name: Git config
      run: |
        git config user.name "github-actions[bot]"
        git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
    - name: Tag new target
      run: git tag -f ${{ env.MAJOR_VERSION }} ${{ env.TARGET }}
    - name: Push new tag
      run: git push origin ${{ env.MAJOR_VERSION }} --force
| Theme: UPortfolio