skip to content

What practices keep publishing credentials out of source control and logs, and what are common mistakes teams make here?

level: middleimportance: should knowfreq 45%

answer

  1. never in script or committed gradle.properties
  2. ~/.gradle/gradle.properties (per-user)
  3. CI: ORG_GRADLE_PROJECT_* from secret store
  4. no tokens in URLs, no logging
  5. scope + rotate tokens

basics

~10 s

Never hardcode credentials in build scripts. Put them in ~/.gradle/gradle.properties (per-user, never committed) or inject via ORG_GRADLE_PROJECT_* env vars in CI. Use the credentials(PasswordCredentials::class) convention so values stay external.

solid answer

~40 s

The cardinal rule: secrets never live in the build script or a committed `gradle.properties`. Use the `credentials(PasswordCredentials::class)` convention and supply `<repoName>Username`/`<repoName>Password` from `~/.gradle/gradle.properties` locally (per-user, outside the project) or from `ORG_GRADLE_PROJECT_*` environment variables in CI sourced from a secret store. Common mistakes: committing the project `gradle.properties` with real values, echoing secrets via `println` or `--info`/`--debug` logs, embedding tokens in repository URLs, and storing them in `local.properties` thinking it's safe (it can still be committed). Prefer lazy `providers.gradleProperty(...)` so the value isn't read until needed and isn't captured by the configuration cache. Rotate and scope tokens, and keep `~/.gradle/gradle.properties` and any secret files in `.gitignore`.

code

toml · 6 lines
toml
# ~/.gradle/gradle.properties  (per-user, NEVER committed)
myRepoUsername=ci-deployer
myRepoPassword=glpat-xxxxxxxxxxxxxxxx

# Project .gitignore should include any secret-bearing files;
# the project gradle.properties must hold NO credentials.

go deeper

for a junior

Know not to hardcode secrets and that ~/.gradle/gradle.properties is the per-user place for them.

for a middle

List the safe sources (per-user file, CI env vars) and the common leak vectors (committed files, URLs, logs).

for a senior

Add lazy-resolution/config-cache reasoning and least-privilege, rotatable tokens; recognize init.gradle distribution risks.

for a principal

Define org secret-management policy: central secret store, scoped deploy tokens, rotation, and audit; ensure conventions make the safe path the default.

## The one rule **Secrets must not be in version control or in the build script.** Everything else follows from making that true while still letting Gradle resolve them. ## Safe storage locations - **`~/.gradle/gradle.properties`** — per-user, outside the project tree, never committed. Best place for a developer's personal publishing creds. - **CI secret store → `ORG_GRADLE_PROJECT_*` env vars** — the secret exists only in the job process. No file on disk. - **Command-line `-P`** — possible but risky (visible in process listings / shell history); avoid for real secrets. The project's own `gradle.properties` is fine for **non-secret** config but must never hold credentials, because it's typically committed. ## How the convention helps `credentials(PasswordCredentials::class)` already forces values out of the script — you reference a *property*, not a literal. That's the main reason to use the type-based form rather than inline literals. ## Avoid leaking via logs - Don't `println(password)` or log credential values. - Be careful with `--info`/`--debug`; avoid printing resolved credentials. - Don't embed tokens in repository URLs (`https://user:token@host/...`) — they show up in logs and error output. ## Laziness reduces exposure Use `providers.gradleProperty("...")` (lazy) rather than eager `System.getenv()` reads, so: - the secret is only read when a publish task runs, and - it isn't serialized into the configuration cache. ## Common mistakes (interview gold) 1. Committing project `gradle.properties` (or `local.properties`) with real credentials. 2. Hardcoding literals in the `build.gradle.kts`. 3. Putting tokens in the repo URL. 4. Logging/echoing secrets in custom tasks or CI scripts. 5. Long-lived, over-scoped tokens that are never rotated. 6. Checking secrets into a shared `init.gradle` without realizing it's distributed. ## Operational hygiene - `.gitignore` any secret-bearing file. - Scope tokens to the minimum (publish-only, single repo) and rotate them. - Use deploy/service tokens rather than personal credentials in CI.

  • Is it safe to keep credentials in the project's own `gradle.properties`?
    No — it's normally committed, so it would leak the secret. Use `~/.gradle/gradle.properties` or CI env vars instead.
  • Why avoid embedding the token directly in the repository URL?
    URLs appear in logs, error messages, and process listings, so the token leaks easily; keep it in credentials sourced from a property.
  • How does using `providers.gradleProperty(...)` reduce secret exposure compared to `System.getenv()`?
    It's lazy — read only when publishing runs and not captured by the configuration cache — so the secret is touched less and isn't serialized.

saying these in an interview costs you the question

  • Treating the committed project `gradle.properties` or `local.properties` as a safe place for secrets.
  • Suggesting `https://user:token@host` URLs.
  • Printing/logging credential values for debugging.
  • Using a personal long-lived token in CI instead of a scoped, rotatable deploy token.

context