Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions docs/PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,50 @@ gpg --list-keys --keyid-format SHORT
# Upload your public key to a key server
gpg --keyserver keyserver.ubuntu.com --send-keys ABCD1234

# Export your secret key ring (for Gradle signing)
gpg --export-secret-keys ABCD1234 > ~/.gnupg/secring.gpg
# Export your secret key ring (legacy approach for GPG < 2.1)
# WARNING: This creates a plaintext secret key file!
gpg --export-secret-keys -o ~/.gnupg/secring.gpg ABCD1234
chmod 600 ~/.gnupg/secring.gpg # Set strict permissions
```

**Note:** The above method uses the legacy keyring format. Modern GPG (2.1+) doesn't use `secring.gpg` by default. See the recommended approach below.

### 3. Configure Credentials

Add credentials to your `~/.gradle/gradle.properties` (NOT the project's `gradle.properties`):
#### Recommended: Modern In-Memory Signing (GPG 2.1+)

For modern GPG versions, use the in-memory signing approach with the `signingKey` property:
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent property name reference. Line 47 mentions signingKey property, but line 64 uses signing.key (with dot notation). The correct Gradle property name is signing.key, so this reference should be updated to match the actual property name used in the configuration examples.

Suggested change
For modern GPG versions, use the in-memory signing approach with the `signingKey` property:
For modern GPG versions, use the in-memory signing approach with the `signing.key` property:

Copilot uses AI. Check for mistakes.

```bash
# Export your private key in ASCII-armored format
gpg --armor --export-secret-keys ABCD1234
```

Add to your `~/.gradle/gradle.properties` (NOT the project's `gradle.properties`):

```properties
# Sonatype OSSRH credentials
sonatypeUsername=your-sonatype-username
sonatypePassword=your-sonatype-password

# GPG signing configuration
# GPG signing configuration (modern approach)
signing.keyId=ABCD1234
signing.password=your-gpg-passphrase
signing.key=-----BEGIN PGP PRIVATE KEY BLOCK-----\n...(paste output from gpg --armor --export-secret-keys)...\n-----END PGP PRIVATE KEY BLOCK-----
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete example with placeholder text. The example shows "...(paste output from gpg --armor --export-secret-keys)..." which may be confusing. It would be more helpful to clarify that this placeholder should be replaced with the actual multi-line ASCII-armored output, or provide a clearer indication of what the actual content looks like (e.g., showing a few more lines of the typical structure).

Suggested change
signing.key=-----BEGIN PGP PRIVATE KEY BLOCK-----\n...(paste output from gpg --armor --export-secret-keys)...\n-----END PGP PRIVATE KEY BLOCK-----
signing.key=-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: GnuPG v2\n\nmQINBF1EXAMPLEBEADAwEAAQ...\n...(multi-line base64 data from `gpg --armor --export-secret-keys ABCD1234` output, with each newline replaced by `\n`)...\n=ABCD\n-----END PGP PRIVATE KEY BLOCK-----

Copilot uses AI. Check for mistakes.
```

**Note:** The `signing.key` value should be the entire ASCII-armored private key block from the `gpg --armor --export-secret-keys ABCD1234` command output. When storing as a single-line property, replace actual newlines with the literal string `\n`.
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning about creating a plaintext secret key file is helpful, but the same concern applies to the modern approach when storing in gradle.properties. Consider adding a note that when using the modern approach with signing.key in ~/.gradle/gradle.properties, users should also set strict permissions on that file (chmod 600) since it contains the private key in plaintext as well.

Suggested change
**Note:** The `signing.key` value should be the entire ASCII-armored private key block from the `gpg --armor --export-secret-keys ABCD1234` command output. When storing as a single-line property, replace actual newlines with the literal string `\n`.
**Note:** The `signing.key` value should be the entire ASCII-armored private key block from the `gpg --armor --export-secret-keys ABCD1234` command output. When storing as a single-line property, replace actual newlines with the literal string `\n`. The `~/.gradle/gradle.properties` file now contains your private key in plaintext, so ensure it has strict permissions (for example, run `chmod 600 ~/.gradle/gradle.properties`).

Copilot uses AI. Check for mistakes.

#### Alternative: Legacy Keyring File (GPG < 2.1)

If you're using an older GPG version or prefer the legacy approach:

```properties
# Sonatype OSSRH credentials
sonatypeUsername=your-sonatype-username
sonatypePassword=your-sonatype-password

