You need internal libraries published to a private repository but releases also mirrored to a public one. How do you model multiple publish repositories and control which build targets which?
answer
- several named maven { } targets
- one task set per repository
- avoid umbrella publish; call …To<Name>Repository
- pipeline decides which task
- convention plugin for org consistency
basics
~10 sDeclare multiple named maven { } repositories in publishing.repositories. Each gets its own publishAllPublicationsTo<Name>Repository task, so CI invokes the one(s) appropriate for that build instead of the catch-all publish.
solid answer
~40 sBecause `publishing.repositories` is a `RepositoryHandler`, you can declare several named targets, and Gradle generates an independent task set per repository. That gives you per-destination control: ```kotlin publishing { repositories { maven { name = "internal"; url = uri("https://nexus.corp/internal") } maven { name = "public"; url = uri("https://repo.example.com/releases") } } } ``` Now CI runs `publishAllPublicationsToInternalRepository` for every build and adds `publishAllPublicationsToPublicRepository` only on tagged releases — rather than the umbrella `publish` task, which would push to *all* declared repositories at once. The decision lives in the pipeline (which task it calls), keeping the build script declarative. For consistency across many projects, factor the repository declarations into a shared convention plugin so every module exposes the same named targets and the same CI command works everywhere.
code
kotlin · 10 linespublishing {
repositories {
maven { name = "internal"; url = uri("https://nexus.corp/internal") }
if (providers.gradleProperty("publishPublic").isPresent) {
maven { name = "public"; url = uri("https://repo.example.com/releases") }
}
}
}
// CI internal: ./gradlew publishAllPublicationsToInternalRepository
// CI release: ./gradlew publishAllPublicationsToPublicRepository -PpublishPublicgo deeper
Know you can declare more than one repository, each with a name.
Map each named repository to its own generated task set and invoke the specific one.
Design the build-declares / pipeline-selects split, avoid the umbrella publish, and gate optional repos with providers.
Own the convention plugin and org-wide naming standard so a uniform CI publish command works across the whole estate and public exposure is governed centrally.
## Multiple named repositories `publishing.repositories` accepts any number of `maven { }` entries. Each declared repository, by virtue of its `name`, gets its own generated tasks: - `publishAllPublicationsToInternalRepository` - `publishAllPublicationsToPublicRepository` - and per-publication variants for each. The global `publish` task depends on **all** of them — which is exactly what you usually do *not* want when destinations differ by build type. ## Controlling which build targets which The cleanest separation is to keep the build script declarative (declare all repositories) and let the **pipeline choose the task**: - Every CI build: `./gradlew publishAllPublicationsToInternalRepository` - Release builds only (e.g. on a git tag): also `./gradlew publishAllPublicationsToPublicRepository` This avoids embedding branch/tag logic in the build script. If you must encode it in Gradle, gate it on a property: ```kotlin repositories { maven { name = "internal"; url = uri("https://nexus.corp/internal") } if (providers.gradleProperty("publishPublic").isPresent) { maven { name = "public"; url = uri("https://repo.example.com/releases") } } } ``` Using `providers.gradleProperty(...)` keeps it configuration-cache friendly versus reading `project.hasProperty` eagerly. ## Consistency at scale With dozens of modules, repeating the repository declarations invites drift (typos in URLs, inconsistent names → CI commands that work in one repo but not another). Extract them into a **convention plugin** (`build-logic`/`buildSrc`) that applies `maven-publish` and registers the canonical named repositories. Then a single org-wide CI command — keyed on the stable names — works across every project. ## Pitfalls - Running `publish` (umbrella) accidentally pushes to *every* repository including public — scope CI to explicit `…To<Name>Repository` tasks. - Two repositories with the same effective name collide; ensure names are unique. - Naming differences across projects break a uniform pipeline; standardise via the convention plugin.
- Why avoid the umbrella `publish` task in this setup?`publish` depends on every declared repository's upload task, so it would push to the public repo on every build. Scope CI to the explicit `…To<Name>Repository` task instead.
- How do you keep repository declarations consistent across many modules?Factor them into a convention plugin in buildSrc/build-logic that applies maven-publish and registers the canonical named repositories, so all modules share identical names and URLs and one CI command works everywhere.
- How do you conditionally add a repository in a configuration-cache-friendly way?Gate it on `providers.gradleProperty("...")` rather than eager `project.hasProperty`, so the read participates in the provider/lazy model the configuration cache expects.
saying these in an interview costs you the question
- Using the umbrella `publish` task and accidentally pushing internal snapshots to the public repo.
- Hardcoding branch/tag logic inside the build script instead of selecting the task in the pipeline.
- Duplicating repository declarations per module and letting names/URLs drift.