skip to content

How would you route a publication to a snapshots URL versus a releases URL based on whether the version ends with -SNAPSHOT?

level: middleimportance: must knowfreq 55%

answer

  1. version.endsWith('-SNAPSHOT')
  2. two URLs: snapshots vs releases
  3. snapshots mutable, releases immutable
  4. conditional repository url
  5. single publish command self-routes

basics

~10 s

Check whether version ends with -SNAPSHOT; if it does, point the repository url at the snapshots repo, otherwise at the releases repo. Typically a conditional expression on the repository's url.

solid answer

~40 s

Maven repositories conventionally separate two URLs: a **snapshots** repo for mutable in-progress versions (`1.0.0-SNAPSHOT`) and a **releases** repo for immutable final versions (`1.0.0`). In Gradle you express that with a single conditional on the repository `url`: ```kotlin val isSnapshot = version.toString().endsWith("-SNAPSHOT") url = uri(if (isSnapshot) snapshotsUrl else releasesUrl) ``` The `-SNAPSHOT` suffix is the contract: snapshot repos accept overwrites and serve the newest timestamped build, while release repos reject re-publishing the same coordinates. Routing on the suffix means a single `./gradlew publish` does the right thing for both CI nightly builds and tagged releases without separate scripts. Take care: `version` may be `Object`/lazy, so coerce to string, and evaluate the condition at configuration time only after the version is finalized.

code

kotlin · 10 lines
kotlin
publishing {
  repositories {
    maven {
      name = "corporate"
      val snap = uri("https://nexus.example.com/repository/maven-snapshots/")
      val rel  = uri("https://nexus.example.com/repository/maven-releases/")
      url = if (version.toString().endsWith("-SNAPSHOT")) snap else rel
    }
  }
}

go deeper

for a junior

Recognize -SNAPSHOT means in-progress and there are two repo URLs.

for a middle

Write the conditional url assignment and explain mutable-vs-immutable semantics of the two repos.

for a senior

Discuss configuration-time evaluation ordering, version finalization, and why one command can self-route in CI.

for a principal

Define org publishing policy: immutable releases, snapshot retention, and how routing fits the release pipeline gates.

## The two-URL convention Maven-style repositories distinguish: - **Snapshot versions** — end in `-SNAPSHOT` (e.g. `2.3.0-SNAPSHOT`). They are *mutable*: every publish overwrites/augments, and consumers always get the latest build (often timestamped under the hood). Used for continuous integration and in-progress work. - **Release versions** — no suffix (e.g. `2.3.0`). They are *immutable*: a release repo refuses to overwrite an existing version, guaranteeing reproducibility. Servers expose these as two different URLs (e.g. `.../content/repositories/snapshots` vs `.../releases`). ## Routing in Gradle The whole mechanism hinges on one boolean: does `version` end with `-SNAPSHOT`? You set the repository `url` conditionally: ```kotlin publishing { publications { create<MavenPublication>("maven") { from(components["java"]) } } repositories { maven { val releasesUrl = uri("https://repo.example.com/releases") val snapshotsUrl = uri("https://repo.example.com/snapshots") url = if (version.toString().endsWith("-SNAPSHOT")) snapshotsUrl else releasesUrl credentials { /* from gradle.properties / env */ } } } } ``` ## Why suffix-based routing It makes `./gradlew publish` self-routing: a nightly job with `version=1.4.0-SNAPSHOT` lands in snapshots; a tagged build with `version=1.4.0` lands in releases — same command, no branching scripts. The `-SNAPSHOT` suffix is a hard convention understood by Gradle's resolution (snapshot dependencies are re-checked subject to `cacheChangingModulesFor`) and by repository managers. ## Gotchas - `version` is typed loosely; always `version.toString()` before `endsWith`. - The condition is evaluated at configuration time, so ensure the version is set (not lazily mutated later by a release plugin after this block runs). - mavenLocal itself needs no routing — `publishToMavenLocal` writes both snapshots and releases into the same `~/.m2/repository`; the suffix only matters for **remote** routing. - Snapshot versions must not contain `-SNAPSHOT` partway through to be treated as a release; the suffix check is exact at the end.

  • Why coerce version to a String before calling endsWith?
    `project.version` is typed as Object and may be a non-String (e.g. a version-plugin type); `version.toString()` guarantees the `endsWith` call works correctly.
  • Does this routing affect publishToMavenLocal?
    No — pTML writes both snapshots and releases into the same ~/.m2/repository. The snapshot/release URL split only matters for remote repositories.

saying these in an interview costs you the question

  • Comparing the raw version object without toString, risking an identity/format mismatch.
  • Believing snapshot/release URL routing also changes where mavenLocal installs.

context