ankri.de

Creating a GitHub Action that automatically publishes itself

September 11, 2019

The problem

This workflow does not work if we have to check-in and publish our node_modules folder. Also I really dislike the idea of publishing the node_modules folder. The manual workflow of constantly removing the node_modules folder and then only installing the production dependencies by running npm install --production and then switching back to developing and having to run npm install again to get the dev dependencies is also kind of annoying. Luckily for us there are tools to bundle our code in a way that the node_modules needed for the production build are also included. You could use webpack, rollup and so on but I recommend using ncc.

Building our action with ncc

To solve our first problem we use ncc to compile our TypeScript code into a single index.js file and also include all the dependencies from the node_modules into the same index.js.

  1. Install ncc by running npm install --save-dev @zeit/ncc
  2. Add a build step to your package.json:
  "scripts": {
    "build": "ncc build src/main.ts --minify"  }
  1. Edit your action.yml and replace the main: line with: main: 'dist/index.js. For example:
runs:
  using: "node12"
  main: "dist/index.js"

Do not add the dist directory to your .gitignore. The dist folder needs to be checked in and committed for the action to work. See Flaws > Not having the dist directory in .gitignore

The workflow

You can use a version of this workflow. Copy the content a new file called release-action.yml inside your .github/workflows folder. Copy the code below into your newly created file .github/worklows/release-action.yml.

The workflow will use the version in the package.json. It will either go to an existing branch with this version or create a new branch. After switching to the branch it will merge with master. It will then build the action and push the new contents to the new branch.

name: release action

on:
  push:
    branches:
      - master

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v1
      - name: go to release branch
        run: git checkout -B $(node -p -e "require('./package.json').version")
      - name: merge with master
        run: git merge origin/master --no-edit
      - name: Authenticate with GitHub package registry to be able to download from private repositories
        run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" > ~/.npmrc
      - name: npm install
        run: npm install
      - name: run build
        run: npm run build
      - name: add and push
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
        run: |
          git config user.name "$GITHUB_ACTOR"
          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
          git add -A
          git commit -m "released new version"
          git push --force https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY $(node -p -e "require('./package.json').version")

The workflow in detail

on:
  push:
    branches:
      - master
  1. only run when a maintainer is pushing to the master branch

- uses: actions/checkout@v1
  1. We need the latest sources.

- name: go to release branch
  run: git checkout -B $(node -p -e "require('./package.json').version")
  1. We use the package version from the package.json and either create a new branch with this version or go to an existing branch with this version

- name: merge with master
  run: git merge origin/master --no-edit
  1. We merge the code with the code from the master branch.

- name: (optional) Authenticate with GitHub package registry
  run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" > ~/.npmrc
  1. If your action has dependencies that can only be found on the GitHub package registry keep this step, otherwise you can get rid of this step.

- name: npm install
  run: npm install
- name: run build
  run: npm run build
  1. We need to install the dependencies to build the action

- name: add and push
  env:
    GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
  run: |
    git config user.name "$GITHUB_ACTOR"
    git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
    git add -A
    git commit -m "released new version"
    git push --force https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY $(node -p -e "require('./package.json').version")
  1. We use the user that pushed the changes to the master branch to release the changes and push the build into the version number branch.

Flaws of this method

Sadly this method is not without its flaws. But I think they’re solvable. PRs welcome.

Not having the dist directory in .gitignore

It would be nice to have the dist directory inside the .gitignore. Solution: We need a step to remove it from the .gitignore before pushing.

Using force pushing

I am not enough of a git wizard to be able to do it without git push --force. If you have an idea, please contact me.

Using branches instead of tags

While testing the action I very much prefer to be able to rapidly update my code. Therefor I prefer using branches instead of fixed tags. Otherwise I would need to bump the version in the package.json for every code change that I want to test.

Having a tag would be more like a fixed version that cannot/should not be changed.

Having to manually update the package version

I like to be able to directly influence the package version number, but it would also be possible to use a script that updates the version number.

Nice to have: Update the references in the README.MD

It would be nice if the script could also update the references to the built action and automatically update the version number.


I write about React, JavaScript, web application development and other stuff. Sometimes in german and sometimes in english.