Git auto-commit action
February’s Action Spotlight theme is all about committing changes that you’ve made back to your GitHub repo, starting with stefanzweifel/git-auto-commit-action.
What does it do?
The GitHub Action for committing files for the 80% use case.
By default, any changes made to the workspace when running a GitHub Action are discarded when the job ends. This means that whilst Actions is great for running tools such as eslint
to check your code style, if you wanted to use eslint --fix
to automatically fix any violations and push those changes to a pull request you’re out of luck.
Until now, that is. git-auto-commit-action
will automatically add, commit and push any created or changed files back to your repo automatically.
How does it work?
Taking a look at the repo, the action is defined as a JavaScript action but has an entrypoint.sh
script. This seems odd as shell scripts are usually run using the Docker runtime.
Looking at index.js
, things become a little clearer. git-auto-commit-action
uses a shim to trick Actions into running a shell script directly on the runner machine rather than building a Docker container. This works as the runner machines have bash
preinstalled (here’s the list of software for Ubuntu 20.04).
The bash script is well architected, with well named functions and good debugging information. It also uses set -eu
to make the script very brittle (this is a good thing!) ensuring that it fails fast. The -e
flag will automatically stop execution of the script if any command executed fails, and the -u
flag treats missing variables as an error when trying to use them. These two options combined mean that if any variables are missing, the script will exit instantly.
The _main
function is our entrypoint into the script. The first thing it does is change to the specified directory. By default the repository
input is .
which means that the script will run in the current directory, which is the repository root.
Any action inputs are available as variables prefixed with
INPUT_
in GitHub Actions
Next, it checks if there have been any changes made. If not, the changes_detected
output is set to false and the script exits. You can disable this check by setting the skip_dirty_check
input to true
.
If changes were detected, changes_detected
is set to true and the real work starts.
In order, the following steps are executed:
- Check out the specified branch (defaults to
github.head_ref
) - Add required files. By default all files will be added with
.
. You can specify a pattern such assrc/*
or*.js
with thefile_pattern
input - Create a commit containing the changes. The default identity will commit the changes as GitHub Actions, with the change author being set to the person that triggered the workflow run. You can also specify
commit_options
such as--signoff
to add a trailer. - If you provide a
tagging_message
a tag will be created pointing to the commit that was just created. - Finally, the commit (and possibly a tag) will be pushed to GitHub. You can specify
push_options
to add additional flags togit push
The default behaviour is the most useful to the majority of people, but having the configuration options available are useful. Imagine setting commit_options
to --amend --no-edit
and push_options
to --force
to automatically fix code style issues in the same commit rather than adding a new one, or passing repository: src
to only commit changes in the src
directory.
Writing the action in bash allows stefanzweifel
to make the most of the git
command line and not reimplement the world in JavaScript for a relatively simple action. They use an index.js
shim which adds support for Windows and MacOS runners (in addition to making the action run fast) as Windows and MacOS don’t support Docker actions, but they do support Bash. The combination of bash
and index.js
provides portability and speed without needing to rebuild the git
command line tooling used.