Workflow running twice (push && push --tags)

Is there a way around git push && git push --tags running the workflow for the same SHA twice?

If you only want it to run on push if there’s a tag:

on:
  push:
    tags: 
      - '*'

Thanks for the suggestion, but that’s not quite enough for my use case.
I want CI builds on normal push - and I want release builds on tags.

I am already checking on a build whether a tag is present or not.

on: [push, pull_request]

jobs:
  test:
    ...

  release:
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    needs: [test]
    ...

But as it stands, the first git push triggers a build, and the subsequent git push --tags triggers another build. Yet - nothing but the tag has changed. And even more important - both builds are for the same SHA. I am actually surprised that the push of the tag does not cancel the previous workflow run.

I am not aware of a way to make git push && git push --tags atomic. So there will always be a time difference. And it is thereby not really clear on the first push whether it will be followed by a tag push or not.

I’ve actually never tried git push --tags && git push. Not sure that’s even feasible - but that order would make it possible to skip one of the two builds. At least in theory.

It works as it is - but it means a lot of wasted build time.
I was hoping there is a better way.

@tcurdt ,

According to your comments, your purpose is:
(If I missed anything, feel free to tell me.)

  • Push a commit to your GitHub repository and also push a tag from this commit.
  • The commit push triggers the CI workflow to execute the build and test jobs.
  • After build and test complete successfully, to execute the release job with the pushed tag in the same workflow.

If so, you can try set the workflow like as below:

name: CI
on:
  push:
    branches: 
      - '**'
  pull_request:

jobs:
  build:
    . . .

  test:
    . . .

  check_tag:
    needs: [build, test]
    if: ${{ github.event_name == 'push' }}
    outputs:
      tag_name: ${{ steps.get_tag.outputs.tag_name }}
    runs-on: ubuntu-latest
    steps:
      - name: Get tag name
        id: get_tag
        run: |
          commit_sha=${{ github.sha }}
          tag_name=""
          page_number=1
          while [ -z $tag_name ]
          do
            response=$(curl \
            -H "Authorization: token ${{ secrets.MY_GITHUB_PAT }}" \
            -H "Accept: application/vnd.github.v3+json" \
            https://api.github.com/repos/${{ github.repository }}/tags?per_page=100&page=$page_number)
            tag_name=$(echo "$response" | jq -r --arg commit_sha "$commit_sha" '.[] | select(.commit.sha == $commit_sha) | .name')
            
            length=$(echo "$response" | jq 'length')
            if [[ $length -lt 100 ]]; then
              break
            fi
            
            page_number=$(($page_number + 1))
          done

          echo "tag_name = $tag_name"
          if [[ -z $tag_name ]]; then
            echo "::set-output name=tag_name::N/A"
            echo "No tag that was created from the commit SHA '$commit_sha'."
          else
            echo "::set-output name=tag_name::$tag_name"
          fi

  release:
    needs: [check_tag]
    if: ${{ needs.check_tag.outputs.tag_name != 'N/A' }}
    runs-on: ubuntu-latest
    steps:
      - name: Create release
        id: create_release
        uses: actions/create-release@v1
        with:
          tag_name: ${{ needs.check_tag.outputs.tag_name }}
          release_name: Release-name
          . . .

Description:

  1. After the build and test jobs complete successfully, the check_tag job will check if exists a tag that has been created from the current commit (github.sha) of the workflow run:

    • If no tag was created from the commit, set the value of the output ‘tag_name’ as ‘N/A’ (NULL).
    • If find the tag, set the tag name as the value of the output ‘tag_name’.
  2. After the check_tag job complete successfully, go to the release job:

    • If the ‘tag_name’ is ‘N/A’, skip release job.
    • If the ‘tag_name’ is not ‘N/A’, execute release job to create a new release with the tag name.

Thanks for the elaborate answer, @brightran - but I don’t fullt understand how this will help yet. On the first glance It seems like this

on:
  push:
    branches: 
      - '**'
  pull_request:

jobs:
  build:
    . . .
  test:
    . . .
  check_tag:
    needs: [build, test]
    if: ${{ github.event_name == 'push' }}
 release:
    needs: [check_tag]

is just a more complicated version of

on: [push, pull_request]

jobs:
  test:
    ...
  release:
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    needs: [test]
    ...

Unless I miss something special about

on:
  push:
    branches: 
      - '**'

How will this stop the workflow to trigger twice for a single SHA?

I assume the push of the tag will not trigger the workflow?
But wouldn’t that also mean I would need to push the tag before I push to the branch? (otherwise there is no tag to find yet)

@tcurdt ,

I assume the push of the tag will not trigger the workflow?
But wouldn’t that also mean I would need to push the tag before I push to the branch? (otherwise there is no tag to find yet)

Yeah, you’re right.
With the workflow I shared above, before push a commit, you must push a tag from this commit, so that the check_tag job can find the tag when the workflow is triggered to run.

About the push event:

  1. if you do not set any branch filters and tag filters for it, by default both pushing tags and pushing commits can trigger the workflow. Don’t care about how the path filters are.
    # No branch filters and tag filters are set for push event.
    on: push
    
    OR
    # No branch filters and tag filters are set for push event.
    on:
      push:
        paths:
          . . .
    
  2. If you define only tags or only branches, the workflow won’t run for events affecting the undefined Git ref. Don’t care about how the path filters are.
    • If only set branch filters, only pushing commits can trigger the workflow, pushing tags will not trigger the workflow.

      # Only set branch filters, no tag filters.
      # Can set path filters if you require.
         on:
           push:
             branches:
               --- some branch filter patterns ---
           . . .
      
    • If only set tag filters, only pushing tags can trigger the workflow, pushing commits will not trigger the workflow.

      # Only set tag filters, no branch filters.
      # Path filters are not evaluated for pushes to tags.
         on:
           push:
             tags:
               --- some tag filter patterns ---
           . . .