Custom GitHub Actions shells

Updates

2024-10-07: It is actually OK to use a shell script as the custom shell, as long as the #! hash-bang is on the very first line. I updated the blog post accordingly.

Shell script as a custom shell

You can specify a custom shell for a GitHub Actions workflow step.

In the first version of this blog post I stated that the custom shell must be a binary program, but that is not true, I just didn’t manage to put the #! hash-bang at the first line of the script.

Create an action

In a conposite action we could do this:

Set up custom GitHub Actions shellSee on GitHub
1
2
3
4
5
6
- name: Setup up remote shell
run: |
cp ${{ github.action_path }}/Rscript \
/usr/local/bin/Rscript
chmod 775 /usr/local/bin/Rscript
shell: bash

Rscript can be also included in the action:

Rscript
1
2
3
#! /bin/bash
echo Calling R...
R -q --no-save < "$1"

This is a toy Rscript script that does not do anything useful. A real example could would set up some special environment to run the script in, e.g. this one runs it in a Docker container. GitHub Actions only supports x86_64 containers natively, but with the custom shell we can also use another architecture.

Use the new shell like this:

Test custom shell
1
2
3
4
5
- name: Test custom Rscript
run: |
getRversion()
R.version[["platform"]]
shell: Rscript {0}

Other actions that call the custom shell

The benefit of this setup is that if another action, e.g. r-lib/actions/setup-r-dependencies, uses Rscript as the shell, then our custom shell will be picked up automatically. E.g. if the custom shell runs the commands in a container, then the setup-r-dependencies action will also run in this container.

Limitations

If you are running the commands in a container, you’ll need to make sure that it has access to the runner’s disk. You’ll need to forward environment variables. If you are running an action with a custom shell, you’ll need to copy the ${GITHUB_ENV} and ${GITHUB_OUTPUT} files (possibly more!) back from the container, after the run.

Is this a hack?

Somewhat. If your custom shell forwards to a container or VM, and a composite action only uses the custom shell sometimes, but not always, then it will run some of its steps in the container, and others on the host. Which will probably end up in chaos.