{{announcement.body}}
{{announcement.title}}

Travis CI to GitHub Actions: What to Know About Migration

DZone 's Guide to

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.

· Java Zone ·
Free Resource

Not 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):

YAML
 




xxxxxxxxxx
1
28


1
language: java
2
 
          
3
jdk: 
4
- openjdk8
5
- openjdk11
6
 
          
7
addons:
8
  apt:
9
    packages:
10
    - libxml2-utils 
11
 
          
12
before_install:
13
- source ./src/ci/before_install.sh 
14
 
          
15
# skip the Travis-CI install phase because Maven handles that directly
16
install:
17
- 'true'
18
 
          
19
script:
20
- "./src/ci/build.sh" 
21
 
          
22
after_success:
23
- bash <(curl -s https://codecov.io/bash) -f coverage/target/site/jacoco/jacoco.xml 
24
1. Build and test the project with Java 8 and 11
25
2. Optional, but provides a quick way to grab the version from a pom.xml
26
3. Runs a script to setup environment variables
27
4. Runs a build script
28
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":

Shell
 




x


 
1
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.

YAML
 




xxxxxxxxxx
1
24


1
name: Java CI
2
 
          
3
on: 
4
  push:
5
  pull_request:
6
  schedule:
7
    - cron: '0 0 * * 0' # weekly
8
 
          
9
jobs:
10
  build:
11
    runs-on: ubuntu-latest 
12
 
          
13
    steps:
14
      - uses: actions/checkout@v2
15
      - name: Set up JDK 1.8
16
        uses: actions/setup-java@v1
17
        with:
18
          java-version: 1.8 
19
      - name: Build with Maven
20
        run: ./mvnw -B verify 
21
1. Run the build on all branches, pull requests, and scheduled weekly
22
2. Target OS
23
3. Use Java 8
24
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:

Shell
 




xxxxxxxxxx
1


 
1
git add .github/workflows/build.yml
2
git commit -m "add github actions script"
3
git push origin github-actions-test


Navigate to the "Actions" tab of your GitHub repository to see the build, for example:

Actions tab

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:

Shell
 




xxxxxxxxxx
1


 
1
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":

Java
 




xxxxxxxxxx
1
30


1
name: Java CI
2
 
          
3
on:
4
  push:
5
  pull_request:
6
  schedule:
7
    - cron: '0 0 * * 0' # weekly
8
 
          
9
jobs:
10
  build:
11
    runs-on: ubuntu-latest
12
    name: Java ${{ matrix.java }} 
13
    strategy: 
14
      matrix:
15
        java: [8, 11] 
16
 
          
17
    steps:
18
      - uses: actions/checkout@v2
19
      - name: Set up JDK ${{ matrix.java }} 
20
        uses: actions/setup-java@v1
21
        with:
22
          java-version: ${{ matrix.java }} 
23
      - name: Build and Test
24
        run: source ./src/ci/before_install.sh && ./src/ci/build.sh
25
1. The strategy node defines the matrix options, similar to Travis CI. You can also define multiple operating systems here
26
2. Defines the versions of Java to support
27
3. Adds a user-friendly name; otherwise the default name will be "build (8)" and "build (11)"
28
4. Updates the display name to be user-friendly
29
5. Uses the matrix.java expression to set the version of java installed by the setup-java action
30
 
          


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.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:

YAML
 




xxxxxxxxxx
1


1
after_success:
2
- 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:

YAML
 




xxxxxxxxxx
1


 
1
- uses: codecov/codecov-action@v1
2
  with:
3
   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:

YAML
 




xxxxxxxxxx
1


1
jobs:
2
  build:
3
    env:
4
      SOME_GLOBAL_ENV_VAR_NAME: a-value


Or scoped to the context of a single step:

YAML
 




xxxxxxxxxx
1


1
steps:
2
  - name: scope to a single step
3
    env:
4
      SOME_ENV_VAR_NAME: your-value
5
    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:

YAML
 




xxxxxxxxxx
1


1
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 against

  • TRAVIS_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:

YAML
 




x


 
1
name: Java CI
2
 
          
3
on:
4
  push:
5
  pull_request:
6
  schedule:
7
    - cron: '0 0 * * 0' # weekly
8
 
          
9
jobs:
10
  build:
11
    runs-on: ubuntu-latest
12
    name: Java ${{ matrix.java }}
13
    strategy:
14
      matrix:
15
        java: [8, 11]
16
    env:
17
      TRAVIS_REPO_SLUG: ${{ github.repository }} 
18
      TRAVIS_BRANCH: ${{ github.head_ref }} 
19
      TRAVIS_PULL_REQUEST: ${{ github.event.number }} 
20
    steps:
21
      - uses: actions/checkout@v2
22
 
          
23
      - name: Set ENV variables
24
        run: |
25
          echo "::set-env name=TRAVIS_BRANCH::${TRAVIS_BRANCH:-$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')}" 
26
          echo "::set-env name=TRAVIS_SECURE_ENV_VARS::$(if [ -z "${{ secrets.something }}" ]; then echo "false"; else echo "true"; fi)" 
27
          echo "::set-env name=TRAVIS_EVENT_TYPE::$(if [ "schedule" == "${{ github.event_name }}" ]; then echo "cron"; else echo "${{ github.event_name }}"; fi)" 
28
 
          
29
      - name: Print Travis ENV vars 
30
        run: |
31
          echo "TRAVIS_BRANCH: ${TRAVIS_BRANCH}"
32
          echo "TRAVIS_PULL_REQUEST: ${TRAVIS_PULL_REQUEST}"
33
          echo "TRAVIS_REPO_SLUG: ${TRAVIS_REPO_SLUG}"
34
          echo "TRAVIS_SECURE_ENV_VARS: ${TRAVIS_SECURE_ENV_VARS}"
35
 
          
36
      - name: Set up JDK ${{ matrix.java }}
37
        uses: actions/setup-java@v1
38
        with:
39
          java-version: ${{ matrix.java }}
40
 
          
41
      - name: Build and Test
42
        run: source ./src/ci/before_install.sh && ./src/ci/build.sh
43
 
          
44
      - uses: codecov/codecov-action@v1
45
        with:
46
         file: target/site/jacoco/jacoco.xml
47
         fail_ci_if_error: true
48
1. TRAVIS_REPO_SLUG is the same as github.repository
49
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
50
3. Another easy one, TRAVIS_PULL_REQUEST is github.event.number on pull_request jobs
51
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>
52
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
53
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
54
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!

Topics:
github actions, java, okta, travis ci

Published at DZone with permission of Brian Demers , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}