Travis CI to GitHub Actions: What to Know About Migration
In this article, we take a look at how to migrate your existing repository to GitHub Actions with the Okta Maven Plugin.
Join the DZone community and get the full member experience.
Join For FreeNot long ago, a colleague brought to my attention that I was configuring Travis CI on new GitHub repos. They suggested I use GitHub Actions instead. I’d looked into Actions back when it was in beta, but didn’t delve too deeply into it because of some issues I found pretty early on. Plus, I’d been using Travis-CI for a bit now and didn’t have the bandwidth to learn another new thing at the time.
However, if GitHub Actions is as good as everyone says, it would be one less external service to deal with. This article is going to walk you through migrating your existing repository to GitHub Actions. And I’ll be sharing my findings along the way!
The Travis-CI Config
I’m going to use the Okta Maven Plugin for my repository. If you are a Java developer, it can get you set up with Okta in 30 seconds, check out the project’s readme. Even though I’m using a Java project, it’s possible to modify these steps for any project.
My original .travis.yml
configures a simple matrix build. Matrix builds contain multiple jobs that run in parallel with different configurations. These can be as complex as you need; run a build on multiple operating systems, against different versions of tools, and with different environment variables. A matrix build that defines two operating systems, three environment variables, and two versions of Java would result in 12 individual builds (2 x 3 x 2
).
My project expands into just two builds, one for each Java (8 and 11):
xxxxxxxxxx
language java
jdk
openjdk8
openjdk11
addons
apt
packages
libxml2-utils
before_install
source ./src/ci/before_install.sh
# skip the Travis-CI install phase because Maven handles that directly
install
'true'
script
"./src/ci/build.sh"
after_success
bash <(curl -s https://codecov.io/bash) -f coverage/target/site/jacoco/jacoco.xml
1. Build and test the project with Java 8 and 11
2. Optional, but provides a quick way to grab the version from a pom.xml
3. Runs a script to setup environment variables
4. Runs a build script
5. Uploads code coverage to codecov.io
My build scripts do have a few references to Travis-CI environment variables like TRAVIS_REPO_SLUG
and TRAVIS_SECURE_ENV_VARS
, so I’ll need to find a replacement for those too.
First Steps to GitHub Actions
The first step is to create a branch in your project. I called mine "github-actions-test":
git checkout -b github-actions-test
GitHub will look at all of the YAML files in the .github/workflows
directory in your project. I named mine, build.yml
.
Why do all of these services still use the yml
extension? The yaml.org FAQ even recommends yaml
! Is anyone out there still supporting DOS 8.3 file names?
To keep this simple, I’m going to use a template for an Apache Maven projects on the first pass to make sure everything is working and then circle back and set up a matrix build with my custom build scripts.
xxxxxxxxxx
name Java CI
on
push
pull_request
schedule
cron'0 0 * * 0' # weekly
jobs
build
runs-on ubuntu-latest
steps
uses actions/checkout@v2
name Set up JDK 1.8
uses actions/setup-java@v1
with
java-version1.8
name Build with Maven
run ./mvnw -B verify
1. Run the build on all branches, pull requests, and scheduled weekly
2. Target OS
3. Use Java 8
4. Run a command
Scheduled jobs are ONLY run against the "default" branch. I lost hours of my life trying to figure this out.
Test it out by adding the file and pushing the branch:
xxxxxxxxxx
git add .github/workflows/build.yml
git commit -m "add github actions script"
git push origin github-actions-test
Navigate to the "Actions" tab of your GitHub repository to see the build, for example:
Of course, if you create a pull request the build status will be reported there as well.
My build was all green, so I’ll update the run
attribute with my custom script:
xxxxxxxxxx
run: source ./src/ci/before_install.sh && ./src/ci/build.sh
My before_install.sh
script just sets environment variables, so it needs to be run in the same context block as my build.sh
. I’ll cover a few other options for environment variables below.
Commit and push the changes again.
Woot! Another successful build!
Matrix Builds with GitHub Actions
Matrix builds are configured a little differently in Actions than Travis CI, and it took some head-scratching before I understood the differences between the two. With Travis CI the configuration is declarative, unlike GitHub Actions which uses an expression syntax for everything. Variables are defined in a matrix
element which are then used in "expressions" throughout your configuration. To build against multiple versions of Java I needed to define strategy.matrix.java = [8, 11]
and then use the expression ${{ matrix.java }}
where I previously had hard coded "1.8":
xxxxxxxxxx
name: Java CI
on:
push:
pull_request:
schedule:
- cron: '0 0 * * 0' # weekly
jobs:
build:
runs-on: ubuntu-latest
name: Java ${{ matrix.java }}
strategy:
matrix:
java: [8, 11]
steps:
- uses: actions/checkout
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java
with:
java-version: ${{ matrix.java }}
- name: Build and Test
run: source ./src/ci/before_install.sh && ./src/ci/build.sh
1. The strategy node defines the matrix options, similar to Travis CI. You can also define multiple operating systems here
2. Defines the versions of Java to support
3. Adds a user-friendly name; otherwise the default name will be "build (8)" and "build (11)"
4. Updates the display name to be user-friendly
5. Uses the matrix.java expression to set the version of java installed by the setup-java action
Once again, commit and push to your branch. Then, head over to the "Actions" tab on your GitHub project to see the matrix build result.
Add Other GitHub Actions
My original travis.yml
included an "after success" step to upload code coverage data. This step simply executes a bash script:
xxxxxxxxxx
after_success
bash <(curl -s https://codecov.io/bash) -f target/site/jacoco/jacoco.xml
I know, I’m not a fan of piping remote URLs to bash either.
The same command could be run directly with GitHub Actions too, but the GitHub Marketplace contains a whole host of third-party actions you can plug in into your build; a quick search for "Codecov" turned up what I was looking for!
Third-party actions use the same format as a GitHub Action, uses: <org>/<repo>@<tag>
. For Codecov the usage looks like this:
xxxxxxxxxx
uses codecov/codecov-action@v1
with
file target/site/jacoco/jacoco.xml
This action does the same thing as the original bash script under the covers, the syntax is just more declarative.
Replace Travis CI Environment Variables
I mentioned before that my bash scripts used a few TRAVIS_*
environment variables. They also default to reasonable values where possible, which allows for running the script locally, or via GitHub Actions. To keep things focused in this post, I’ll walk through setting the Travis CI environment variables and tease the implementation-specific bits out of my build in a future post.
There are two ways to set environment variables with GitHub Actions: declare them directly in the YAML file or use a special syntax to output them to the console.
Declare them globally for your whole job:
xxxxxxxxxx
jobs
build
env
SOME_GLOBAL_ENV_VAR_NAME a-value
Or scoped to the context of a single step:
xxxxxxxxxx
steps
name scope to a single step
env
SOME_ENV_VAR_NAME your-value
run echo "my env var: ${SOME_ENV_VAR_NAME}"
You can also write a script to output a specific format: ::set-env name=<var-name>::<value>
. In practice, that looks like this:
xxxxxxxxxx
run echo "::set-env name=SOME_ENV_VAR_NAME::your-value"
The GitHub Actions context variables and Travis CI environment variables don’t always line up one-to-one, but I was able to find the Actions equivalent for the following:
TRAVIS_BRANCH
- The branch/tag the build is running againstTRAVIS_EVENT_TYPE
- For scheduled tasks, the value will be "cron"TRAVIS_PULL_REQUEST
- The PR number, or "false"TRAVIS_SECURE_ENV_VARS
- This value is "true" when "secrets" are available to a build
Here is my final GitHub Actions build.yml
:
x
name Java CI
on
push
pull_request
schedule
cron'0 0 * * 0' # weekly
jobs
build
runs-on ubuntu-latest
name Java $ matrix.java
strategy
matrix
java 8 11
env
TRAVIS_REPO_SLUG $ github.repository
TRAVIS_BRANCH $ github.head_ref
TRAVIS_PULL_REQUEST $ github.event.number
steps
uses actions/checkout@v2
name Set ENV variables
run
echo "::set-env name=TRAVIS_BRANCH::${TRAVIS_BRANCH:-$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')}"
echo "::set-env name=TRAVIS_SECURE_ENV_VARS::$(if [ -z "${{ secrets.something }}" ]; then echo "false"; else echo "true"; fi)"
echo "::set-env name=TRAVIS_EVENT_TYPE::$(if [ "schedule" == "${{ github.event_name }}" ]; then echo "cron"; else echo "${{ github.event_name }}"; fi)"
name Print Travis ENV vars
run
echo "TRAVIS_BRANCH: ${TRAVIS_BRANCH}"
echo "TRAVIS_PULL_REQUEST: ${TRAVIS_PULL_REQUEST}"
echo "TRAVIS_REPO_SLUG: ${TRAVIS_REPO_SLUG}"
echo "TRAVIS_SECURE_ENV_VARS: ${TRAVIS_SECURE_ENV_VARS}"
name Set up JDK $ matrix.java
uses actions/setup-java@v1
with
java-version $ matrix.java
name Build and Test
run source ./src/ci/before_install.sh && ./src/ci/build.sh
uses codecov/codecov-action@v1
with
file target/site/jacoco/jacoco.xml
fail_ci_if_errortrue
1. TRAVIS_REPO_SLUG is the same as github.repository
2. The branch name is tricky. For pull_request jobs it equals github.head_ref. For push jobs it needs to be updated in #4
3. Another easy one, TRAVIS_PULL_REQUEST is github.event.number on pull_request jobs
4. For non-pull-request builds, the TRAVIS_BRANCH env var will be empty. Extract it from GITHUB_REF in the format of refs/heads/<branch-name>
5. There is no generic way to detect if secrets are present so pick a name of a secret you have defined and wrap it in an if/else
6. The push and pull_request event types from Travis CI line up with GitHub Actions, but the "cron" needs to be worked around with another bash if/else
7. Tried and true print line debugging
If you are trying to figure out what properties are available in the build context, you can add a run: echo "${{ toJson(github) }}"
line to print them all.
While it’s possible to use the Travis CI environment variables, I don’t recommend it. It’s a great option if you want to test out GitHub Actions or need to run them in parallel in the short term, but to say this option is ugly and difficult to debug, is an understatement. Cleaning up these scripts is out of the scope of this post.
Learn More About CI and Secure Applications
Overall I’m happy with GitHub Actions. I was able to migrate my build with minimal effort. The GitHub Marketplace has a lot of potential. I can use the ability to define actions across multiple repositories, which has me excited. Going forward, I’ll be migrating my other projects to Actions.
If you want to learn more about CI or building secure applications, check out these links:
If you enjoyed this blog post and want to see more like it, follow @oktadev on Twitter, subscribe to our YouTube channel, or follow us on LinkedIn. As always, please leave your questions and comments below—we love to hear from you!
Published at DZone with permission of Brian Demers, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments