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
.
- Install ncc by running
npm install --save-dev @zeit/ncc
- Add a
build
step to yourpackage.json
:
"scripts": {
"build": "ncc build src/main.ts --minify" }
- Edit your
action.yml
and replace themain:
line with:main: 'dist/index.js
. For example:
runs:
using: "node12"
main: "dist/index.js"
Do not add the
dist
directory to your.gitignore
. Thedist
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
- only run when a maintainer is pushing to the
master
branch
- uses: actions/checkout@v1
- We need the latest sources.
- name: go to release branch
run: git checkout -B $(node -p -e "require('./package.json').version")
- 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
- 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
- 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
- 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")
- 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.