|
| 1 | +# Release Setup Guide for Timer Ninja |
| 2 | + |
| 3 | +This document explains how to set up release process for publishing Timer Ninja to Maven Central (Sonatype) using JReleaser. |
| 4 | + |
| 5 | +## Recovery from Previous Mistake |
| 6 | + |
| 7 | +During a previous session, following critical files were accidentally deleted: |
| 8 | +- `public_key.asc` - GPG public key |
| 9 | +- `secret_key.asc` - GPG secret key (containing passphrase) |
| 10 | +- Sonatype credentials from build.gradle |
| 11 | + |
| 12 | +This guide documents recovery process and provides instructions for future reference. |
| 13 | + |
| 14 | +## What Has Been Done |
| 15 | + |
| 16 | +### 1. Generated New GPG Key Pair |
| 17 | +A new GPG key pair has been generated with following details: |
| 18 | +- **Key ID**: `799A99750C819FB915ECDBBC144D9369E0328F75` |
| 19 | +- **Type**: RSA 4096-bit |
| 20 | +- **Owner**: Thang Le Quoc <thanglequoc.it@gmail.com> |
| 21 | +- **Expiration**: None (permanent) |
| 22 | +- **Protection**: No passphrase (for automated CI/CD) |
| 23 | + |
| 24 | +**Note**: The key was generated without a passphrase to support automated releases in CI/CD pipelines. This is a common practice but requires careful handling of secret key file. |
| 25 | + |
| 26 | +### 2. Exported Keys |
| 27 | +- `public_key.asc` - Exported and stored in project root (safe to commit) |
| 28 | +- `secret_key.asc` - Exported and stored in project root (DO NOT commit) |
| 29 | + |
| 30 | +### 3. Updated .gitignore |
| 31 | +Added rules to prevent accidental commits of sensitive files: |
| 32 | +``` |
| 33 | +### Security - Keys & Credentials ### |
| 34 | +secret_key.asc |
| 35 | +*.asc |
| 36 | +!public_key.asc |
| 37 | +``` |
| 38 | + |
| 39 | +### 4. Created JReleaser Configuration |
| 40 | +Created `jreleaser.yml` with proper configuration for: |
| 41 | +- Maven Central deployment |
| 42 | +- Sonatype integration |
| 43 | +- GPG signing |
| 44 | +- Environment variable-based credentials |
| 45 | + |
| 46 | +### 5. Updated build.gradle |
| 47 | +Added JReleaser DSL configuration to enable signing and deployment features. No hardcoded credentials. |
| 48 | + |
| 49 | +## Required Setup Steps |
| 50 | + |
| 51 | +### 1. Publish Your GPG Public Key to a Key Server |
| 52 | + |
| 53 | +Before you can publish to Maven Central, your public key must be available on a public key server: |
| 54 | + |
| 55 | +```bash |
| 56 | +# Upload to multiple key servers |
| 57 | +gpg --keyserver keyserver.ubuntu.com --send-keys 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 58 | +gpg --keyserver pgp.mit.edu --send-keys 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 59 | +gpg --keyserver keys.openpgp.org --send-keys 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 60 | +``` |
| 61 | + |
| 62 | +**Important**: The key must be propagated to at least one key server before Maven Central will accept signed artifacts. |
| 63 | + |
| 64 | +### 2. Set Up Sonatype User Token |
| 65 | + |
| 66 | +You need to obtain Sonatype User Token for publishing to Maven Central: |
| 67 | + |
| 68 | +1. Go to https://central.sonatype.com/usertoken |
| 69 | +2. Sign in with your Sonatype account (or create one) |
| 70 | +3. Click "Generate User Token" |
| 71 | +4. Save the generated credentials: |
| 72 | + - **Token ID** (username): This is just an identifier |
| 73 | + - **Token** (password): This is the actual BEARER token that will be used for authentication |
| 74 | + |
| 75 | +**Note**: Sonatype User Token uses BEARER token authentication. Both username and password are required in configuration, but for BEARER auth, JReleaser only uses the password field as the actual token. |
| 76 | + |
| 77 | +### 3. Configure Environment Variables |
| 78 | + |
| 79 | +#### For Local Development: |
| 80 | +Set the following environment variables before running JReleaser: |
| 81 | + |
| 82 | +```bash |
| 83 | +export SONATYPE_USERNAME="your-sonatype-user-token-username" |
| 84 | +export SONATYPE_PASSWORD="your-sonatype-user-token-password" |
| 85 | +export GPG_PASSPHRASE="" # Empty since key has no passphrase |
| 86 | +``` |
| 87 | + |
| 88 | +You can add these to your shell profile (~/.zshrc or ~/.bashrc): |
| 89 | +```bash |
| 90 | +# Add to ~/.zshrc or ~/.bashrc |
| 91 | +export SONATYPE_USERNAME="your-username" |
| 92 | +export SONATYPE_PASSWORD="your-password" |
| 93 | +export GPG_PASSPHRASE="" |
| 94 | +``` |
| 95 | + |
| 96 | +#### For GitHub Actions: |
| 97 | +Add these as secrets and variables in your repository settings: |
| 98 | + |
| 99 | +**Variables** (not sensitive - visible to repository members): |
| 100 | +- `GPG_PUBLIC_KEY` - The full content of `public_key.asc` file (NOT base64-encoded) |
| 101 | + |
| 102 | +**Secrets** (sensitive - hidden): |
| 103 | +- `GPG_SECRET_KEY` - The full content of `secret_key.asc` file (NOT base64-encoded) |
| 104 | +- `SONATYPE_USERNAME` - Your Sonatype User Token username (Token ID) |
| 105 | +- `SONATYPE_PASSWORD` - Your Sonatype User Token password (the actual token) |
| 106 | + |
| 107 | +### 4. Verify GPG Key Configuration |
| 108 | + |
| 109 | +Ensure that GPG keys are properly configured: |
| 110 | + |
| 111 | +```bash |
| 112 | +# List public keys |
| 113 | +gpg --list-keys |
| 114 | + |
| 115 | +# List secret keys |
| 116 | +gpg --list-secret-keys |
| 117 | + |
| 118 | +# Verify key is exported |
| 119 | +cat public_key.asc |
| 120 | +cat secret_key.asc |
| 121 | +``` |
| 122 | + |
| 123 | +## Release Process |
| 124 | + |
| 125 | +### Option 1: Local Release |
| 126 | + |
| 127 | +1. Set environment variables: |
| 128 | +```bash |
| 129 | +export SONATYPE_USERNAME="your-username" |
| 130 | +export SONATYPE_PASSWORD="your-password" |
| 131 | +export GPG_PASSPHRASE="" |
| 132 | +``` |
| 133 | + |
| 134 | +2. Build and publish: |
| 135 | +```bash |
| 136 | +./gradlew clean build |
| 137 | +./gradlew publishToMavenLocal |
| 138 | +``` |
| 139 | + |
| 140 | +3. Deploy to Maven Central: |
| 141 | +```bash |
| 142 | +./gradlew jreleaserFullRelease |
| 143 | +``` |
| 144 | + |
| 145 | +### Option 2: GitHub Actions (Recommended) |
| 146 | + |
| 147 | +1. Ensure GitHub Actions are configured in `.github/workflows/` |
| 148 | + |
| 149 | +2. Add variables and secrets to repository (Settings → Secrets and variables → Actions): |
| 150 | + |
| 151 | +**Variables**: |
| 152 | +- `GPG_PUBLIC_KEY` - Copy the entire content of `public_key.asc` file (including -----BEGIN/END PGP PUBLIC KEY BLOCK-----) |
| 153 | + |
| 154 | +**Secrets**: |
| 155 | +- `GPG_SECRET_KEY` - Copy the entire content of `secret_key.asc` file (including -----BEGIN/END PGP PRIVATE KEY BLOCK-----) |
| 156 | +- `SONATYPE_USERNAME` - Your Sonatype User Token username (Token ID) |
| 157 | +- `SONATYPE_PASSWORD` - Your Sonatype User Token password (the actual token) |
| 158 | + |
| 159 | +3. Trigger the workflow manually: |
| 160 | + - Go to **Actions** → **Release - Publish to Sonatype Maven Central** |
| 161 | + - Click **Run workflow** |
| 162 | + - Select branch (usually `master`) |
| 163 | + - Click **Run workflow** |
| 164 | + |
| 165 | +## Publishing Your Public Key |
| 166 | + |
| 167 | +If you haven't already published your public key to a key server, do this now: |
| 168 | + |
| 169 | +```bash |
| 170 | +# Upload to Ubuntu key server (most commonly used) |
| 171 | +gpg --keyserver keyserver.ubuntu.com --send-keys 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 172 | + |
| 173 | +# Verify it's available |
| 174 | +gpg --keyserver keyserver.ubuntu.com --recv-keys 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 175 | +``` |
| 176 | + |
| 177 | +Wait a few minutes for key to propagate across servers before attempting to publish. |
| 178 | + |
| 179 | +## Troubleshooting |
| 180 | + |
| 181 | +### Issue: "Key not found on key server" |
| 182 | +**Solution**: Upload your public key to a key server and wait for propagation (can take 5-30 minutes) |
| 183 | + |
| 184 | +### Issue: "GPG signing failed" |
| 185 | +**Solution**: Ensure `secret_key.asc` exists in project root and is accessible |
| 186 | + |
| 187 | +### Issue: "Sonatype authentication failed (401 Unauthorized)" |
| 188 | +**Solution**: |
| 189 | +- Verify you're using User Token credentials, not your regular Sonatype login |
| 190 | +- Ensure BOTH `SONATYPE_USERNAME` and `SONATYPE_PASSWORD` are set in GitHub secrets |
| 191 | +- Check that `jreleaser.yml` has `authorization: BEARER` configured |
| 192 | +- Verify the token hasn't expired by regenerating it at https://central.sonatype.com/usertoken |
| 193 | +- Ensure environment variables in workflow use correct JRELEASER-specific names: `JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_USERNAME` and `JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_PASSWORD` |
| 194 | + |
| 195 | +### Issue: "Artifacts rejected by Maven Central" |
| 196 | +**Solution**: |
| 197 | +- Verify your GPG public key is on a key server |
| 198 | +- Check that pom.xml has all required metadata |
| 199 | +- Ensure all required files (javadoc jar, sources jar) are included |
| 200 | + |
| 201 | +### Issue: "Version mismatch" |
| 202 | +**Solution**: |
| 203 | +- Ensure that version in `build.gradle` is one you want to release |
| 204 | +- Commit and push version change before running workflow |
| 205 | +- The workflow uses version directly from `build.gradle` |
| 206 | + |
| 207 | +### Issue: "Signing is not enabled. Skipping" |
| 208 | +**Solution**: This is expected when running locally without all environment variables set. In GitHub Actions with proper secrets, signing will be enabled. |
| 209 | + |
| 210 | +### Issue: "Deploying is not enabled. Skipping" |
| 211 | +**Solution**: This is expected when running locally without `SONATYPE_PASSWORD` set. In GitHub Actions with proper secrets, deployment will be enabled. |
| 212 | + |
| 213 | +## Security Best Practices |
| 214 | + |
| 215 | +1. **Never commit `secret_key.asc`** - It's already in .gitignore |
| 216 | +2. **Never share Sonatype credentials** - Use environment variables or secrets |
| 217 | +3. **Back up your secret key securely** - Store in a password manager or encrypted storage |
| 218 | +4. **Keep revocation certificate** - Located at `~/.gnupg/openpgp-revocs.d/799A99750C819FB915ECDBBC144D9369E0328F75.rev` |
| 219 | +5. **Consider using a passphrase** - For production, add a passphrase and store it securely |
| 220 | + |
| 221 | +## Key Revocation Certificate |
| 222 | + |
| 223 | +The revocation certificate is stored at: |
| 224 | +``` |
| 225 | +~/.gnupg/openpgp-revocs.d/799A99750C819FB915ECDBBC144D9369E0328F75.rev |
| 226 | +``` |
| 227 | + |
| 228 | +**IMPORTANT**: Back up this certificate in a secure location. If your private key is ever compromised, you'll need it to revoke the public key. |
| 229 | + |
| 230 | +## JReleaser Configuration Details |
| 231 | + |
| 232 | +### Authentication Method |
| 233 | +The current configuration uses **BEARER authentication** (Sonatype User Token), not BASIC authentication. This is configured in `jreleaser.yml` with `authorization: BEARER`. |
| 234 | + |
| 235 | +### Sonatype User Token Authentication |
| 236 | +Sonatype Central uses User Token authentication for publishing: |
| 237 | +- **Token ID** (username): Identifier for the token |
| 238 | +- **Token** (password): The actual BEARER token used for authentication |
| 239 | + |
| 240 | +For BEARER authentication with JReleaser: |
| 241 | +- Set `authorization: BEARER` in `jreleaser.yml` |
| 242 | +- Set `username: ${SONATYPE_USERNAME}` and `password: ${SONATYPE_PASSWORD}` in YAML |
| 243 | +- JReleaser ignores username for BEARER auth and only uses password as token |
| 244 | + |
| 245 | +### JReleaser Environment Variables |
| 246 | +GitHub Actions workflow sets the following JReleaser-specific environment variables: |
| 247 | +- `JRELEASER_GITHUB_TOKEN`: GitHub token for creating releases |
| 248 | +- `JRELEASER_GITHUB_USERNAME`: GitHub username |
| 249 | +- `JRELEASER_GPG_PUBLIC_KEY`: GPG public key content |
| 250 | +- `JRELEASER_GPG_SECRET_KEY`: GPG secret key content |
| 251 | +- `JRELEASER_GPG_PASSPHRASE`: GPG passphrase (empty) |
| 252 | +- `JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_USERNAME`: Sonatype User Token username (from `SONATYPE_USERNAME` secret) |
| 253 | +- `JRELEASER_DEPLOY_MAVEN_MAVENCENTRAL_SONATYPE_PASSWORD`: Sonatype User Token password (from `SONATYPE_PASSWORD` secret) |
| 254 | + |
| 255 | +**Important**: JReleaser requires specific environment variable names with prefix `JRELEASER_`. The YAML file uses short names like `${SONATYPE_USERNAME}`, and JReleaser automatically looks for the corresponding full environment variable name. |
| 256 | + |
| 257 | +### Signing Mode |
| 258 | +Using MEMORY mode (default) which treats `JRELEASER_GPG_SECRET_KEY` as the actual GPG secret key content (not base64-encoded or file path). |
| 259 | + |
| 260 | +## Next Steps |
| 261 | + |
| 262 | +1. Upload your GPG public key to key servers |
| 263 | +2. Generate and configure Sonatype User Token credentials |
| 264 | +3. Set environment variables for your development environment |
| 265 | +4. Configure GitHub Actions secrets and variables (if using CI/CD) |
| 266 | +5. Test the release process |
| 267 | +6. Create your first official release! |
| 268 | + |
| 269 | +## GitHub Actions Release (Recommended) |
| 270 | + |
| 271 | +For detailed setup and troubleshooting, see: |
| 272 | +- **[`.github/workflows/README.md`](.github/workflows/README.md)** - Workflow documentation |
| 273 | +- **[`development/jreleaser_fix.md`](jreleaser_fix.md)** - Recent fixes and configuration details |
| 274 | + |
| 275 | +### Quick Setup for GitHub Actions |
| 276 | + |
| 277 | +1. **Add variables and secrets** (Settings → Secrets and variables → Actions): |
| 278 | + |
| 279 | + **Variables**: |
| 280 | + - `GPG_PUBLIC_KEY`: Copy the entire content of `public_key.asc` file (including -----BEGIN/END PGP PUBLIC KEY BLOCK-----) |
| 281 | + |
| 282 | + **Secrets**: |
| 283 | + - `GPG_SECRET_KEY`: Copy the entire content of `secret_key.asc` file (including -----BEGIN/END PGP PRIVATE KEY BLOCK-----) |
| 284 | + - `SONATYPE_USERNAME`: Your Sonatype User Token username (Token ID) |
| 285 | + - `SONATYPE_PASSWORD`: Your Sonatype User Token password (the actual token) |
| 286 | + |
| 287 | +2. **To release**: |
| 288 | + - Go to **Actions** → **Release - Publish to Sonatype Maven Central** |
| 289 | + - Click **Run workflow** |
| 290 | + - Select branch (usually `master`) |
| 291 | + - Click **Run workflow** |
| 292 | + - The workflow will use the version from `build.gradle` and deploy to Maven Central |
| 293 | + |
| 294 | +3. **Verify workflow permissions**: Go to Settings → Actions → General and ensure: |
| 295 | + - ✅ Read and write permissions |
| 296 | + - ✅ Allow GitHub Actions to create and approve pull requests |
| 297 | + |
| 298 | +## Additional Resources |
| 299 | + |
| 300 | +- [JReleaser Documentation](https://jreleaser.org/) |
| 301 | +- [JReleaser Maven Central Reference](https://jreleaser.org/guide/latest/reference/deploy/maven/maven-central) |
| 302 | +- [Sonatype Central Publishing Guide](https://central.sonatype.org/publish/) |
| 303 | +- [Maven Central Portal](https://central.sonatype.com/) |
| 304 | +- [GPG Documentation](https://gnupg.org/documentation/) |
| 305 | +- [GitHub Actions Documentation](https://docs.github.com/en/actions) |
| 306 | + |
| 307 | +## Summary of Files |
| 308 | + |
| 309 | +| File | Status | Purpose | |
| 310 | +|------|--------|---------| |
| 311 | +| `public_key.asc` | ✅ Safe to commit | GPG public key for signature verification | |
| 312 | +| `secret_key.asc` | ❌ Do NOT commit | GPG private key for signing artifacts | |
| 313 | +| `jreleaser.yml` | ✅ Safe to commit | JReleaser configuration | |
| 314 | +| `build.gradle` | ✅ Updated | Build configuration with JReleaser DSL | |
| 315 | +| `.gitignore` | ✅ Updated | Prevents accidental commits of secrets | |
| 316 | +| `.github/workflows/release.yml` | ✅ Safe to commit | GitHub Actions workflow for releases | |
| 317 | + |
| 318 | +## Workflow Flow |
| 319 | + |
| 320 | +The GitHub Actions release workflow follows these steps: |
| 321 | + |
| 322 | +1. **Run tests** - Ensure code quality |
| 323 | +2. **Publish Maven artifacts** - Creates JARs and publishes to `build/staging-deploy` |
| 324 | +3. **Validate JReleaser configuration** - Checks configuration before attempting release |
| 325 | +4. **Full release with JReleaser**: |
| 326 | + - Sign artifacts with GPG |
| 327 | + - Deploy to Sonatype Maven Central |
| 328 | + - Create GitHub release |
| 329 | + |
| 330 | +--- |
| 331 | + |
| 332 | +**Last Updated**: 2026-02-10 |
| 333 | +**Key ID**: 799A99750C819FB915ECDBBC144D9369E0328F75 |
| 334 | +**Authentication**: BEARER (Sonatype User Token) |
0 commit comments