If you ever browsed the source code of any GitHub Action that you had encountered in the wild, for sure you realized that most authors push bundled JavaScript code directly to a git repository. Usually - it's a single file like action.js
, but in the worst case it's a set of files with the whole node_modules
committed directly to the git π±
Why is it a bad idea?
There are various reasons why you might want to avoid that. First of all, it makes code reviews harder. Reviewers now have to take a look at the committed bundle or add additional tooling to verify if the user doesn't try pushing anything fishy instead of a proper bundle. It's a 10k line blob - who's gonna check it anyway?
Bundled files can easily produce merge conflicts which add even more unnecessary work for maintainers. It's confusing for newcomers too, "should I commit this blob or what?". Not to mention, it's just plain ugly to keep these files in the git repo.
Automation for the rescue
Usually, committing anything that can be derived from the source to the git repositories is a bad practice. Sadly, we can't get rid of pushing JavaScript bundles entirely because GitHub Actions Runner requires it but at least we can hide that fact.
GitHub Actions are all about making developing code easier so let's use GitHub Actions to develop GitHub Actions!
The plan is to develop GitHub Actions as any other JavaScript code - with bundles/node_modules
git-ignored. Let's have another branch for build artifacts. This release branch will be automatically updated by GitHub Actions on every push to the main branch.
To make this task easier I've created a helper action called: branch-push-action.
Here's the complete workflow:
name: Deploy
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 12.x
- run: yarn --no-progress --non-interactive --frozen-lockfile
- run: yarn build
- uses: ActionwareIO/branch-push-action@action
with:
branch: action
files: |
README.md
action.yml
./dist/index.js
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The build
task uses ncc
to produce a single file bundle. branch-push-action
will save specified files, checkout target branch (action
in this example), and commit updates. A branch will be automatically created if it doesn't exist and it will contain only necessary files.
The last thing is that you need to point users of your GitHub Action to the branch with bundled code like:
- uses: your-awesome-action@action
Of course, you can tweak this workflow to your needs - use dev
and master
branches or even commit files to the same branch. I prefer to keep build artifacts on a separate branch as this allows me to completely git ignore any unnecessary files on the default branch.
Summary
Now you can develop GitHub Actions without worrying about build artifacts. I prefer to use TypeScript while creating Actions and pushing compiled JavaScript code was messing up language statistics on GitHub. Now I can brag about that beautiful 100% TypeScript badge ;)
In case you're wondering - yes, branch-push-action
is also deployed using itself ;) For the complete solution for automatic deployment browse its source code.
Now, you know how to continuously deploy GitHub Actions, but you wouldn't want to push a broken release, would you? In my next article, I will dive into testing GitHub Actions properly - given their nature, which is full of side effects, it's not that easy. If you don't want to miss it follow me on π€ twitter π€!
Top comments (3)
Hey Kris, this is clever. While reading I replicating the gh-pages pattern here, curious if that inspired you. I think will be using your branch-push-action for a future project that I need to deploy to multiple providers.
Great stuff!
Awesome! Let me know how it goes ;)
Yes, it's a kinda similar pattern of having a "special" branch for a different thing.
Thatβs awesome! Especially the title is cool!