git-helpers.gradle.kts clones a git
repository at configuration time, fetches the latest changes, and checks out a
branch — so shared build logic can be versioned in one repo and pulled into many
projects without vendoring it.
It uses plain git (over SSH for a GitHub owner/name, or as-is for a local
file:// URI), so it relies on your existing git credentials — no token
parameter is required.
The work is split across two Gradle ValueSources, each of which makes its
result a first-class configuration-cache input. Gradle re-runs a
ValueSource's obtain() on every build to check whether its value changed, so
the build re-runs dependent logic only when the git state actually advances — no
manual version bump needed.
GitRepositorySource— clonesrepointotargetDirectory(only if that directory doesn't already exist; the clone runs in its parent, using its name as the clone target), then runsgit fetchunless fetching is skipped. An existing clone that can no longer be updated fromrepois deleted and recloned, with a warning: its origin points at a different repository, it is stuck mid-merge, or the fetch reveals its checked-out branch has diverged from origin (upstream history rewritten/force-pushed, so origin can't be merged). A clone merely ahead (deliberate local commits, preserved) or behind (fast-forwardable) is left in place. It returns the SHA of the latest commit across all branches (git rev-list --branches --remotes --max-count=1), which changes whenever any branch gets a new commit.GitBranchSource— checks outbranchin an already-clonedtargetDirectoryand fast-forwards it to the already-fetchedorigin/<branch>(a localgit merge, no network). It returns the branch's tip commit SHA (git rev-parse <branch>), which changes when that branch's tip changes.
Three convenience functions are registered on extra:
cloneAndCheckoutGitRepositoryBranch— the full path: clone (if needed), fetch, check outbranch, and fast-forward it to the fetched remote-tracking branch. Backed by both value sources.checkoutGitRepositoryBranch— checkout only, for a repository that has already been cloned/fetched. It neither clones nor fetches; it just checks outbranchand fast-forwards it to the already-fetchedorigin/<branch>. Backed byGitBranchSource.getCheckoutGitRepositoryBranchProvider— same ascheckoutGitRepositoryBranch, but returns theProvider<String>(the branch's tip SHA) instead of running the checkout eagerly. Call.get()on the returned provider to perform the checkout. Backed byGitBranchSource.
cloneAndCheckoutGitRepositoryBranch skips the git fetch (reusing whatever is
already cloned) when either:
- Gradle is run offline (
--offline), or - a
local.sonos.propertiesfile sets the property named by theskipRemoteFetchPropertyargument totrue.
The property name is caller-supplied: pass the key to look up (e.g.
"com.sonos.skip-fetch-latest"), or null to rely on offline mode
alone. The clone itself still happens if targetDirectory doesn't exist yet.
tests/validate.sh drives the real script through Gradle
against throwaway local file:// repositories in a temp directory — no network,
no credentials — and asserts the behavior described above: cloning, both
fetch-skipping mechanisms, the self-healing reclones (and the preserved
ahead/behind clones), all three entry points, the failure modes, and the
configuration-cache store/reuse/invalidate cycle. Requires git and gradle
on the PATH:
tests/validate.sh// build.gradle.kts (or settings.gradle.kts)
apply(from = "git-helpers.gradle.kts")
@Suppress("UNCHECKED_CAST")
val cloneAndCheckoutGitRepositoryBranch =
extra["cloneAndCheckoutGitRepositoryBranch"]
as (String, String, Directory, String?, LogLevel) -> Unit
val repo = layout.buildDirectory.dir("gradle").get()
cloneAndCheckoutGitRepositoryBranch(
"Sonos-Inc/gradle", // repo: owner/name (SSH) or a file:// URI
"main", // branch to check out and fast-forward
repo, // local clone directory (Directory)
"com.sonos.skip-fetch-latest", // skipRemoteFetchProperty (or null for offline-only)
LogLevel.INFO, // log level for progress output
)
// The repo is now cloned/updated and the branch checked out; apply a script from it:
apply(from = repo.file("common.gradle.kts"))apply(from = "git-helpers.gradle.kts")
@Suppress("UNCHECKED_CAST")
val checkoutGitRepositoryBranch =
extra["checkoutGitRepositoryBranch"]
as (String, Directory, LogLevel) -> Unit
val repo = layout.buildDirectory.dir("gradle").get()
checkoutGitRepositoryBranch(
"release/1.x", // branch to check out and fast-forward
repo, // local directory of the already-cloned repo (Directory)
LogLevel.INFO, // log level for progress output
)
apply(from = repo.file("common.gradle.kts"))| Parameter | Type | Description |
|---|---|---|
repo |
String |
Repository to clone: a GitHub owner/name (SSH) or file:// URI. |
branch |
String |
Git branch to check out and fast-forward. |
targetDirectory |
Directory |
Local directory the repo is cloned into (clone runs in its parent, using its name). |
skipRemoteFetchProperty |
String? |
Property name in local.sonos.properties that skips the fetch when true; null to rely on --offline only. |
logLevel |
LogLevel |
Level at which progress is logged. |
Both take the same parameters; getCheckoutGitRepositoryBranchProvider returns a
Provider<String> instead of Unit.
| Parameter | Type | Description |
|---|---|---|
branch |
String |
Git branch to check out and fast-forward. |
targetDirectory |
Directory |
Local directory containing the already-cloned repository. |
logLevel |
LogLevel |
Level at which progress is logged. |