diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dbceac0..3ab6b22 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: - name: Build with Gradle run: ./gradlew build deploy: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' + # if: github.event_name == 'push' && github.ref == 'refs/heads/master' name: Deploy SNAPSHOT runs-on: ubuntu-latest steps: @@ -46,20 +46,5 @@ jobs: java-version: 17 - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Jar - run: ./gradlew shadowJar - - name: Generate tag version - uses: anothrNick/github-tag-action@v1 - id: tag_version_dry_run - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: true - - name: publish - env: - SNAPSHOT_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }}-SNAPSHOT - ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY_ASCII_ARMOR }} - ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_KEY_PASSPHRASE }} - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - run: ./gradlew publish -Pversion=$SNAPSHOT_VERSION -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} -Psign=true + - name: Build and publish SNAPSHOT to Maven Local + run: ./gradlew clean build publishToMavenLocal -x test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0935d72..460d6cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,56 +5,30 @@ on: jobs: release: - name: Release to Maven Central, Tag & Release + name: Release with JReleaser runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Generate tag version - uses: anothrNick/github-tag-action@v1 - id: tag_version_dry_run - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: true - - name: Set up JDKs + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 17 uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 + - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Jar - env: - RELEASE_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }} - run: ./gradlew shadowJar -Pversion=$RELEASE_VERSION - - name: Publish to Maven Central - env: - ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY_ASCII_ARMOR }} - ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_KEY_PASSPHRASE }} - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - RELEASE_VERSION: ${{ steps.tag_version_dry_run.outputs.tag }} - run: ./gradlew -Pversion=$RELEASE_VERSION publish -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} -Psign=true - - name: Close & Release Staging Repository + + - name: Build artifacts + run: ./gradlew clean build -x test + + - name: Publish to Maven Central with JReleaser env: - MAVEN_CENTRAL_TOKEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} - MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} - run: ./gradlew closeAndReleaseRepository -PossrhUsername=${MAVEN_CENTRAL_TOKEN_USERNAME} -PossrhPassword=${MAVEN_CENTRAL_TOKEN_PASSWORD} - - name: Push new tag - uses: anothrNick/github-tag-action@v1 - id: tag_version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WITH_V: false - DRY_RUN: false - - name: Build Changelog - id: github_release - uses: mikepenz/release-changelog-builder-action@v3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Github Release - uses: softprops/action-gh-release@v1 - with: - body: ${{steps.github_release.outputs.changelog}} - tag_name: ${{ steps.tag_version.outputs.tag }} - files: java-snapshot-testing-*/build/libs/*.jar + JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_TOKEN_USERNAME }} + JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} + JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY_ASCII_ARMOR }} + JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_KEY_PASSPHRASE }} + JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./gradlew jreleaserFullRelease diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f28f546..12cae05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,53 +34,43 @@ Ensure you add `mavenLocal()` to your consuming project and the dependency veris folder # Uploading to maven central -see `.github/workflows/release.yml` -# Uploading to maven central (manually) +The project now uses [JReleaser](https://jreleaser.org/) to automate releases to Maven Central. -Gradle release plugin is not currently working so this is a manual process at the moment. +See [JRELEASER_SETUP.md](JRELEASER_SETUP.md) for complete setup and release instructions. -# Setup GPG on your machine +### Quick Release Steps -1. copy the GPG Private key into a file `private.key` -1. run +1. Ensure you have Maven Central Portal API credentials: + - `JRELEASER_MAVENCENTRAL_USERNAME` + - `JRELEASER_MAVENCENTRAL_PASSWORD` -``` -gpg --import private.key -gpg -k -``` +2. Setup GPG signing (see JRELEASER_SETUP.md) -## Preparing +3. Update version in `gradle.properties` (remove `-SNAPSHOT`) -1. Create a tag `X.X.X` -1. Update `gradle.properties` and remove `-SNAPSHOT` from the version number -1. Check this file into version control and push the branch to the remote -1. run +4. Run full release: + ```bash + ./gradlew jreleaserFullRelease + ``` -``` -export SONAR_USERNAME=? -export SONAR_PASSWORD=? -export ORG_GRADLE_PROJECT_signingKey="$(cat private.key)" -export ORG_GRADLE_PROJECT_signingPassword=? - -# I found shadowed classes are not included if you don't separate the gradle operations -./gradlew clean shadowJar -./gradlew publish -PossrhUsername=${SONAR_USERNAME} -PossrhPassword=${SONAR_PASSWORD} -Psign=true +Or use step-by-step approach: +```bash +./gradlew clean build publishToMavenLocal -Psign +./gradlew jreleaserPublish ``` -## Releasing [Full Tutorial](https://central.sonatype.org/pages/ossrh-guide.html) +JReleaser will automatically: +- Sign all artifacts with GPG +- Publish to Maven Central via Portal API +- Close and release the staging repository +- Create a GitHub release -1. Login to SONAR (https://oss.sonatype.org) -1. Click 'Staging Repositories' and locate the 'iogithubcodedabble-dev' bundle -1. Review artifacts are correct in the 'Content' tab -1. Press the 'Close' and give a reason such as "Jack Matthews - Confirmed artifacts are OK" -1. Wait for about 1 min and press the 'Refresh button', if all sanity checks have passed the 'Release' button will be - visible -1. Press the 'Release' button and give a reason for releasing -1. Objects should be available in about 10 min (Longer for search.maven.org) +### Previous Manual Process (No Longer Needed) -## Cleanup +The old process involved: +1. Manual staging repository management via Sonatype UI +2. Direct OSSRH publishing +3. Manual close/release steps -1. Checkout master branch -1. Increment version number in `gradle.properties` -1. Create pull request for merge +All of this is now automated by JReleaser. diff --git a/JRELEASER_MIGRATION.md b/JRELEASER_MIGRATION.md new file mode 100644 index 0000000..a8893a0 --- /dev/null +++ b/JRELEASER_MIGRATION.md @@ -0,0 +1,127 @@ +# JReleaser Migration Summary + +This document summarizes the conversion from manual Maven Central publishing via OSSRH to automated publishing using JReleaser. + +## Changes Made + +### 1. **build.gradle** +- Added JReleaser Gradle plugin: `id 'org.jreleaser' version '1.13.1'` +- Applied jreleaser plugin to root project + +### 2. **gradle/publishing.gradle** +- **Removed**: Direct Sonatype OSSRH repository configuration +- **Removed**: nexus-staging plugin configuration and usage +- **Added**: Local repository configuration (`build/repos/release`) +- **Updated**: Signing configuration to detect JReleaser environment variables + - `JRELEASER_GPG_SECRET_KEY` + - `JRELEASER_GPG_SECRET_KEY_FILE` + - `JRELEASER_GPG_PASSPHRASE` + +### 3. **New File: jreleaser.yml** +Complete JReleaser configuration including: +- Project metadata (name, description, URL, license, authors) +- GPG signing configuration with verification +- Maven Central Portal Publisher API configuration +- Automatic repository closing and release + +### 4. **New File: JRELEASER_SETUP.md** +Comprehensive guide covering: +- Prerequisites and credential setup +- Step-by-step release process +- Environment variable reference +- Troubleshooting tips +- JReleaser task reference + +### 5. **CONTRIBUTING.md** +- Replaced old manual release instructions +- Added link to JRELEASER_SETUP.md +- Documented new quick release steps + +## Key Improvements + +| Aspect | Before | After | +|--------|--------|-------| +| **Repository Management** | Manual via Sonatype UI | Automated by JReleaser | +| **Staging** | Manual close/release steps | Auto close/release | +| **Publishing** | Direct OSSRH API | Maven Central Portal API | +| **GPG Signing** | Manual gradle parameters | Environment variables | +| **Release Workflow** | Multi-step manual process | Single `jreleaserFullRelease` command | +| **GitHub Releases** | Manual creation | Automated by JReleaser | + +## Migration Path + +### Old Workflow +```bash +# Manual staging +./gradlew publish -PossrhUsername=... -PossrhPassword=... -Psign=true + +# Manual login to Sonatype UI +# - Locate staging repo +# - Close repo +# - Release repo +# - Wait for sync +``` + +### New Workflow +```bash +# Automated full release +./gradlew jreleaserFullRelease +``` + +## Backward Compatibility + +- Old gradle properties can still be used if needed +- The `net.researchgate.release` plugin is retained for version management +- Manual publishing with `-PossrhUsername/-PossrhPassword` still works but not recommended + +## Environment Setup for Releases + +To perform releases, configure these environment variables: + +```bash +export JRELEASER_MAVENCENTRAL_USERNAME="your-sonatype-token-username" +export JRELEASER_MAVENCENTRAL_PASSWORD="your-sonatype-token-password" +export JRELEASER_GPG_SECRET_KEY="base64-encoded-gpg-key" +export JRELEASER_GPG_PASSPHRASE="your-gpg-passphrase" +export JRELEASER_GITHUB_TOKEN="github-token-if-creating-releases" # optional +``` + +Or use GPG key file: +```bash +export JRELEASER_GPG_SECRET_KEY_FILE="/path/to/gpg/key.gpg" +``` + +## Testing Before Production Release + +1. **Dry Run**: + ```bash + ./gradlew jreleaserFullReleaseDryRun + ``` + +2. **Validate Config**: + ```bash + ./gradlew jreleaserValidate + ``` + +3. **View Config**: + ```bash + ./gradlew jreleaserConfig + ``` + +## Resources + +- [JReleaser Official Documentation](https://jreleaser.org/) +- [JReleaser Gradle Plugin Guide](https://jreleaser.org/guide/latest/reference/gradle.html) +- [Maven Central Portal API](https://central.sonatype.org/publishing/publish-maven/) +- [JRELEASER_SETUP.md](./JRELEASER_SETUP.md) - Detailed setup guide + +## Next Steps + +1. Store Maven Central Portal API credentials securely (e.g., GitHub Secrets) +2. Optionally update CI/CD workflows to use jreleaser +3. Test with a dry-run release: `./gradlew jreleaserFullReleaseDryRun` +4. Perform first production release when ready + +## Questions? + +See [JRELEASER_SETUP.md](./JRELEASER_SETUP.md) for detailed troubleshooting and additional information. diff --git a/JRELEASER_SETUP.md b/JRELEASER_SETUP.md new file mode 100644 index 0000000..953b522 --- /dev/null +++ b/JRELEASER_SETUP.md @@ -0,0 +1,163 @@ +# JReleaser Maven Central Setup + +This project has been converted to use [JReleaser](https://jreleaser.org/) for automated releases to Maven Central via the Portal Publisher API. + +## Overview + +JReleaser simplifies the release process by: +- Automating version bumping and git tagging +- Building and signing artifacts +- Publishing to Maven Central using the new Portal API +- Creating GitHub releases with changelog + +## Prerequisites + +Before performing a release, ensure you have: + +1. **Maven Central Portal API Credentials** + - Set environment variable: `JRELEASER_MAVENCENTRAL_USERNAME` (your Sonatype token username) + - Set environment variable: `JRELEASER_MAVENCENTRAL_PASSWORD` (your Sonatype token password) + - Get these from: https://central.sonatype.com/account + +2. **GPG Signing Setup** + - Export your GPG secret key in base64: + ```bash + gpg --export-secret-keys | base64 + ``` + - Set environment variable: `JRELEASER_GPG_SECRET_KEY` with the base64-encoded key + - Set environment variable: `JRELEASER_GPG_PASSPHRASE` with your GPG passphrase + + OR use a key file: + ```bash + export JRELEASER_GPG_SECRET_KEY_FILE=~/.gnupg/secring.gpg + export JRELEASER_GPG_PASSPHRASE= + ``` + +3. **GitHub Token** (optional, for creating releases) + - Set environment variable: `JRELEASER_GITHUB_TOKEN` with a GitHub personal access token + +## Release Process + +### 1. Prepare for Release +Update version in `gradle.properties`: +```properties +version=X.Y.Z +``` + +### 2. Build Artifacts +```bash +./gradlew clean build publishToMavenLocal -x test +``` + +### 3. Full Release Workflow +```bash +# Dry run (recommended first) +./gradlew jreleaserConfig +./gradlew jreleaserFullReleaseDryRun + +# Perform actual release +./gradlew jreleaserFullRelease +``` + +### 4. Manual Step-by-Step Release +If you prefer to control each step: + +```bash +# 1. Build and sign artifacts +./gradlew clean build publishToMavenLocal -Psign -PsigningKey="$JRELEASER_GPG_SECRET_KEY" -PsigningPassword="$JRELEASER_GPG_PASSPHRASE" + +# 2. Copy artifacts to staging directory +./gradlew :java-snapshot-testing-core:publishToMavenLocal +./gradlew :java-snapshot-testing-junit4:publishToMavenLocal +./gradlew :java-snapshot-testing-junit5:publishToMavenLocal +./gradlew :java-snapshot-testing-plugin-jackson:publishToMavenLocal +./gradlew :java-snapshot-testing-plugin-jackson3:publishToMavenLocal +./gradlew :java-snapshot-testing-spock:publishToMavenLocal + +# 3. Upload to Maven Central Portal +./gradlew jreleaserPublish +``` + +## JReleaser Configuration + +The main configuration is in `jreleaser.yml`: + +- **project**: Project metadata (name, description, URL, etc.) +- **signing**: GPG signing configuration +- **deploy.maven.mavenCentral**: Maven Central Portal API settings + - `url`: Portal API endpoint + - `closeRepository`: Auto-close staging repository + - `releaseRepository`: Auto-release after publishing + +## Environment Variables Reference + +| Variable | Description | +|----------|-------------| +| `JRELEASER_MAVENCENTRAL_USERNAME` | Maven Central Portal API username | +| `JRELEASER_MAVENCENTRAL_PASSWORD` | Maven Central Portal API password | +| `JRELEASER_GPG_SECRET_KEY` | Base64-encoded GPG secret key | +| `JRELEASER_GPG_SECRET_KEY_FILE` | Path to GPG secret key file | +| `JRELEASER_GPG_PASSPHRASE` | GPG key passphrase | +| `JRELEASER_GITHUB_TOKEN` | GitHub token for creating releases | +| `JRELEASER_DRY_RUN` | Set to `true` to perform dry run | + +## Useful JReleaser Tasks + +```bash +# Show current configuration +./gradlew jreleaserConfig + +# Validate configuration +./gradlew jreleaserValidate + +# Full release (dry run) +./gradlew jreleaserFullReleaseDryRun + +# Full release (execute) +./gradlew jreleaserFullRelease + +# Just publish to Maven Central +./gradlew jreleaserPublish + +# Create GitHub release +./gradlew jreleaserRelease + +# Generate changelogs +./gradlew jreleaserChangelog +``` + +## Troubleshooting + +### Publication Fails +- Ensure version in `gradle.properties` is not a SNAPSHOT +- Verify Maven Central credentials are correct +- Check that artifacts are properly signed + +### GPG Signing Issues +- Verify GPG secret key is correctly base64 encoded +- Ensure passphrase is correct +- Test GPG locally: `gpg --list-secret-keys` + +### Maven Central Portal Issues +- Login to https://central.sonatype.com and verify credentials +- Check portal API documentation: https://central.sonatype.com/publishing/publish-maven/ + +## Migration from Previous Setup + +The previous setup used: +- `net.researchgate.release` for version management +- Direct OSSRH/Sonatype publishing +- Manual staging repository management with `nexus-staging` + +The new JReleaser setup: +- Still supports version management (can integrate with `net.researchgate.release`) +- Automates the entire publish-to-Maven-Central flow +- Uses the modern Portal Publisher API +- Simplifies artifact signing and deployment + +## Additional Resources + +- [JReleaser Documentation](https://jreleaser.org/) +- [Maven Central Portal Publisher API](https://central.sonatype.org/publishing/publish-maven/) +- [JReleaser Gradle Plugin](https://jreleaser.org/guide/latest/reference/gradle.html) +- [Maven Central Requirements](https://central.sonatype.org/publish/requirements/) diff --git a/build.gradle b/build.gradle index 33579c4..b7b1a01 100644 --- a/build.gradle +++ b/build.gradle @@ -8,8 +8,11 @@ plugins { id 'com.gradleup.shadow' version '9.3.2' apply false id 'io.codearte.nexus-staging' version '0.22.0' id 'com.diffplug.spotless' version '8.4.0' apply false + id 'org.jreleaser' version '1.13.1' apply false } +apply plugin: 'org.jreleaser' + ext { junit5Version = '5.10.2' lombokVersion = '1.18.20' diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 278e512..48d4a27 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -8,8 +8,6 @@ task sourcesJar(type: Jar) { from sourceSets.main.allSource } -apply plugin: 'net.researchgate.release' - publishing { publications.withType(MavenPublication).configureEach { artifact javadocJar @@ -46,49 +44,11 @@ publishing { } } } -} - -if (project.hasProperty("sign")) { - apply plugin: 'signing' - signing { - def signingKey = findProperty("signingKey") - def signingPassword = findProperty("signingPassword") - useInMemoryPgpKeys(signingKey, signingPassword) - } -} - -afterEvaluate { - if (project.hasProperty("sign")) { - signing { - sign publishing.publications + + repositories { + maven { + name = 'local' + url = layout.buildDirectory.dir('repos/release') } } } - -if (project.hasProperty("ossrhUsername") && project.hasProperty("ossrhPassword")) { - def snapshotRepositoryUrl = 'https://central.sonatype.com/repository/maven-snapshots/' - def releaseRepositoryUrl = 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/' - def stagingServerUrl = 'https://ossrh-staging-api.central.sonatype.com/service/local/' - - publishing { - repositories { - maven { - name = 'sonatype' - url = uri(project.version.toString().endsWith('-SNAPSHOT') - ? snapshotRepositoryUrl - : releaseRepositoryUrl) - credentials { - username = project.property("ossrhUsername") - password = project.property("ossrhPassword") - } - } - } - } - - nexusStaging { - serverUrl stagingServerUrl - username project.property("ossrhUsername") - password project.property("ossrhPassword") - packageGroup 'io.github.codedabble-dev' - } -} diff --git a/jreleaser.yml b/jreleaser.yml new file mode 100644 index 0000000..bf181f6 --- /dev/null +++ b/jreleaser.yml @@ -0,0 +1,41 @@ +jreleaser: + project: + name: java-snapshot-testing + description: Snapshot Testing for Java + longDescription: A Java library for snapshot testing with support for JUnit 4, JUnit 5, and Spock + website: https://github.com/codedabble-dev/java-snapshot-testing + documentationUrl: https://github.com/codedabble-dev/java-snapshot-testing/blob/main/README.md + repositoryUrl: https://github.com/codedabble-dev/java-snapshot-testing + bugTrackerUrl: https://github.com/codedabble-dev/java-snapshot-testing/issues + authors: + - Jack Matthews + license: MIT + inceptionYear: 2021 + tags: + - snapshot-testing + - testing + - java + java: + version: 1.8 + groupId: io.github.codedabble-dev + artifactId: java-snapshot-testing + + release: + github: + enabled: false + + signing: + active: RELEASE + armored: true + verify: true + + deploy: + maven: + mavenCentral: + active: RELEASE + auth: BASIC + url: https://central.sonatype.com/api/v1/publisher + closeRepository: true + releaseRepository: true + stagingRepositories: + - build/repos/release