# GPG signing configuration (legacy approach)
signing.keyId=ABCD1234
signing.password=your-gpg-passphrase
signing.secretKeyRingFile=/path/to/.gnupg/secring.gpg
Expand All @@ -54,17 +84,26 @@ signing.secretKeyRingFile=/path/to/.gnupg/secring.gpg
**Important Security Notes:**
- Never commit credentials to version control
- Use environment variables in CI/CD instead of files
- The modern in-memory approach (`signing.key`) is preferred as it doesn't require a plaintext key file on disk
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially misleading security claim. The statement that the modern in-memory approach "doesn't require a plaintext key file on disk" is not entirely accurate. When the signing.key property is stored in ~/.gradle/gradle.properties, it is still a plaintext key file on disk - just in a different location. The main security advantage is that the key can be provided via environment variables in CI/CD without persisting to disk, which should be clarified.

Suggested change
- The modern in-memory approach (`signing.key`) is preferred as it doesn't require a plaintext key file on disk
- The modern in-memory approach (`signing.key`) is preferred because it supports providing the key via environment variables (so it does not need to be written to disk in CI/CD) and avoids the legacy keyring file; if you store `signing.key` in `~/.gradle/gradle.properties`, it is still a plaintext key in a file on disk

Copilot uses AI. Check for mistakes.
- If using `secretKeyRingFile`, ensure the file has strict permissions (0600)
- Consider using GPG agent for passphrase management

### Alternative: Environment Variables
### 4. Environment Variables for CI/CD

For CI/CD environments, use environment variables:
For CI/CD environments, use environment variables. The modern in-memory approach is recommended:

```bash
# Recommended: Modern in-memory signing
export ORG_GRADLE_PROJECT_sonatypeUsername=your-username
export ORG_GRADLE_PROJECT_sonatypePassword=your-password
export ORG_GRADLE_PROJECT_signingKeyId=ABCD1234
export ORG_GRADLE_PROJECT_signingPassword=your-passphrase
# In CI/CD, reference the key from a secret (preferred)
export ORG_GRADLE_PROJECT_signingKey="$GPG_PRIVATE_KEY_SECRET"
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent secret variable naming. Line 102 uses $GPG_PRIVATE_KEY_SECRET as an example, but line 218 documents the GitHub Actions secret as GPG_SIGNING_KEY. For consistency and to avoid confusion, the example on line 102 should reference the same variable name used in the GitHub Actions section, or clearly indicate that users should replace this with their actual secret variable name.

Suggested change
export ORG_GRADLE_PROJECT_signingKey="$GPG_PRIVATE_KEY_SECRET"
export ORG_GRADLE_PROJECT_signingKey="$GPG_SIGNING_KEY"

Copilot uses AI. Check for mistakes.
# Or export directly from GPG (for local testing)
# export ORG_GRADLE_PROJECT_signingKey="$(gpg --armor --export-secret-keys ABCD1234)"

# Alternative: Legacy keyring file
export ORG_GRADLE_PROJECT_signingSecretKeyRingFile=/path/to/secring.gpg
```

Expand Down Expand Up @@ -175,11 +214,11 @@ For automated releases, add secrets to your GitHub repository:
2. Add these repository secrets:
- `SONATYPE_USERNAME`
- `SONATYPE_PASSWORD`
- `GPG_KEY_ID` (the key ID, e.g., ABCD1234)
- `GPG_SIGNING_KEY` (base64-encoded secret key)
- `GPG_KEY_ID` (the 8-character short key ID, e.g., ABCD1234)
- `GPG_SIGNING_KEY` (ASCII-armored private key from `gpg --armor --export-secret-keys`)
- `GPG_SIGNING_PASSWORD`

Example workflow step:
Example workflow step using modern in-memory signing:
```yaml
- name: Publish to Maven Central
env:
Expand Down