Archives containing JAR files are available as releases.
GitHub Gradle implements a way to get dependencies from a GitHub asset, so you don't need services like jitpack anymore
Using the plugins DSL:
plugins {
id "io.github.intisy.github-gradle" version "1.11.3"
}Using legacy plugin application:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "io.github.intisy.github-gradle:1.11.3"
}
}
apply plugin: "io.github.intisy.github-gradle"Once you have the plugin installed you can use it like so:
dependencies {
// OWNER:REPOSITORY:TAG resolves the main JAR from that release
githubImplementation "intisy:simple-logger:1.12.7"
// A 4th segment selects a classifier asset (simple-logger-api.jar)
githubImplementation "intisy:simple-logger:1.12.7:api"
// The reserved "all" classifier pulls every module of a multi-module release
githubImplementation "intisy:dough:1.3.0:all"
}Public releases resolve without a token, but GitHub caps unauthenticated API use at 60 requests/hour. Provide credentials in the auth block to raise that to 5,000/hour and to reach private repositories:
github {
auth {
token = "ghp_your_token" // a Personal Access Token, or
tokenFile = file("secrets/gh.txt") // a file that contains one
sshKey = file("~/.ssh/id_ed25519") // an SSH private key for git clone/pull
}
}When the auth block states nothing, the token is taken from GITHUB_TOKEN, then GH_TOKEN, then
from whatever gh is signed in with (gh auth token). So a developer who has run gh auth login
needs no token in any file, and a CI job that already exports GITHUB_TOKEN needs no configuration
either. Anything stated in auth still wins, and finding nothing at all is a supported state: the
plugin simply works unauthenticated.
Every standard Gradle configuration has a github counterpart, all using the OWNER:REPOSITORY:TAG[:CLASSIFIER] coordinate:
dependencies {
githubImplementation "intisy:simple-logger:1.12.7"
githubApi "intisy:java-utils:2.0.0" // leaks to consumers (needs the java-library plugin)
githubCompileOnly "intisy:annotations:1.0.0" // compile classpath only
githubCompileOnlyApi "intisy:annotations:1.0.0" // compile only + leaked (needs the java-library plugin)
githubRuntimeOnly "intisy:driver:1.0.0" // runtime classpath only
}Beyond a GitHub release, a dependency can also be resolved by cloning and building an arbitrary
git repository, or by downloading a jar directly over HTTP(S). Both are declared in a nested
sources { } block inside github { }, and both git { } and jar { } are repeatable:
github {
sources {
git {
url = "https://gitlab.com/me/lib.git"
ref = "main" // branch, tag or commit; optional, default the remote's default branch
dir = "java" // gradle project directory; optional, default the checkout root
modules = "routing contracts" // modules whose jars to take; optional, default the root project's jar
into = "implementation" // native configuration; optional, default "implementation"
}
jar {
url = "https://nexus.internal/libs/foo-1.0.jar"
header "Authorization", "Bearer ${myToken}"
sha256 = "80a981f3202da20cc46a0bf22e6e0ff40803e857ba6f4571496805c079162ffc" // optional; verified after download
into = "implementation"
}
}
}git { } clones any git host, not just github.com, checks out ref, builds it with its own
Gradle wrapper, and caches the result by resolved commit. dir moves the build to a repository
whose Gradle root is a subdirectory rather than the checkout root, and modules names the modules
of a multi-module build whose jars to take, one cached jar each. One clone and one build serve
every module. Together they let a library with several consumable modules be consumed straight from
a branch, with no release to cut for each change. jar { } downloads a jar with optional
request headers (for a private Nexus/Artifactory/S3-backed host) and an optional expected
sha256; a mismatch fails the build instead of silently using the wrong jar. A jar reachable
through more than one of the github* coordinates, sources { git { } }, or sources { jar { } }
is only ever added to the native configuration once.
A release asset is a bare jar: it carries no pom and no Gradle module metadata, so transitive
dependencies have to be restated by hand and variants such as test fixtures cannot be expressed at
all. A package can carry both. Declare the repositories a build resolves packages from in a nested
packages { } block inside github { }, and the dependencies themselves stay ordinary Gradle
dependencies:
github {
packages {
from "my-org/libs"
from("my-org/core") { group = "com.example" }
}
}
dependencies {
implementation "com.example:common:1.0.0"
testImplementation testFixtures("com.example:common:1.0.0")
}from is repeatable and takes owner/repo, which becomes
https://maven.pkg.github.com/owner/repo. The optional group narrows a repository to one
dependency group, so resolving anything else never queries it and never collects a 404 from it.
A multi-module build declares its sources once, at the root: every subproject can resolve from
them, since the modules are what actually have dependencies.
Credentials come from the same auth block, environment variables and gh login as everything
else, and a token is always required: GitHub Packages refuses an anonymous read even of a public
package. That is also why publishing to a package is not a replacement for attaching a release
asset, which on a public repository resolves with no credentials at all. Publish to both.
Configure the publishGithub extension and run gradle publishGithub to build the project and upload its JAR(s) as a GitHub release. Every field is optional:
publishGithub {
owner = "intisy" // auto-detected from the git remote if omitted
repo = "my-repo" // auto-detected from the git remote if omitted
version = "2.0.0" // defaults to project.version
tag = "v2.0.0" // defaults to version
releaseName = "Release 2.0.0" // defaults to tag
jar = file("build/libs/my-app.jar") // auto-selected from build/libs if omitted
}A multi-module repository uploads one asset per module with modules = true, which needs no list
of them:
publishGithub {
artifacts {
artifact { modules = true }
}
}Every project of the build that produces a jar becomes an asset, the root included when it produces
one of its own. A module uploads as <repo>-<module>.jar and the root as <repo>.jar, so a plain
OWNER:REPO:TAG coordinate resolves the root's jar and the :all classifier pulls the modules.
To publish the project's Maven publications to GitHub Packages in the same run, enable the nested
packages { } block. One publishGithub then reaches both destinations, so neither can drift
behind the other:
publishGithub {
packages {
enabled = true
}
}Every project of the build that applies maven-publish becomes a destination, the root and its
subprojects alike, and what is published is whatever publications each declares, or one created
from its java component if it declares none. So a multi-module repository whose root publishes
nothing of its own needs the block only once, at the root, exactly as
artifact { modules = true } covers the same modules on the release side. owner and repo fall
back to the ones publishGithub already resolves, so a build normally states nothing but
enabled. Uploading an asset whose name the release already carries replaces it, so republishing
one version, as a rolling snapshot does on every push, works rather than failing on the second run.
Run gradle updateGithubDependencies to rewrite every github* coordinate in your build files to the latest release tag, or gradle printGithubDependencies to list them.
github {
resilience {
// On a rate limit, fall back to the cached (outdated) jar or keep the current version instead of failing (default false)
skipOnRateLimit = true
}
cli {
enabled = true // route API calls through the local "gh" CLI, reusing its auth and higher limits (default false)
fallback = true // fall back to HTTP if gh is unavailable or a call fails (default true)
}
}Cloning repositories, resolving releases and downloading assets are also published as a small, Gradle-free library, separate from the plugin jar:
dependencies {
implementation "io.github.intisy:github-gradle-api:1.3.8"
}The entry point is GitHubApi.create(...). It needs a GitHubConfig (the access token and
auth/cli/resilience settings); GitHubConfig.builder() assembles one without any Gradle DSL,
every builder method is optional, and calling build() with none produces a config for fully
anonymous, unauthenticated access:
import io.github.intisy.gradle.github.api.*;
import io.github.intisy.gradle.github.api.config.*;
import java.io.File;
import java.util.Collections;
GitHubConfig config = GitHubConfig.builder()
.token(System.getenv("GITHUB_TOKEN"))
.build();
GitHubApi api = GitHubApi.create(config, new ResourceSettings());
// A GitHub release
File releaseJar = api.releases().downloadJar("intisy", "simple-logger", "1.12.7")
.orElseThrow(() -> new IllegalStateException("jar not found"));
// An arbitrary git repository, cloned and built
File gitJar = api.sourceBuilds().buildFromGit("https://gitlab.com/me/lib.git", "main");
// A direct jar URL, with an optional header and sha256 check
File urlJar = api.downloads().download("https://nexus.internal/libs/foo-1.0.jar",
Collections.singletonMap("Authorization", "Bearer " + System.getenv("NEXUS_TOKEN")),
"80a981f3202da20cc46a0bf22e6e0ff40803e857ba6f4571496805c079162ffc");api.repositories(), api.publishing(), api.sourceBuilds(), api.downloads() and
api.resolver() reach the same capabilities the plugin's own tasks use. GitHubApi.create also
accepts a GitHubLogger argument if you want diagnostics sent somewhere other than System.err,
and GitHubApi.create() with no arguments defaults to an anonymous config for quick,
unauthenticated use.