Running our code and tests on Github
March 31, 2024
Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you’d like, including CI/CD, and combine actions in a completely customized workflow. - Github Action documentation
Allow you to execute code on a remote server hosted by Github.
There is a tab for Github Actions for every repository.
You are running code on someone else’s server, so there is a limit to how much you can run. Github Action Billing.
But, Github Actions are free for public repositories.
And, you should have 2,000 minutes of run time for private repositories for a free account.
So practically, you can run most things without a worry.
By default, Github Actions will run on a server with
Which is to say, these server resources are not super big. Probably your computer will be faster at everything.
But they also should be big enough to run most projects.
You can upgrade to Github Actions running on servers that allocate more resources to you.
Up to
But this will start costing actual money to do (Github Larger Runners). You should probably be looking at running your code on Brown’s HPC if you need something close to this size.
Github Actions is a service that allows you to run workflows.
from the Github Actions documentation
Events are what trigger a workflow to run.
A runner is a server—hosted by Github—that will run your jobs.
There is always one runner for each job.
They are virtual machines that can have Ubuntu Linux, Microsoft Windows, or macOS operating systems.
They default to a small amount of computing power, but can be upgraded.
Jobs are a set of steps to be run.
One job gets assigned to each runner.
Jobs can be run in parallel (default) or in sequence.
A workflow could have one or more jobs.
Steps are the actual commands given to the runner (the server).
Steps can be either:
Steps are where we will say “run this code” or “execute this R script”
This is not to be confused with Github Actions which is the name of the whole service.
An action is…
Basically, an action performs many steps (kind of like a function call).
Github provides some default actions, and you can use actions written by other users.
Github Actions is a service that allows you to run workflows.
from the Github Actions documentation
name: learn-github-actions
run-name: ${{ github.actor }} is learning GitHub Actions
on: [push]
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
We are going to look at each piece of this in turn.
I am not trying to have you learn how to write one of these. In general, you will be copying and maybe lightly editing ones that already exist. The goal is to be able to read one.
${{ github.actor }}
is grabbing your Github username as a variable${{ }}
, that is referencing a variable
You could also only trigger the workflow when certain files are edited, or when the repository is forked, there are many options.
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
runs-on
“ubuntu-latest”You can also choose to run on a specific prior version of an OS.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
This job has 4 steps (count the dashes).
Remember, the steps are the actual commands being run on the server.
The uses
shows that this is running an action
actions/checout@v4
is a default Github action
The next step is also an action.
actions/checout@v4
is a default Github action
with:
command sets an option for the actionWhen runners launch a virtual machine for us, they only have the operating system installed (and some other basics). Anything else we want to use we have to install with an action or install ourselves.
These last two steps are not actions, but just shell commands. You can see that by the run
keyword.
npm install -g bats
is a command telling “node.js” to install the bats package.
bats -v
is a command asking for the version of the package bats.
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
Now we know how this defines a Github workflow.
Let’s see where we put this in Github.
Github Workflows live a specific folder in your repository:
“.github/workflows/”
Each workflow is defined by a “yaml” file.
Once you define this folder, and a “yaml” file in it, Github will launch a workflow for you defined by the file.
For your first workflow, it may be easier to define it through Github.
This is how you will do your assignment and also what is detailed in the coding example.
We have seen how Github Actions can run code for us on virtual machines.
How do we get it to run R code?
We first have to tell it to install R, then give it R code to run.
This is a very basic Github workflow that runs print("hello world")
in R.
First, we had to install R on the virtual machine.
Then, we simply executed our R command.
Rscript -e 'command'
allows you to run any one-line R commandon: [push]
name: Run R code example
jobs:
run-some-R-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
- run: Rscript -e 'print("hello world")'
But what if we want to run more than one line?
Usually we will have R scripts written in our repository that we want to run.
Let’s assume we have a “main.r” file, we can run it with… . . .
This is still a single step.
The name:
line just names the step and is optional.
The run:
line is broken up into multiple lines with the |
symbol.
And then an R script can be run by calling Rscript name-of-file.r
.
Remember, these virtual machines come with nothing installed.
Which means we don’t have access to any packages.
A couple of options:
install.packages()
or renv::install()
renv
to create a lockfile, and then simply run renv::restore()
Option 2 is far better to option 1. In fact, there is a r-lib action that will restore a renv
environment for us.
renv
on: [push]
name: Run Main.r
jobs:
RunMain:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: r-lib/actions/setup-r@v2
- uses: r-lib/actions/setup-renv@v2
- name: Run main.r
run: |
Rscript main.r
This will restore our environment to whatever state of packages are listed in the lockfile.
It goes beyond that and will cache the packages, so the next time our workflow runs, it’s much faster.
Pre-built Github Actions for R
They also have a set of example workflows:
Continuous integration (CI) is a programing practice / framework.
The idea is that team of developers write their own sections of code separately, but continually integrate their code to a common repository.
This is in contrast to a system where developers write their code on their own machine, then everyone merges their code together at the end and tries to fix any errors then.
You sometimes will see CI/CD for Continuous Integration/Continuous Deployment. Which adds that the main repository of code is automatically shipped/deployed so customers/other people can use it.
Github allows users to effectively have a CI practice for their code.
If working with multiple users, they can all share a common repository.
And Github Actions can build the code and run tests automatically.
How could CI be useful for us?
Here are a few Github Actions I have used for projects:
devtools::check()
on an R package I was writingstylr
and lintr
which check your code’s style formating