Maintaining a version string in a project and using it for creating releases

Hi

I was looking into automating our release process with Actions and I am in a fix as to how I can best achieve a clean methodology. We want to make releases manually so something like Semantic Release is not going to be a good fit. As such I decided that we can create an action which creates a release and gets triggered when we want a release. The question was how to trigger this action.

For that, I went with creating a VERSION file which just has the version string. The release action gets triggered only when this file changes which works well for now. But, creation of this file means that the version number is duplicated in this file and in the project’s native manifest file (like package.json, build.gradle etc). Here’s a few more ideas I have:

  1. I can remove the version information in such manifest files but there might be some framework which screams and throws a fit if I do so.
  2. I can put a fake value in the version field of these manifest files but this seems to uncomfortable hacky
  3. I can drop the VERSION file idea and trigger the release action on change to the manifest file itself. Trouble is, that I can’t trigger the release everytime the manifest changes since it can change without the version information being changed like updation of dependencies. I would need to specifically check if the version field has changed. I would need to handle edge cases like the line number of the version field getting relocated elsewhere for which I would need to check against the same file with the previous version, etc.

The 3rd solution seems to me to be the most flexible but it needs some effort in getting right. I was looking for opinions and insights as to which of these ideas is the best or if there is an even better idea.

@sayakmukhopadhyay ,

As such I decided that we can create an action which creates a release and gets triggered when we want a release.

In fact, we have an official action provided by GitHub to create release (actions/create-release).


The question was how to trigger this action.

When creating a release, we need to provide a tag which generally is used as the version of the new release.
To trigger the action to create release in the workflow, here I have two ways as reference:

  1. When a specified tag is pushed to the repository, trigger the workflow.

    name: Release
    on:
      push:
        tags:
          - '<tags_filter_patterns>'
    # Only the tags match the filter patterns can trigger the workflow.
    # See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestbranchestags
    
    jobs:
      job1:
        name: Create release
        runs-on: ubuntu-latest
        steps:
          - name: Create Release
            id: create_release
            uses: actions/create-release@v1
            with:
              tag_name: ${{ github.ref }}
              . . .
    

    This way generally is applied when you manually create a tag and push it to the repository.

  2. If you want to automatically create a tag from the commit that you want to release, you also need to provide the name of the new tag and the commit SHA or the branch name on the action.

    name: Release
    on:
      push:
        branches:
          - '<branches_filter_patterns>'
    
    jobs:
      job1:
        name: Create release
        runs-on: ubuntu-latest
        steps:
          - name: Create Release
            id: create_release
            uses: actions/create-release@v1
            with:
              tag_name: <tag_name>
              commitish: ${{ github.sha }}
              . . .
    

    In this way, you may need to use a “Build Number Generator” action to generate the auto-incrementing build number used for setting the tag name (release version number).
    Related actions from the GitHub Marketplace:

    In addition, when you commit and push the changes you want to release, you can add a keyword in the commit message so that the workflow can know this commit need to be released.
    For example,

    • set the commit message starts with or contains the keyword “[release]”.
      commit_message

    • In the workflow, use the if statement to trigger the action to create release when the specified keyword contained in the commit message.

      - name: Create Release
        id: create_release
        if: ${{ contains(github.event.head_commit.message, '[release]') }}
        uses: actions/create-release@v1
        with:
          tag_name: <tag_name>
          commitish: ${{ github.sha }}
          . . .
      

Hi @brightran, thanks for the response. I didn’t mention it but I am already using the create-release action to create a release.

As such I decided that we can create an action which creates a release and gets triggered when we want a release.

I meant that I created a workflow, not an action. Sorry for confusing the terms :pray:.

But anyway, this is how my workflow looks right now

name: Release
on:
  push:
    branches:
      - master
    paths:
      - 'VERSION'

jobs:
  docker-build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set Version ENV
        run: |
          echo '::set-env name=VERSION::'$(cat VERSION)

      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.VERSION }}
          release_name: ${{ env.VERSION }}

The VERSION file contains a string like 1.2.3. So, when I change the version file to something like 1.3.0, and push the commit, this workflow is triggered and a new release is created.

My concern was that everytime I change this file, I also need to change the package.json file’s version field. This can give rise to inconsistencies in the long run which I want to avoid.

@sayakmukhopadhyay ,

You can try like as this:
In the workflow, use the ‘sed’ command to replace the variable name with the actual version number in the ‘package.json’.

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set Version ENV
        run: echo "::set-env name=VERSION::'$(cat VERSION)"

      - name: Update version in 'package.json' and push changes
        run: |
          old_str="\"version\": \"1.2.3\""
          new_str="\"version\": \"1.3.0\""
          sed -i 's/$old_str/$new_str/g' package.json

          git config --global user.name "you username"
          git config --global user.email "you email"
          git add package.json
          git commit -m "Update version"
          git push

      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.VERSION }}
          release_name: ${{ env.VERSION }}
          commitish: master

In this way, you only need to changes the version number in the VERSION file to trigger the workflow, then the workflow will execute the ‘sed’ command to automatically changes the version in the ‘package.json’.

Nice, this is a great idea. Right now, I have implemented another idea which doesn’t require the workflow to make a commit but relies on the reviewer. I have added a workflow job which parses the package.json and the VERSION file and tests if both of them have the same value. If not, this is reflected as a failed test when someone creates a PR. Thus a reviewer can ensure that the PR author fixes this.

check-version:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          persist-credentials: false

      - name: Generate version strings
        run: |
          echo '::set-env name=VERSION_FILE::'$(cat VERSION)
          echo '::set-env name=PACKAGE_FILE::'$(sed -e 's/^"//' -e 's/"$//' <<< $(jq .version package.json))
          echo '::set-env name=PACKAGE_LOCK_FILE::'$(sed -e 's/^"//' -e 's/"$//' <<< $(jq .version package-lock.json))

      - name: Verify version strings
        run: |
          [[ "$VERSION_FILE" == "$PACKAGE_FILE" && "$PACKAGE_FILE" == "$PACKAGE_LOCK_FILE" ]]

Hopefully this alternate idea can also help some people.