Thanks to a pull-request "Install TagBot as a GitHub Action", which was created by a bot, I realized now is the time to move one of my Julia projects, Recommendation.jl, from Travis CI to GitHub Actions.
Since I haven't had enough time to work on the project for a year, migration was not straightforward as I expected. Hence, this post summarizes what I've undergone to get there. As always, I referred to some of the most actively/recently maintained official Julia packages (e.g., Statistics.jl, Tokenize.jl) to see what is the most 'modern' way to organize a Julia project.
Original .travis.yml
I've written the following Travis CI configuration more than a year ago:
language: julia
os:
- linux
- osx
julia:
- 0.7
- 1.0
- nightly
matrix:
allow_failures:
- julia: nightly
notifications:
email: false
after_success:
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())'
jobs:
include:
- stage: "Documentation"
julia: 1.0
os: linux
script:
- julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd()));
Pkg.build("Recommendation");
Pkg.instantiate()'
- julia --project=docs/ docs/make.jl
after_success: skip
The workflow breaks down into three pieces:
- Unit testing over multiple Julia versions
- Profiling test coverage
- Building and publishing a documentation page to GitHub Pages
Running a Workflow over multiple Julia versions
First and foremost, what I like about GitHub Actions is the easiness of using a build matrix.
For every pull-requests onto the master branch, as well as push event to the master with an arbitrary tag, the following workflow executes 3x3=9 jobs for 3 Julia versions and 3 operating systems:
name: CI
on:
pull_request:
branches:
- master
push:
branches:
- master
tags: '*'
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.0'
- '1' # automatically expands to the latest stable 1.x release of Julia
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
arch:
- x64
steps:
# (actual tasks are defined here)
Having a comprehensive build matrix is particularly important for Julia because Julia is one of the rare programming languages that aggressively introduce backward-incompatible changes as the version number grows.
Notice that I dropped Julia 0.7 from the matrix when I switched out the Travis config with GitHub Workflow; considering the stable version is 1.5.3 as of Nov 2020, it's reasonable not to support Julia < 1.0 anymore.
Testing and reporting coverage
Next, we could leverage julia-actions/julia-runtest and julia-actions/julia-processcoverage as follows:
jobs:
test:
# (define a build matrix - see above)
steps:
- uses: actions/[email protected]
- uses: julia-actions/[email protected]
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/[email protected]
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/[email protected]
- uses: julia-actions/[email protected]
- uses: julia-actions/[email protected]
- uses: codecov/[email protected]
with:
file: lcov.info
As long as your repository is registered to Codecov.io, test coverage is automatically reported in a pull-request. See this comment as an example.
Documenting and hosting on GitHub Pages
Finally, we can define a dedicated job for building and publishing a documentation page using Documenter.jl.
Luckily, there is an official step-by-step guide: Hosting Documentation, and the definition of the job looks like:
jobs:
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: julia-actions/[email protected]
with:
version: '1'
- run: |
julia --project=docs -e '
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()'
- run: julia --project=docs docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
DOCUMENTER_DEBUG: true
secrets.GITHUB_TOKEN
is automatically set by GitHub Workflow.
Meanwhile, secrets.DOCUMENTER_KEY
can be generated as explained in Authentication: SSH Deploy Keys and be configured as a custom GitHub repository secret at: https://github.com/{user}/{repository}/settings/secrets/
.
It is important to note that we should accordingly update TagBot Workflow:
- uses: JuliaRegistries/[email protected]
with:
token: ${{ secrets.GITHUB_TOKEN }}
+ ssh: ${{ secrets.DOCUMENTER_KEY }}
Moreover, in case you're using an old version of Documenter.jl like me, you probably need to update the version in docs/Project.toml
first of all because GitHub Actions are supported since Documenter.jl 0.24:
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
[compat]
- Documenter = "~0.20"
+ Documenter = "0.24"
That's all, and here is a complete set of GitHub Actions Workflows for my Julia project.
Author: Takuya Kitazawa
Takuya Kitazawa (a.k.a. takuti) is working on machine learning, data science, and product development at Treasure Data.
Opinions are my own.
Popular articles
- 2020-02-07
- Why a Data Science Engineer Becomes a Product Manager
- 2018-10-26
- Apache Hivemall at #ODSCEurope, #RecSys2018, and #MbedConnect
- 2017-02-25
- Parallel Programming vs. Concurrent Programming
Support
See also
- 2019-07-26
- Lightning Talk about Recommender Systems in Julia at #JuliaCon 2019
- 2019-03-31
- Publishing My Master's Thesis with Documenter.jl
- 2017-06-26
- Deploying Static Site to GitHub Pages via Travis CI