34

I'm trying to move my CI workflow from CircleCI to GitHub Actions. The last major struggle I'm facing is with deployment.

Currently my workflow is such that when I push a tag to my GitHub repo, it will run the tests, then run the deployment. Only thing is CircleCI filters tags to only run the job if the tag matches the regex: /v[0-9]+(\.[0-9]+)*/.

How can I check to ensure the tag I pushed matches the regex pattern above before running the deployment?

I currently have the following GitHub Actions yml file:

name: CI
on: [create]

jobs:
  # ...

  deploy:
    runs-on: ubuntu-latest
    if: github.event.ref_type == 'tag' && github.event.ref == SOMETHING HERE
    steps:
      - uses: actions/checkout@v1
      # ...

Under the if block, I need to change github.event.ref == SOMETHING HERE to be something else. I have looked in the Contexts and expression syntax for GitHub Actions documentation page. But due to how flexible and powerful GitHub Actions is, it seems like there should be a method or way to do this, or at least some type of workaround.

How can I ensure the tag (github.event.ref) matches the regex pattern (/v[0-9]+(\.[0-9]+)*/)?

Charlie Fish
  • 18,491
  • 19
  • 86
  • 179

3 Answers3

46

Unfortunately, I don't think there is any way to do regex matching on if conditional expressions yet.

One option is to use filtering on push events.

on:
  push:
    tags:
      - 'v*.*.*'

Another option is to do the regex check in a separate step where it creates a step output. This can then be used in an if conditional.

      - name: Check Tag
        id: check-tag
        run: |
          if [[ ${{ github.event.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
              echo "match=true" >> $GITHUB_OUTPUT
          fi
      - name: Build
        if: steps.check-tag.outputs.match == 'true'
        run: |
          echo "Tag is a match"
Community
  • 1
  • 1
peterevans
  • 34,297
  • 7
  • 84
  • 83
  • 2
    Yeah. Due to my workflow I want the job before deploy to run on all tags created, not just “v*.*.*”. The matching of that also isn’t as powerful as my regex. Then the separate step option might work, but feels really messy when you get to have a lot of steps. Hopefully GitHub adds support for this soon! – Charlie Fish Nov 17 '19 at 02:30
  • 1
    Your Check Tag action step is great, it works like a charm! – Huan Feb 08 '20 at 13:43
  • @peterevans Looking good but can you explain what the latter example does or how it works or at least add a reference to some docs? – j3141592653589793238 Feb 17 '20 at 20:34
  • 1
    @user0800 I've added a link to the docs for `set-output`. The regex part is just shell scripting and not specific to GitHub Actions. – peterevans Feb 18 '20 at 00:21
  • 1
    The shell if matching can be very dangerous if you use user input (pr title for example or branchname as you did) This example is very concerning i would consider removing it. What if you name your branch `true]]||["a"` ? (if square brachets are allowed?) – Niton Nov 10 '21 at 14:55
20

I have managed to achieve this with a two part approach. The first part consists of filtering the tags that you want to run on. The second part is to create a condition on your deploy job.

A cut down version of my workflow looks like the following:

name: CI-CD

on:
  push:
    branches:
    - stable
    tags:
    - '[0-9]+.[0-9]+.[0-9]+'
    - '[0-9]+.[0-9]+.[0-9]+rc[0-9]+'
  pull_request:
    branches:
    - stable


jobs:
  test:
    steps:
      ...

  deploy:
    needs: test
    if: startsWith(github.ref, 'refs/tags')
    steps:
      ...

I run my workflows on pushes, tags, and pull request to specific branches. You can get creative with how exactly you want to filter your tags. Check out the filter patterns.

The part I was struggling with a bit was how to set the right condition to run the deploy job. I settled on the following:

needs: test

which ensures that it will only run when the test job has succeeded.

Then I abuse the fact that github.ref will be 'refs/heads/stable' on a push to the branch and 'refs/tags/<your tag>' on pushing a tag. So I simply test for that difference with the following condition.

if: startsWith(github.ref, 'refs/tags')

Works like a charm for me but may depend on your setup.

You can also create separate workflows, of course, but in my case I always wanted to run the test suite first.

EDIT: As @alex-povel comments below, a more explicit check is:

if: github.ref_type == 'tag'

Note that the symbol on the other side of the comparison should be a single quoted string. Using a double quoted string would lead to the error: Unexpected symbol: '"tag"'.

suvayu
  • 4,271
  • 2
  • 29
  • 35
Midnighter
  • 3,771
  • 2
  • 29
  • 43
  • 1
    For reference, `startsWith` and other expressions are documented here: https://docs.github.com/en/actions/learn-github-actions/expressions#startswith – Arthur Jan 11 '22 at 08:30
  • 4
    The `startsWith(github.ref, 'refs/tags')` check can be replaced with `github.ref_type == 'tag'`. – Alex Povel Aug 25 '22 at 15:27
  • In the if-expression double quotes is wrong. It has to be a single quoted string (as mentioned in the comment by @AlexPovel). – suvayu Jun 07 '23 at 12:06
16

As per docs, you can do this:

on:
  create:
    tags:
      - "v[0-9]+.[0-9]+"

I tried the above and can confirm it works. This is not full regex capability but should suffice for your needs.

Mrchief
  • 75,126
  • 20
  • 142
  • 189
  • 1
    To be complete, "v[0-9]+\.[0-9]+" (with backslash) is even better, otherwise your regex will also match on both 3.6 and 3a6, since the . character stands for "anything" in regex. You can confirm this with https://regex101.com/ for example. – Dennis Ameling Oct 30 '20 at 22:14
  • 6
    @DennisAmeling Check the "docs" link he mentioned. You will see that the "." are not affected like a fully conventional regex. – Isometriq Dec 25 '20 at 07:00
  • -1, The branch and tag filter are only supported for push and pull_request events. This config will not work. ref: https://github.com/actions/runner/issues/1007#issuecomment-808904408 – hiropon Oct 04 '22 at 02:42