You need to inject a publish version and a signing secret into a Gradle build from CI. How do you wire each, and why?
answer
- build inputs → project properties
- ORG_GRADLE_PROJECT_ env vars in CI
- secret off the command line
- never commit secrets to gradle.properties
- providers.gradleProperty lazy read
basics
~10 sInject both as ORG_GRADLE_PROJECT_ env vars: ORG_GRADLE_PROJECT_version and ORG_GRADLE_PROJECT_signingKey. They become project properties, keep the secret off the command line, and need no -P args.
solid answer
~40 sFor a CI publish, both the version and the secret are build inputs, so I expose them as **project properties via environment variables**: `ORG_GRADLE_PROJECT_version` and `ORG_GRADLE_PROJECT_signingKey`. CI runners provide config and secrets as env vars, so this is a zero-glue mapping, and — critically — it keeps the signing key out of the command line, which would otherwise appear in `ps` listings and echoed logs. The build reads them lazily with `providers.gradleProperty("version")` / `providers.gradleProperty("signingKey")`, which is configuration-cache safe and fails fast if missing. I would NOT use `-PsigningKey=...` (leaks the secret) nor a checked-in `gradle.properties` (secrets must never be committed). For JVM/tool-level needs — say a proxy — I'd use `-D` or `systemProp.` instead, but for build-domain inputs like version and signing material, project properties via env vars are the right channel.
code
kotlin · 7 linesval version = providers.gradleProperty("version")
val signingKey = providers.gradleProperty("signingKey")
signing {
useInMemoryPgpKeys(signingKey.get(), "")
sign(publishing.publications)
}go deeper
Name ORG_GRADLE_PROJECT_ env vars as the way to inject build inputs in CI.
Explain that these become project properties and keep secrets off the command line.
Reason about namespace (project vs system) and channel (CLI/env/properties), and justify env vars over -P and over committed gradle.properties for secrets.
Set org-wide policy: secrets only via CI secret store → ORG_GRADLE_PROJECT_ env vars; ban secret-bearing -P args and committed secrets; standardize lazy providers reads.
## Frame it by namespace and channel Two decisions: (1) which **namespace** does the value belong to — project property or system property? (2) which **channel** delivers it — command line, env var, or gradle.properties? A publish version and a signing secret are **build inputs your own scripts consume** → project-property namespace. The safest CI channel for that namespace is the environment-variable convention: ``` ORG_GRADLE_PROJECT_version=... ORG_GRADLE_PROJECT_signingKey=... ``` ## Why env vars beat command-line -P for secrets Command-line arguments are visible to any process via `ps`, and CI systems frequently echo the executed command into build logs. A `-PsigningKey=...` therefore risks leaking the key. Environment variables are not part of the visible command line and, when sourced from the CI secret store, are masked in logs. So: - **version** could go either way, but using the same env-var channel keeps the wiring uniform. - **signingKey** must use the env-var channel for hygiene. ## Why not checked-in gradle.properties A project `gradle.properties` is committed to VCS; putting a secret there leaks it permanently into history. It is fine for non-secret defaults only. ## Reading them in the build ```kotlin val version = providers.gradleProperty("version") val signingKey = providers.gradleProperty("signingKey") publishing { /* use version.get() */ } signing { useInMemoryPgpKeys(signingKey.get(), /* passphrase */ "") } ``` `providers.gradleProperty` returns a lazy `Provider<String>`; calling `.get()` on a missing value fails the build clearly, which is what you want in CI. ## When system properties (-D / systemProp.) are the right tool instead If the value is consumed by the JVM or a tool — proxy host, trust store, a library that reads `System.getProperty` — use `-D` (one-off) or `systemProp.` in `~/.gradle/gradle.properties` (persistent), not a project property. Matching the value to its namespace is the senior-level judgment this question probes. ## GitHub Actions example ```yaml - run: ./gradlew publish env: ORG_GRADLE_PROJECT_version: ${{ github.ref_name }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} ```
- Could you pass the version with -Pversion=... instead?Yes for the non-secret version it's fine, but the signing key must not go on the command line. Using the env-var channel for both keeps wiring uniform and safe.
- Why not store the signing key in the project's gradle.properties?That file is committed to version control, so the secret would leak into history permanently. gradle.properties is only for non-secret defaults.
- If the build needed a corporate proxy host instead, which mechanism would you use?A system property: -Dhttp.proxyHost or systemProp.http.proxyHost in ~/.gradle/gradle.properties — not a project property, because it's JVM/tool-level config.
saying these in an interview costs you the question
- Passing the signing secret as a -P command-line argument.
- Committing the secret to a checked-in gradle.properties.
- Treating JVM/tool config (proxy, TLS) as project properties or vice versa.