skip to content

Conditional and Required Signing

Requiring signatures only for release publishes so local and snapshot builds need no key. Asked because unconditional signing blocks every developer who has no keyring.

on this pageshow

questions

5

What does the `isRequired` property in Gradle's signing block control, and why would you set it conditionally instead of leaving it at its default?

level: juniorimportance: must knowfreq 55%

answer

  1. isRequired = fail-when-cannot-sign
  2. default true
  3. false => warn + skip, unsigned artifacts
  4. predicate: hasTask('publish') && !SNAPSHOT
  5. lets local/dev builds pass without a key

basics

~20 s

isRequired decides whether a missing or failing signature aborts the build. By default it's true. You set it conditionally (e.g. only for releases) so local or SNAPSHOT builds don't fail when no signing key is configured.

solid answer

~40 s

The signing plugin's `isRequired` (Groovy: `required`) flag controls what happens when a signature can't be produced — typically because no key is configured. When `true` (the default), the `sign*` tasks fail the build if signing materials are missing or signing throws. When `false`, the tasks log a warning and skip, leaving artifacts unsigned. You set it conditionally so that everyday developer builds and SNAPSHOT publishes (which usually have no GPG key on the machine) succeed, while release publishes to Maven Central — which *must* be signed — still fail loudly if the key is absent. The canonical predicate is `isRequired = gradle.taskGraph.hasTask('publish') && !version.endsWith('-SNAPSHOT')`, evaluated lazily after the task graph is ready.

code

kotlin · 5 lines
kotlin
signing {
    isRequired = gradle.taskGraph.hasTask("publish") &&
        !version.toString().endsWith("-SNAPSHOT")
    sign(publishing.publications["maven"])
}

go deeper

for a junior

Know that true means the build fails if it can't sign, and false lets it pass unsigned; conditional so local builds without a key don't break.

for a middle

Explain the canonical predicate and that SNAPSHOTs/dev builds skip while releases require signatures.

for a senior

Discuss lazy evaluation against the task graph and that materials-present still signs even when not required.

for a principal

Frame it as a release-safety gate: unsigned releases must fail fast; tie to org-wide publishing policy and CI key provisioning.

## What `isRequired` does The `signing` plugin adds `Sign` tasks (e.g. `signMavenPublication`) that produce detached `.asc` signatures for your artifacts. Each `Sign` task — and the extension as a whole — exposes a boolean `isRequired` (Kotlin DSL) / `required` (Groovy DSL). - `isRequired = true` (the default): if signing **cannot** happen — no key configured, bad passphrase, signatory throws — the build **fails**. - `isRequired = false`: the same failure is downgraded to a logged warning, the `Sign` task is effectively skipped, and the build continues with **unsigned** artifacts. Note a subtlety: `isRequired` governs the *failure-when-impossible* behavior, not whether signing is attempted. If signing materials *are* present, signing still runs even when `isRequired = false`. ## Why make it conditional Two realities collide: 1. **Local/CI dev builds and SNAPSHOTs** usually run on machines with no GPG key. Requiring signatures there breaks `./gradlew build` for everyone. 2. **Release publishing to Maven Central** *requires* a valid PGP signature — an unsigned release silently slipping through is a release-blocking defect you want to catch at build time, not at upload. So you flip `isRequired` based on intent: require it only when the build is actually a release publish. ## The canonical predicate ```kotlin signing { isRequired = gradle.taskGraph.hasTask("publish") && !version.toString().endsWith("-SNAPSHOT") } ``` Two conditions: - `gradle.taskGraph.hasTask("publish")` — true only when a `publish` task is in the execution graph, so it doesn't fire for `build`/`assemble`. - `!version.endsWith("-SNAPSHOT")` — true only for release versions, so SNAPSHOT publishes stay unsigned. ## Lazy evaluation matters `gradle.taskGraph` is only populated **after** the configuration phase, during `whenReady`. Because `signing { isRequired = ... }` is evaluated lazily by the plugin when the `Sign` task's `required` provider is queried at execution time, querying `hasTask` works. If you instead read it too eagerly you can get a 'task graph not ready' error — see the follow-ups.

  • If `isRequired` is false but a valid key IS configured, are artifacts signed?
    Yes. `isRequired` only controls the failure behavior when signing is impossible. If materials are present, the Sign task still runs and produces signatures.
  • Why can reading `gradle.taskGraph.hasTask(...)` throw if done in the wrong place?
    The task graph is only ready after configuration. If the predicate is evaluated during configuration rather than lazily at execution, the graph isn't populated yet and Gradle complains. The signing plugin resolves `required` lazily, so the standard idiom works.

saying these in an interview costs you the question

  • Saying isRequired controls *whether signing runs at all* — it controls failure-when-impossible, not attempt.
  • Claiming the default is false (it's true).

context

open as a page

Walk through how `gradle.taskGraph.hasTask('publish')` works in a conditional signing predicate and why the task graph readiness matters.

level: middleimportance: must knowfreq 45%

basics

~20 s

gradle.taskGraph is the resolved DAG of tasks Gradle will run. hasTask('publish') returns true only when a publish task is scheduled. The graph is built after configuration, so the predicate must be read lazily at execution time, not during configuration.

open as a page

Why does the conditional signing predicate include `!version.endsWith('-SNAPSHOT')`, and what would go wrong if you omitted it?

level: middleimportance: should knowfreq 38%

basics

~20 s

SNAPSHOTs are mutable pre-release builds that repositories don't require to be signed. The -SNAPSHOT check skips signing for them so frequent dev publishes don't need a key, while only immutable release versions are forced to sign.

open as a page

How would you design a conditional-signing setup for a library published from CI, balancing local developer builds, snapshot CI, and signed release CI?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Make isRequired true only for release publishes (hasTask('publish') && !SNAPSHOT). Provide the signing key/passphrase only to the release CI job via secrets; leave local and snapshot builds keyless so they pass with unsigned (or skipped) artifacts.

open as a page

What's the difference between making signing conditional via `isRequired` versus disabling the Sign task with `onlyIf { ... }` or `enabled = false`? When would you use each?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

isRequired controls whether a missing signature fails the build but still signs when a key exists. onlyIf/enabled=false skips the Sign task entirely — no signature produced even with a key. Use isRequired to relax failure; use onlyIf to truly skip signing.

open as a page