Data report"State of code review 2024" is now liveRead the full report

Using GitHub Actions with Gradle

Kenny DuMez
Kenny DuMez
Graphite software engineer

GitHub Actions provides a platform that can be used for continuous integration and continuous deployment (CI/CD) of Java projects using Gradle. This guide covers how to configure GitHub Actions to cache dependencies, build projects, and publish artifacts using Gradle.

Before diving into specific tasks like caching or publishing, let's first set up a basic Gradle workflow on GitHub Actions. Here’s how to get started:

  1. Create a workflow file: In your GitHub repository, create a .github/workflows directory if it doesn’t already exist. Inside this directory, create a workflow file named gradle.yml.

  2. Define workflow configuration: At the beginning of your workflow file, define the name of the workflow and the events that trigger it. For a Gradle project, you might want to trigger the workflow on push events to the main branch and on pull request events targeting the main branch.

    Terminal
    name: Gradle build
    on:
    push:
    branches: [main]
    pull_request:
    branches: [main]
  3. Set up the job: Define a job that runs on a GitHub-hosted runner. For Java projects, ubuntu-latest is a common choice.

    Terminal
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 11
    uses: actions/setup-java@v3
    with:
    java-version: '11'
    distribution: 'adopt'
  • jobs: This key starts the definition of one or more jobs. Jobs are individual units of work that make up a workflow. Each job runs independently from each other unless specifically configured to do otherwise.

  • build: Under jobs, this is the identifier for a specific job. You can name it anything, but here it's named build to indicate it likely compiles and builds the project.

  • runs-on: ubuntu-latest This specifies the type of runner that the job will execute on. A runner is a server that has the GitHub Actions runner application installed. ubuntu-latest refers to the latest stable version of Ubuntu Linux provided by GitHub at the time the workflow runs. It's a popular choice for many development environments, especially for Java projects.

  • steps: This key defines a sequence of tasks (steps) that are executed as part of the job. Each step can run commands or actions.

    • - uses: actions/checkout@v3 This step uses a predefined action (actions/checkout@v3). This action checks out your repository onto the runner, allowing the workflow to access its contents.

    • - name: Set up JDK 11 This step specifically sets up the Java Development Kit (JDK), which is necessary for building Java applications.

      • uses: actions/setup-java@v3 It uses another predefined action, actions/setup-java@v3, which sets up a specific version of Java JDK provided by the specified distribution.
      • with: This key starts the definition of inputs for the setup-java action.
        • java-version: '11' Specifies the version of the JDK to be installed. Here, it's version 11.
        • distribution: 'adopt' Specifies which distribution of Java to use. The 'adopt' value refers to AdoptOpenJDK, a popular distribution of the OpenJDK project.

Caching dependencies can significantly speed up build times by reducing the time spent downloading artifacts. GitHub Actions provides built-in support for caching:

  1. Cache Gradle Dependencies: After setting up Java, add steps to cache the Gradle dependencies. The cache key can include a hash of the build.gradle or gradle-wrapper.properties file to ensure the cache is updated when dependencies change.

    Terminal
    - name: Cache Gradle packages
    uses: actions/cache@v3
    with:
    path: |
    ~/.gradle/caches
    ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
    restore-keys: |
    ${{ runner.os }}-gradle-
  • path:

    • Purpose: Specifies the directories whose contents you want to cache.
    • Details: The path field lists the directories on the runner that will be cached. Here, ~/.gradle/caches stores the Gradle dependency cache, and ~/.gradle/wrapper stores the Gradle wrapper data. By caching these directories, subsequent runs can use the cached data to avoid redownloading the same dependencies.
  • key:

    • Purpose: Unique identifier for the cache. This key changes if the contents you want to cache change, triggering the creation of a new cache entry.
    • Details: The key is composed of the runner's operating system (${{ runner.os }}), a static string (gradle-), and a hash that represents the contents of all .gradle files and gradle-wrapper.properties in the repository. The hashFiles function generates a hash based on the contents of these files, ensuring that any change in dependencies (reflected in these files) results in a different cache key, thereby invalidating the old cache and creating a new one.
  • restore-keys:

    • Purpose: Provide fallback keys if an exact match for the key is not found. This helps in partially restoring cache to speed up execution even if an exact match isn't available.
    • Details: restore-keys provides a list of alternative keys to use for restoring the cache if the exact key does not have a cache hit. Here, it starts with the operating system and the prefix gradle-, meaning it will match any cache key that starts with this prefix, even if the hash doesn't match. This allows the use of a less specific cache, which might still be beneficial if exactly matching cache data is not available.

To build your Gradle project:

  1. Build the project: After caching the dependencies, add a step to execute the Gradle build.

    Terminal
    - name: Build with Gradle
    run: ./gradlew build

This command runs the build task, which compiles your code, runs tests, and assembles the artifacts.

Publishing artifacts or packages can be done using the publish Gradle task, which you need to configure in your build.gradle file using plugins like maven-publish.

  1. Publish artifacts: To publish artifacts to a repository, you might want to add conditional steps that only run on a push to the main branch, or use secrets to handle credentials securely.

    Terminal
    - name: Publish package
    if: github.ref == 'refs/heads/main'
    run: ./gradlew publish
    env:
    GRADLE_PUBLISH_KEY: ${{ secrets.PUBLISH_KEY }}
    GRADLE_PUBLISH_SECRET: ${{ secrets.PUBLISH_SECRET }}

This configuration assumes that the publish task is set up in your build.gradle to handle publication logistics, and that you are using repository secrets to store publication credentials securely.

For further information on using GitHub Actions with Gradle, see the official Gradle documentation.

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2