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/project” user - 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:
CI - run and test project
If test success, publish new package
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:
Plugin itself
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
Subscribe to my newsletter
Read articles from Vyacheslav Rusakov directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
