Using github packages in Gradle and Maven projects

Github packages is an easy way to publish current snapshot versions. The biggest problem with it is access token requirement for accessing published packages (no free access).

Yes, I know, there is a super simple jitpack for this, but it can’t be used in some cases because instead of using your build output it searches for packages with some euristic. Moreover, jitpack generates its own BOM, overwriting your own. So for multi-module (or projects with more complex build) projects jitpack is not an option.

The biggest advantage of github packages is its simple integration with github actions (super simple publication). This covers one important CI use case: test commit - publish new snapshot - use snapshot to run tests.

Using already published package

Create access token

On any github page, in the upper top corner, click on your profile icon and select Settings

On the left bar click Developer setting

Click Tokens (classic) under Personal access tokens section

Choose Generate new token (classic) in the Generate new token dropdown

Most likely, github would ask you to confirm authentication here.

Set token description. Set token to never expire because this token will only grant a read right on github packages (moreover, you’ll be able to use the same token to access any github packages repository)

Select ONLY read:packages permission.

Of course, token with any other rights added will also work, but if you do so consider setting token expiration (as such token stealing might be sensitive).

IMPORTANT Github will show you token just once

Gradle project configuration

Add github packages repository either in build.gradle :

repositories {
    mavenCentral()
    maven {
        url  = 'https://maven.pkg.github.com/user/project'
        credentials {
            username = findProperty('gpr.user') ?: System.getenv("USERNAME")
            password = findProperty('gpr.key') ?: System.getenv("TOKEN")
        }
    }
}

OR in settings.gradle

dependencyResolutionManagement {
    repositories {
        mavenCentral()
        maven {
            url  = 'https://maven.pkg.github.com/user/project'
            credentials {
                username = settings.ext.find('gpr.user') ?: System.getenv("USERNAME")
                password = settings.ext.find('gpr.key') ?: System.getenv("TOKEN")
            }
        }
    }
}

Note: System.getenv part will be used if you’re goinf to run this project on github actions to consume credentials directly from environment variables. If you don’t plan to run project on CI, you can remove this section (see complete description at the bottom).

Put you access token in the global gradle configuration: ~/.gradle/gradle.properties

gpr.user=<your github user name>
gpr.key=<your github classic token>

Maven project configuration

Add credentials into ~/.m2/settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">            

    <servers>
        <server>
            <id>github</id>
            <username>USERNAME</username>
            <password>TOKEN</password>
        </server>
    </servers>
</settings>

Add github packages repository (directly in project or in profile inside settings.xml)

<repositories>    
  <repository>
        <id>github</id>
        <url>https://maven.pkg.github.com/user/project</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

IMPORTANT: repository id must be the same as server id in settings.xml (to apply configured authentication)

Publishing package in github action

Will show publishing only for gradle project. No special repository configuration is required - just publish new package.

In build.gradle add github packages repository:

 // https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle#publishing-packages-to-github-packages
publishing {
    repositories {
        maven {
            name = "GitHub"
            url = "https://maven.pkg.github.com/user/project"
            credentials {
                username = System.getenv("GITHUB_ACTOR")
                password = System.getenv("GITHUB_TOKEN")
            }
    }
}

GITHUB_ACTOR and GITHUB_TOKEN are provided automatically (no configuration required)

In “https://maven.pkg.github.com/user/projectuser - github user name, project - github project name (same as in github project url)

CI script:

name: Publish snapshot

on:
  push:

jobs:
  publish:
    name: Publish snapshot
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'zulu'
          java-version: 17

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3

      - name: Build without tests
        run: |
          chmod +x gradlew
          ./gradlew build -x test -x check --no-daemon

      - name: Publish
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SNAPSHOT: true
        run: ./gradlew publishAllPublicationsToGitHubRepository --no-daemon

Here on each commit project would be built without tests and new snapshot package published.

Real project with packages publication

In my project, I use a series of connected workflows:

  1. CI - run and test project

  2. If test success, publish new package

  3. Run examples project build with just published package

Use github package repo on gtihub actions

On github actions you don’t need an additional token to access githubg packages because GITHUB_TOKEN is already provided.

Gradle project

Just add repository, as it was shown in the first chapter.

repositories {
    maven {
        url  = 'https://maven.pkg.github.com/user/project'
        credentials {
            username = findProperty('gpr.user') ?: System.getenv("USERNAME")
            password = findProperty('gpr.key') ?: System.getenv("TOKEN")
        }
    }
}

This time, token would not be configured in global gradle properties, but, instead, would be provided in the environment variables.

Mapping these variables in the CI scipt:

jobs:
  build:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: examples
    name: Examples run
    env:
      USERNAME: ${{ github.actor }}
      TOKEN: ${{ secrets.GITHUB_TOKEN }}

Note that

    defaults:
      run:
        working-directory: examples

Required only in my case, because examples project is in github repository subfolder (examples).

Maven project

For maven project, credentials must be inside settings file. Custom settings file could reference environemnt variables maven-settings.xml:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <activeProfiles>
        <activeProfile>github</activeProfile>
    </activeProfiles>
    <profiles>
        <profile>
            <id>github</id>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>https://repo1.maven.org/maven2</url>
                </repository>
                <repository>
                    <id>github</id>
                    <url>https://maven.pkg.github.com/user/project</url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
        </profile>
    </profiles>

    <servers>
        <server>
            <id>github</id>
            <username>${env.USERNAME}</username>
            <password>${env.TOKEN}</password>
        </server>
    </servers>

</settings>

In my case, I run maven projects from gradle, using maven-exec-plugin and so could simply specify custom settings file:

tasks.register('maven-test', MavenExec) {
    goals 'test'
    options {
        settings = rootProject.file('maven-settings.xml')
    }
}

But for pure maven project, you could simply override existing settings.xml with a custom file with an additional CI job step.

Gradle plugin snapshots and github package

If you develop gradle plugin, you know that it requires two publications:

  1. Plugin itself

  2. Redirection pom for gradle plugins syntax

Publication method above will correctly publish both into github packages repository.

In order to use such snapshot, repositorty must be specified in settings.gradle’s pluginManagement section:

pluginManagement {
    repositories {
        mavenLocal()
        gradlePluginPortal()
        maven {
            url  = 'https://maven.pkg.github.com/user/project'
            credentials {
                username = settings.ext.find('gpr.user') ?: System.getenv("USERNAME")
                password = settings.ext.find('gpr.key') ?: System.getenv("TOKEN")
            }
        }
    }
}

For kotlin syntax (settings.gradle.kts):

pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
        maven {
            url  = uri("https://maven.pkg.github.com/user/project")
            credentials {
                username = extra.has("gpr.user").let { if (it) extra["gpr.user"] as String else System.getenv("USERNAME")}
                password = extra.has("gpr.key").let { if (it) extra["gpr.key"] as String else System.getenv("TOKEN")}
            }
        }
    }
}

Example project with “test - publish - run tests” cyle for gradle plugin

0
Subscribe to my newsletter

Read articles from Vyacheslav Rusakov directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vyacheslav Rusakov
Vyacheslav Rusakov