Something I want to do on most of my versioned projects is handle the versioning and committing as part of the merge/push to the main branch. So I’m putting this here as a reminder to myself and help to anyone else who is doing this with github actions.

First, you’ll need the script to do the work. Mine looks like this:

#! /bin/bash

# Check for required environment variables
if [ -z "$GIT_EMAIL" ]; then
  echo "❌ GIT_EMAIL is not set. Please set GIT_EMAIL."
  exit 1
fi

if [ -z "$GIT_NAME" ]; then
  echo "❌ GIT_NAME is not set. Please set GIT_NAME."
  exit 1
fi

if [ -z "$NPM_TOKEN" ]; then
  echo "❌ NPM_TOKEN is not set. Please set NPM_TOKEN."
  exit 1
fi

if [ -z "$GIT_COMMIT_TOKEN" ]; then 
    echo "❌ GIT_COMMIT_TOKEN is not set. Please set GIT_COMMIT_TOKEN."
    exit 1
fi

# Make sure we're on the master branch
if [ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]; then
  echo "❌ Not on main branch. Please switch to the main branch before deploying."
  exit 1
fi

# Setup the git config if not already setup
if [ -z "$(git config --global user.email)" ]; then
    echo "🎯 Setting github config user"
    git config --global user.email "${GIT_EMAIL}"
    git config --global user.name "${GIT_NAME}"  
    git remote set-url origin https://x-access-token:$GIT_COMMIT_TOKEN@github.com/kshehadeh/cmdq
fi

# Setup npmrc publish
npm config list | grep -q //registry.npmjs.org/:_authToken
result=$?
if [ $result -ne 0 ]; then
    echo "🎯 Setting npm auth token"
    echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
fi

git remote -v

# # Bump the version
echo "🎯 Incrementing version..."
npm run increment

# Build, and publish
echo "🎯 Installing packages..."
npm install

echo "🎯 Running build..."
npm run build

echo "🎯 Publishing to NPM..."
npm publish --access public

# Commit the version bump
echo "🎯 Committing version change..."
git add .
git commit -m "Bump version"
git push

The important parts of this are the configuration changes that ensure that git and npm have permission to alter upstream state.

The GIT_COMMIT_TOKEN in Github Actions is just the GITHUB_TOKEN that is automatically available to you in secrets.GITHUB_TOKEN. This, by default, does not have permission to write to the repo. In order to allow for it to do so, you will need to add the following to your github action yaml:

permissions:
    contents: write

So that my deploy.yaml looks like this:

name: Deploy

on:
    push:
        branches:
            - main

jobs:
    deploy:
        runs-on: ubuntu-latest
        env:
            NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
            GIT_EMAIL: ${{ secrets.GIT_EMAIL }}
            GIT_NAME: ${{ secrets.GIT_NAME }}
            GIT_COMMIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}

        permissions:
            contents: write

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

            - name: Set up Node.js
              uses: actions/setup-node@v2
              with:
                  node-version: "20"

            - name: Run deploy script
              run: npm run deploy

            

npm run deploy simply executes deploy.sh which contains the shell script above.

What If Your Branch is Protected

If your branch is protected and pushing to your default branch is not allowed and you don’t want to automate a pull request flow, you have to go through some hoops.

Create a PAT or use an App Token

At Under Armour we have created a custom Github app that we use for a variety of tasks. One of the advantages of this is that we can generate tokens that are specific to this app and then give permission to the app to perform certain tasks.

Using the official actions/create-github-app-token task, we can create a token with permission to push to the default branch as part of a github action then use that token with the checkout action.

steps:
    - uses: actions/create-github-app-token@v1
      id: app-token
      with:
        app-id: ${{ vars.QB_APP_ID }}
        private-key: ${{ secrets.QB_APP_PRIVATE_KEY }}

    - uses: actions/checkout@v2
      with:
        token: ${{ steps.app-token.outputs.token }}
        ref: ${{ github.head_ref }}

Then in your repo settings, make sure to establish a bypass for this particular app.

src
💡
It’s usually better to use an app token than just using a personal token because it’s not tied to a particular person. If that person leaves, the action will continue to work.

But there’s a problem with this approach. If your action is triggered by a push to the branch, then this will cause an infinite loop of action runs.

Github Actions explicitly looks for git activity that is associated with the GITHUB_TOKEN from an action and will not retrigger an event. When using a token that is different, it will trigger push even again and start the action all over again. This is very bad. But you can avoid it by using a special commit message that tells Github not to trigger the event.

By including [skip-ci] in the commit message, it instructs github not to trigger a push or pull_request event for this particular commit.

© Karim Shehadeh
  • X
  • BlueSky
  • RSS
  • LinkedIn
  • StackOverflow
  • Github