diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f73c5b1..4eef085 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,14 +48,26 @@ jobs: javadocJar sourcesJar \ --no-daemon --stacktrace - # Sign and publish (no need to build again!) + # Import GPG key for signing + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + + # Sign and publish to Maven Central (Central Portal) - name: Publish to Maven Central env: - ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} - ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }} - ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }} - ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} + ORG_GRADLE_PROJECT_centralPortalUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} + ORG_GRADLE_PROJECT_centralPortalPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} + ORG_GRADLE_PROJECT_signing.keyId: ${{ secrets.SIGNING_KEY_ID }} + ORG_GRADLE_PROJECT_signing.password: ${{ secrets.GPG_PASSPHRASE }} + ORG_GRADLE_PROJECT_signing.secretKeyRingFile: ${{ github.workspace }}/.gnupg/secring.gpg run: | + # Export secret key for Gradle signing + gpg --export-secret-keys > ${{ github.workspace }}/.gnupg/secring.gpg + + # Publish to Maven Central via Central Portal ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository \ --no-daemon --stacktrace @@ -86,8 +98,11 @@ jobs: ### Changes See [CHANGELOG.md](CHANGELOG.md) for details. + + --- + **Note:** This is a pre-1.0.0 release. The API may change between releases. draft: false - prerelease: false + prerelease: ${{ startsWith(steps.extract_version.outputs.VERSION, '0.') }} files: | build/libs/*.jar build/publications/maven/pom-default.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index bb96f0b..ea67e46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,35 +8,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Complete Open Payments API implementation with Java 25 -- GNAP (Grant Negotiation and Authorization Protocol) support -- HTTP Message Signatures with Ed25519 -- Token lifecycle management (rotation and revocation) -- HTTP interceptors for logging, authentication, and error handling -- Async-first API with CompletableFuture -- Immutable data models using Java records -- Comprehensive JavaDoc documentation -- 465 unit tests with 100% pass rate -- PMD and Checkstyle quality checks -- Automatic code formatting with Spotless -- Complete resource service implementations: - - WalletAddressService for wallet address discovery - - IncomingPaymentService for receiving payments - - OutgoingPaymentService for sending payments - - QuoteService for exchange rate quotes -- Comprehensive usage examples and documentation +- 📦 **Maven Central Publishing Setup** + - Automated CI/CD publishing via GitHub Actions + - Central Portal integration + - Comprehensive release guide (RELEASE_GUIDE.md) + - GPG artifact signing configuration + - Automated GitHub Release creation + - JavaDoc deployment to GitHub Pages + - Publication verification in CI/CD pipeline ### Changed -- Converted payment and auth domain models to Java records for improved immutability - - IncomingPayment, OutgoingPayment, Quote (payment models) - - AccessToken, Grant, AccessRight (auth models) - - Preserved builder patterns for backward compatibility - - Added Optional-returning getters for nullable fields - - Maintained custom equals/hashCode/toString implementations +- 🔢 **Versioning Strategy** + - Changed from `1.0.0-SNAPSHOT` to `0.1.0` (pre-1.0 development) + - Adopted semantic versioning with 0.x.y for initial development + - Configured for release-only versions (no SNAPSHOT support) + +- 🔧 **Publishing Configuration** + - Fixed Maven Central Portal URL to `https://central.sonatype.com` + - Updated to token-based authentication (Central Portal tokens) + - Removed legacy OSSRH references and configurations + - Removed duplicate version declarations in build files + - Simplified publishing workflow + +- 📝 **Documentation** + - Added publishing docs into single comprehensive RELEASE_GUIDE.md + - Updated CI_CD_SETUP.md to reflect Central Portal approach + - Updated GITHUB_ACTIONS_SETUP.md with correct secret names + +- ⚙️ **CI/CD Workflow** + - Updated release.yml for Central Portal authentication + - Added GPG key import step using crazy-max/ghaction-import-gpg + - Updated secret names: `CENTRAL_PORTAL_*` instead of `SONATYPE_*` + - Added automatic pre-release flag for 0.x versions + - Enhanced artifact verification steps ### Removed -- Phase-specific TODO comments from completed implementation -- Replaced with proper documentation for future enhancements +- 🗑️ **Cleanup** + - Removed all SNAPSHOT version references from codebase + - Removed OSSRH (legacy Sonatype) documentation and references + - Removed duplicate/redundant publishing documentation files + - Removed old sunset `s01.oss.sonatype.org` endpoint references ## [0.1.0] - Initial Development @@ -163,7 +174,6 @@ Not applicable for initial release. 1. Integration tests for Phases 5+ are still pending implementation 2. Performance benchmarks not yet established -3. Maven Central publication pending first stable release ## Future Plans @@ -173,17 +183,18 @@ Not applicable for initial release. - Additional authentication schemes - Enhanced error recovery -### Version 1.0.0 -- Production-ready release +### Version 1.0.0 (Stable Release) +- Production-ready release with API stability commitment - Full Open Payments API coverage -- Performance benchmarks -- Maven Central publication +- Performance benchmarks and optimization - Comprehensive integration testing - Production deployment guide +- First stable release on Maven Central ## Contributors - Boniface Kabaso - Initial implementation +- Espoir Diteekemena - Initial implementation and Documentation ## References diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index 196c857..7868434 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -356,7 +356,7 @@ All completed phases meet the following quality standards: - **HTTP**: Apache HttpClient 5 (abstracted, multiple implementations) - **JSON**: Jackson with Jdk8Module (for Optional support) and JSR310 (for Java Time) - **Crypto**: Ed25519 (Java standard library KeyPairGenerator) -- **Testing**: JUnit 5, Mockito, AssertJ +- **Testing**: JUnit 6, Mockito, AssertJ - **Quality**: PMD, Checkstyle, SpotBugs, Spotless --- diff --git a/README.md b/README.md index 3e61a43..73e46cc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Open Payments Java SDK -[![CI](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml/badge.svg)](https://github.com/bonifacekabaso/open-payments-java/actions/workflows/ci.yml) -[![Security & Quality](https://img.shields.io/github/actions/workflow/status/boniface/open-payments-java/ci.yml?label=Security%20%26%20Quality&query=jobs.security-and-quality.conclusion)](https://github.com/bonifacekabaso/open-payments-java/actions/workflows/ci.yml) +[![CI](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml/badge.svg)](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml) +[![Security & Quality](https://img.shields.io/github/actions/workflow/status/boniface/open-payments-java/ci.yml?label=Security%20%26%20Quality&query=jobs.security-and-quality.conclusion)](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml) [![JaCoCo](https://img.shields.io/badge/JaCoCo-Coverage-green.svg)](https://github.com/boniface/open-payments-java/actions/workflows/ci.yml) [![Java](https://img.shields.io/badge/Java-25-orange.svg)](https://openjdk.java.net/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) @@ -41,7 +41,7 @@ graph TD Auth ~~~ Note Resource ~~~ Note - Note["Operated by Account Service Entity (ASE)
(Bank, Wallet Provider, Payment Processor)"] + Note["Operated by Account Service Entity (ASE) (Bank, Wallet Provider, Payment Processor)"] end App -->|HTTP Request via SDK| SDK @@ -80,7 +80,7 @@ graph TD ```kotlin dependencies { - implementation("zm.hashcode:open-payments-java:1.0-SNAPSHOT") + implementation("zm.hashcode:open-payments-java:0.1.0") } ``` @@ -90,10 +90,12 @@ dependencies { zm.hashcode open-payments-java - 1.0-SNAPSHOT + 0.1.0 ``` +> **Note:** Versions 0.x.y are pre-1.0.0 releases. The API may change between releases until 1.0.0 is reached. + ### Basic Usage ```java @@ -213,10 +215,10 @@ cd open-payments-java - Complete unit test implementations - Performance optimization -- Maven Central publication +- Maven Central publication (see [MAVEN_CENTRAL_PUBLISHING.md](MAVEN_CENTRAL_PUBLISHING.md)) - Version 1.0 release -**Version**: 0.1.0-SNAPSHOT | **Target Release**: 1.0.0 | **Java**: 25+ +**Version**: 1.0.0-SNAPSHOT | **Target Release**: 1.0.0 | **Java**: 25+ See [PROJECT_STATUS.md](PROJECT_STATUS.md) for detailed roadmap. diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md new file mode 100644 index 0000000..bf1c242 --- /dev/null +++ b/RELEASE_GUIDE.md @@ -0,0 +1,586 @@ +# Maven Central Release Guide + +Complete guide for publishing `open-payments-java` to Maven Central using automated CI/CD and the Central Portal. + +--- + +## Table of Contents + +- [Overview](#overview) +- [Versioning Strategy](#versioning-strategy) +- [Prerequisites (One-Time Setup)](#prerequisites-one-time-setup) +- [Automated Release Process](#automated-release-process) +- [Manual Release Process (Fallback)](#manual-release-process-fallback) +- [Troubleshooting](#troubleshooting) +- [CI/CD Integration](#cicd-integration) +- [Quick Reference](#quick-reference) + +--- + +## Overview + +This project is configured to publish to Maven Central via the **Central Portal** API: + +- **Publishing Endpoint:** `https://central.sonatype.com` +- **Namespace:** `zm.hashcode` +- **Artifact:** `open-payments-java` +- **Group ID:** `zm.hashcode` +- **Authentication:** Token-based +- **Current Version:** `0.1.0` (pre-1.0 development) + +**Publication Methods:** +1. **Automated (Recommended):** Push a git tag → GitHub Actions publishes automatically +2. **Manual (Fallback):** Run Gradle commands locally + +--- + +## Versioning Strategy + +We follow [Semantic Versioning 2.0.0](https://semver.org/) with a **pre-1.0.0 development phase**. + +### Version Number Format: `MAJOR.MINOR.PATCH` + +#### Pre-1.0 Development (Current) +- **0.x.y** = Initial development (API may change) + - `0.1.0` = First release ← **YOU ARE HERE** + - `0.2.0` = New features added + - `0.3.0` = More features, bug fixes + - Continue until API is stable... + +#### Post-1.0 Stable +- **1.0.0** = First stable, production-ready release + - Signals API stability commitment + - Breaking changes require MAJOR version bump + +- **1.x.y+** = Production releases + - `1.1.0` = New backward-compatible features (MINOR) + - `1.0.1` = Backward-compatible bug fixes (PATCH) + - `2.0.0` = Breaking changes (MAJOR) + +### When to Bump Versions + +| Change Type | Current Phase (0.x) | Stable Phase (1.x+) | Example | +|-------------|---------------------|---------------------|---------| +| Breaking API changes | MINOR | MAJOR | 0.1.0 → 0.2.0 or 1.0.0 → 2.0.0 | +| New features | MINOR | MINOR | 0.2.0 → 0.3.0 or 1.0.0 → 1.1.0 | +| Bug fixes | PATCH | PATCH | 0.2.0 → 0.2.1 or 1.0.1 → 1.0.2 | +| API stabilization | MAJOR (→ 1.0.0) | N/A | 0.9.0 → 1.0.0 | + +--- + +## Prerequisites (One-Time Setup) + +### 1. Central Portal Account + +**Register at:** https://central.sonatype.com + +- Sign up with your GitHub account +- Verify your email address +- **Note:** The `zm.hashcode` namespace is already verified and ready to use ✓ + +### 2. Generate Publishing Token + +1. Go to https://central.sonatype.com/account +2. Click "**Generate User Token**" +3. Copy both the **username** and **password** + +### 3. GPG Signing Setup + +All artifacts published to Maven Central must be cryptographically signed with GPG. + +#### Generate GPG Key + +```bash +# Generate new GPG key (use default options) +gpg --gen-key + +# Follow prompts: +# - Real name: +# - Email: +# - Passphrase: (choose a strong password - save it!) +``` + +#### List Keys and Get KEY_ID + +```bash +# List your keys to get KEY_ID +gpg --list-keys + +# Example output: +# pub rsa3072 2025-01-15 [SC] [expires: 2027-01-15] +# ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 ← This is the KEY_ID +# uid [ultimate] FULL NAME + +# The KEY_ID is the full 40-character fingerprint +# The SIGNING_KEY_ID is the last 8 characters (e.g., QRST7890) +``` + +#### Export Public Key to Key Servers (REQUIRED) + +```bash +# Export to multiple key servers (for redundancy) +gpg --keyserver keys.openpgp.org --send-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 +gpg --keyserver keyserver.ubuntu.com --send-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 + +# Verify it was uploaded (wait a few minutes) +gpg --keyserver keys.openpgp.org --recv-keys ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 +``` + +### 4. Configure GitHub Secrets + +**For Automated Releases (Recommended)** + +Go to: `https://github.com/boniface/open-payments-java/settings/secrets/actions` + +Add these **5 required secrets**: + +| Secret Name | Value | How to Get | +|-------------|-------|------------| +| `GPG_PRIVATE_KEY` | ASCII armored GPG private key | `gpg --export-secret-keys --armor YOUR_KEY_ID` | +| `GPG_PASSPHRASE` | Your GPG key passphrase | The password you chose during `gpg --gen-key` | +| `SIGNING_KEY_ID` | Last 8 characters of GPG key ID | From `gpg --list-keys` (e.g., `QRST7890`) | +| `CENTRAL_PORTAL_USERNAME` | Central Portal token username | From step 2 above | +| `CENTRAL_PORTAL_PASSWORD` | Central Portal token password | From step 2 above | + +**Example: Getting GPG_PRIVATE_KEY** + +```bash +# Export private key in ASCII armor format +gpg --export-secret-keys --armor ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 + +# Copy the ENTIRE output (including BEGIN/END lines): +# -----BEGIN PGP PRIVATE KEY BLOCK----- +# ... +# -----END PGP PRIVATE KEY BLOCK----- +``` + +### 5. Configure Local Credentials (For Manual Releases) + +**Option A: User-level configuration** (recommended - keeps secrets out of repo) + +Create/edit `~/.gradle/gradle.properties`: + +```properties +# GPG Signing +signing.keyId=QRST7890 +signing.password=your-gpg-passphrase +signing.secretKeyRingFile=/[HOME DIRECTORY]/.gnupg/secring.gpg + +# Central Portal Authentication +centralPortalUsername=your-token-username +centralPortalPassword=your-token-password +``` + +**Export Secret Key for Gradle (GPG 2.1+)** + +```bash +# Export to legacy format for Gradle +gpg --export-secret-keys > ~/.gnupg/secring.gpg +``` + +--- + +## Automated Release Process + +### Overview + +Push a git tag → GitHub Actions automatically publishes to Maven Central. + + +### Step 1: Update Version + +Edit `gradle.properties`: + +```properties +# For first release (already set): +version=0.1.0 + +# For subsequent releases: +version=0.2.0 # New features +version=0.1.1 # Bug fix +version=1.0.0 # Stable release +``` + +### Step 2: Update CHANGELOG (Optional but Recommended) + +Document what changed in this release: + +```markdown +## [0.1.0] - 2025-01-22 + +### Added +- Initial implementation of Open Payments client +- Support for wallet addresses, quotes, and payments +- GNAP authorization flow +- HTTP signature authentication + +### Fixed +- None (first release) + +### Changed +- None (first release) +``` + +### Step 3: Run Tests Locally + +```bash +# Run all tests +./gradlew test + +# Build artifacts +./gradlew build + +# Verify coverage +./gradlew jacocoTestCoverageVerification + +# Check for PMD violations +./gradlew pmdMain +``` + +### Step 4: Commit and Tag + +```bash +# Commit version change +git add gradle.properties CHANGELOG.md +git commit -m "Release version 0.1.0" + +# Create annotated tag (version must match gradle.properties) +git tag -a v0.1.0 -m "Release version 0.1.0" + +# Push commits and tag to trigger release +git push origin feature/maven-publish +git push origin v0.1.0 +``` + +### Step 5: Monitor the Release + +1. **GitHub Actions:** Watch the workflow run + ``` + https://github.com/boniface/open-payments-java/actions + ``` + +2. **Central Portal:** Check upload status (after ~5-10 minutes) + ``` + https://central.sonatype.com/publishing + ``` + +3. **Maven Central:** Verify publication (after ~15-30 minutes) + ``` + https://central.sonatype.com/artifact/zm.hashcode/open-payments-java + https://search.maven.org/artifact/zm.hashcode/open-payments-java + ``` + +### Step 6: Verify Publication + +```bash +# Check if artifact is available (wait 15-30 minutes after release) +curl -I "https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/0.1.0/open-payments-java-0.1.0.pom" + +# Should return: HTTP/1.1 200 OK +``` + +### Step 7: Post-Release + +#### Update to Next Version + +Edit `gradle.properties`: + +```properties +# Bump to next version +version=0.2.0 +``` + +```bash +git add gradle.properties +git commit -m "Bump version to 0.2.0" +git push origin feature/maven-publish +``` + +#### Test Installation + +Create a test project to verify: + +```kotlin +// build.gradle.kts +dependencies { + implementation("zm.hashcode:open-payments-java:0.1.0") +} +``` + +```bash +./gradlew build --refresh-dependencies +``` + +--- + +## Manual Release Process (Fallback) + +If CI/CD fails or you need to publish manually: + +### Step 1: Prepare Release + +```bash +# Update version in gradle.properties +version=0.1.0 + +# Commit and tag +git add gradle.properties +git commit -m "Release version 0.1.0" +git tag -a v0.1.0 -m "Release version 0.1.0" +git push origin main +git push origin v0.1.0 +``` + +### Step 2: Build and Test + +```bash +# Clean build +./gradlew clean + +# Run all tests +./gradlew test + +# Build all artifacts (main JAR, sources JAR, javadoc JAR) +./gradlew build + +# Verify artifacts were created +ls -lh build/libs/ +# Should see: +# - open-payments-java-0.1.0.jar +# - open-payments-java-0.1.0-sources.jar +# - open-payments-java-0.1.0-javadoc.jar +``` + +### Step 3: Publish to Central Portal + +Ensure you have configured `~/.gradle/gradle.properties` with credentials (see Prerequisites). + +```bash +# Publish to Maven Central (signs and uploads) +./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository + +# Or in separate steps for more control: +./gradlew publishToSonatype # Upload to staging +./gradlew closeAndReleaseSonatypeStagingRepository # Release to Central +``` + +**What happens:** +1. Gradle builds all artifacts (JAR, sources, javadoc) +2. Signs each artifact with your GPG key +3. Uploads to Central Portal staging repository +4. Validates artifacts (POM, signatures, required files) +5. Releases to Maven Central (public within 15-30 minutes) + +### Step 4: Create GitHub Release (Manual) + +```bash +# Via GitHub CLI +gh release create v0.1.0 \ + --title "Release 0.1.0" \ + --notes "Initial release of Open Payments Java SDK" \ + build/libs/open-payments-java-0.1.0.jar \ + build/libs/open-payments-java-0.1.0-sources.jar \ + build/libs/open-payments-java-0.1.0-javadoc.jar + +# Or via web interface: +# https://github.com/boniface/open-payments-java/releases/new +``` + +--- + +## Troubleshooting + +### ❌ "No value has been specified for property 'signing.keyId'" + +**Problem:** GPG signing not configured + +**Solution:** +```bash +# Configure in ~/.gradle/gradle.properties +signing.keyId=QRST7890 +signing.password=your-gpg-passphrase +signing.secretKeyRingFile=/Users/yourusername/.gnupg/secring.gpg +``` + +### ❌ "Unable to find secret key" + +**Problem:** Secret keyring not found + +**Solution:** +```bash +# Export secret key to legacy format +gpg --export-secret-keys > ~/.gnupg/secring.gpg + +# Verify location +ls -lh ~/.gnupg/secring.gpg +``` + +### ❌ Publishing Fails with "401 Unauthorized" + +**Problem:** Invalid or expired Central Portal token + +**Solution:** +1. Go to https://central.sonatype.com/account +2. Click "Generate User Token" (revokes old token) +3. Update credentials: + - **Local:** Update `~/.gradle/gradle.properties` + - **CI/CD:** Update GitHub secrets `CENTRAL_PORTAL_USERNAME` and `CENTRAL_PORTAL_PASSWORD` + +### ❌ Signature Verification Fails + +**Problem:** GPG public key not on key servers + +**Solution:** +```bash +# Re-upload to key servers +gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID +gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID + +# Wait a few minutes, then verify +gpg --keyserver keys.openpgp.org --recv-keys YOUR_KEY_ID +``` + +### ❌ Artifacts Not Appearing on Maven Central + +**Problem:** Artifact not showing after 30+ minutes + +**Solution:** +1. Check Central Portal status: https://central.sonatype.com/publishing +2. Look for validation errors in the publishing log +3. Check direct repository: `https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/` +4. Contact Central Portal support if issue persists: https://central.sonatype.com/support + +### ❌ Build Fails in CI/CD + +**Problem:** Tests fail or build errors in GitHub Actions + +**Solution:** +```bash +# Run locally to debug +./gradlew clean test build --stacktrace + +# Fix issues, commit, and re-tag: +git tag -d v0.1.0 # Delete local tag +git push origin :refs/tags/v0.1.0 # Delete remote tag + +# After fixes: +git tag -a v0.1.0 -m "Release version 0.1.0" +git push origin v0.1.0 +``` + +--- + +## CI/CD Integration + +### GitHub Actions Workflow + +The release workflow (`.github/workflows/release.yml`) is triggered when you push a git tag matching `v*.*.*`. + +**What it does:** +1. ✅ Validates Gradle wrapper +2. ✅ Runs all quality checks (tests, coverage, PMD, SpotBugs) +3. ✅ Builds all artifacts (JAR, sources, javadoc) +4. ✅ Imports GPG key for signing +5. ✅ Signs artifacts with GPG +6. ✅ Publishes to Maven Central via Central Portal +7. ✅ Creates GitHub Release with artifacts +8. ✅ Deploys JavaDoc to GitHub Pages +9. ✅ Verifies artifact availability on Maven Central + +**Required GitHub Secrets:** +- `GPG_PRIVATE_KEY` - ASCII armored GPG private key +- `GPG_PASSPHRASE` - GPG key passphrase +- `SIGNING_KEY_ID` - Last 8 characters of GPG key ID +- `CENTRAL_PORTAL_USERNAME` - Central Portal token username +- `CENTRAL_PORTAL_PASSWORD` - Central Portal token password + +--- + +## Quick Reference + +### Release Checklist + +- [ ] All tests passing: `./gradlew test` +- [ ] Code formatted: `./gradlew spotlessApply` +- [ ] No PMD violations: `./gradlew pmdMain` +- [ ] Coverage meets threshold: `./gradlew jacocoTestCoverageVerification` +- [ ] CHANGELOG.md updated +- [ ] Version bumped in `gradle.properties` +- [ ] Committed changes: `git commit -m "Release version X.Y.Z"` +- [ ] Created git tag: `git tag -a vX.Y.Z -m "Release version X.Y.Z"` +- [ ] Pushed to remote: `git push origin branch && git push origin vX.Y.Z` +- [ ] GitHub Actions workflow succeeded +- [ ] Verified on Central Portal +- [ ] Verified on Maven Central +- [ ] Tested installation in sample project +- [ ] Version bumped to next development version + +### Common Commands + +```bash +# Automated Release +git add gradle.properties CHANGELOG.md +git commit -m "Release version 0.1.0" +git tag -a v0.1.0 -m "Release version 0.1.0" +git push origin feature/maven-publish && git push origin v0.1.0 + +# Manual Release +./gradlew clean test build +./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository + +# Verify Publication +curl -I "https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/0.1.0/open-payments-java-0.1.0.pom" + +# Monitor Release +open https://github.com/boniface/open-payments-java/actions +open https://central.sonatype.com/publishing +open https://central.sonatype.com/artifact/zm.hashcode/open-payments-java +``` + +### Version Examples + +```properties +# Pre-1.0 releases +version=0.1.0 # First release +version=0.2.0 # New features +version=0.1.1 # Bug fix +version=1.0.0 # API stabilization + +# Post-1.0 releases +version=1.1.0 # New features +version=1.0.1 # Bug fix +version=2.0.0 # Breaking changes +``` + +### Important Links + +| Resource | URL | +|----------|-----| +| **Central Portal** | https://central.sonatype.com | +| **Account/Tokens** | https://central.sonatype.com/account | +| **Publishing Status** | https://central.sonatype.com/publishing | +| **Namespace Management** | https://central.sonatype.com/publishing/namespaces | +| **Maven Central Search** | https://search.maven.org/artifact/zm.hashcode/open-payments-java | +| **Direct Repository** | https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/ | +| **GitHub Actions** | https://github.com/boniface/open-payments-java/actions | +| **GitHub Secrets** | https://github.com/boniface/open-payments-java/settings/secrets/actions | +| **Central Portal Docs** | https://central.sonatype.org/publish/publish-portal-gradle/ | +| **GPG Guide** | https://central.sonatype.org/publish/requirements/gpg/ | +| **Support** | https://central.sonatype.com/support | + +--- + +## Resources + +- **Central Portal Documentation:** https://central.sonatype.org/publish/publish-portal-gradle/ +- **Gradle Nexus Publish Plugin:** https://github.com/gradle-nexus/publish-plugin +- **GPG Signing Guide:** https://central.sonatype.org/publish/requirements/gpg/ +- **Semantic Versioning:** https://semver.org/ +- **Project Issues:** https://github.com/boniface/open-payments-java/issues + +--- + +**Last Updated:** 2025-10-22 +**Publishing Method:** Maven Central Portal (current standard) +**Namespace:** `zm.hashcode` (verified ✓) +**Current Version:** `0.1.0` diff --git a/build.gradle.kts b/build.gradle.kts index ea7f259..b33c7bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,8 +16,9 @@ plugins { id("utilities-convention") } -group = "zm.hashcode" -version = "1.0-SNAPSHOT" +// Group and version are defined in gradle.properties +// group = zm.hashcode +// version = 1.0.0-SNAPSHOT java { toolchain { diff --git a/buildSrc/src/main/kotlin/publishing-convention.gradle.kts b/buildSrc/src/main/kotlin/publishing-convention.gradle.kts index 29a796f..ee1ab62 100644 --- a/buildSrc/src/main/kotlin/publishing-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/publishing-convention.gradle.kts @@ -1,3 +1,5 @@ +import java.time.Duration + plugins { `java-library` `maven-publish` @@ -31,12 +33,12 @@ publishing { developer { id = "boniface" name = "Boniface Kabaso" - email = "boniface.kabaso@example.com" + email = "550236+boniface@users.noreply.github.com" } developer { id = "espoir" - name = "Espoir D" - email = "espoir.d@example.com" + name = "Espoir Diteekemena" + email = "47171587+ESPOIR-DITE@users.noreply.github.com" } } @@ -59,8 +61,21 @@ signing { nexusPublishing { repositories { sonatype { - nexusUrl = uri("https://s01.oss.sonatype.org/service/local/") - snapshotRepositoryUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + // Maven Central Portal + nexusUrl = uri("https://central.sonatype.com") + + // Use Central Portal token authentication + // Set via environment variables or ~/.gradle/gradle.properties: + // centralPortalUsername= + // centralPortalPassword= + username = project.findProperty("centralPortalUsername") as String? + ?: System.getenv("CENTRAL_PORTAL_USERNAME") + password = project.findProperty("centralPortalPassword") as String? + ?: System.getenv("CENTRAL_PORTAL_PASSWORD") } } + + // Timeout configuration for large uploads + connectTimeout = Duration.ofMinutes(3) + clientTimeout = Duration.ofMinutes(3) } diff --git a/docs/CI_CD_SETUP.md b/docs/CI_CD_SETUP.md index ab08f51..abd3eba 100644 --- a/docs/CI_CD_SETUP.md +++ b/docs/CI_CD_SETUP.md @@ -8,7 +8,7 @@ This document describes the CI/CD pipeline configuration for the Open Payments J ## Overview -The project uses GitHub Actions for continuous integration, quality checks, and automated releases to Maven Central. +The project uses GitHub Actions for continuous integration, quality checks, and automated releases to Maven Central via the **Central Portal**. ## Workflows @@ -39,13 +39,13 @@ Runs on every push and pull request to `main` and `develop` branches. ### 2. Release Workflow (`.github/workflows/release.yml`) -Triggers when a version tag is pushed (e.g., `v1.0.0`). +Triggers when a version tag is pushed (e.g., `v0.1.0`). #### Steps 1. **Validation** - Gradle wrapper validation 2. **Quality Gates** - All CI checks must pass 3. **Build & Sign** - Artifacts signed with GPG -4. **Publish to Maven Central** - Via Sonatype OSSRH +4. **Publish to Maven Central** - Via Central Portal 5. **GitHub Release** - Create release with artifacts 6. **JavaDoc Deployment** - Publish to GitHub Pages 7. **Verification** - Confirm artifact availability on Maven Central @@ -64,10 +64,11 @@ Analyzes code for security vulnerabilities and quality issues. Configure these secrets in your GitHub repository settings: ### Maven Central Publishing -- `SONATYPE_USERNAME` - Sonatype JIRA username -- `SONATYPE_PASSWORD` - Sonatype JIRA password -- `GPG_PRIVATE_KEY` - Base64 encoded GPG private key +- `CENTRAL_PORTAL_USERNAME` - Central Portal token username +- `CENTRAL_PORTAL_PASSWORD` - Central Portal token password +- `GPG_PRIVATE_KEY` - ASCII armored GPG private key - `GPG_PASSPHRASE` - GPG key passphrase +- `SIGNING_KEY_ID` - Last 8 characters of GPG key ID ### Code Coverage - `CODECOV_TOKEN` - Codecov.io token for coverage reports @@ -136,12 +137,17 @@ open build/reports/jacoco/test/html/index.html ### Prerequisites -1. **Sonatype JIRA Account** - - Create account at https://issues.sonatype.org - - Request new project (OSSRH ticket) - - Wait for approval (~2 business days) +1. **Central Portal Account** + - Register at https://central.sonatype.com + - Sign up with GitHub (recommended) + - **Note:** The `zm.hashcode` namespace is already verified -2. **GPG Key for Signing** +2. **Central Portal Token** + - Go to https://central.sonatype.com/account + - Click "Generate User Token" + - Save username and password as GitHub secrets + +3. **GPG Key for Signing** ```bash # Generate GPG key gpg --gen-key @@ -149,49 +155,50 @@ open build/reports/jacoco/test/html/index.html # List keys gpg --list-keys - # Export private key (base64) - gpg --export-secret-keys YOUR_KEY_ID | base64 + # Export private key (ASCII armored) + gpg --export-secret-keys --armor YOUR_KEY_ID - # Export public key to keyserver + # Export public key to keyservers + gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID ``` -3. **GitHub Secrets Configuration** +4. **GitHub Secrets Configuration** - Add all required secrets to repository - - Test with a snapshot release first + - See [RELEASE_GUIDE.md](../RELEASE_GUIDE.md) for detailed setup ### Release Process 1. **Prepare Release** ```bash - # Update version in build.gradle.kts - version = "1.0.0" + # Update version in gradle.properties + version=0.1.0 # Commit version change - git add build.gradle.kts - git commit -m "chore: prepare release 1.0.0" + git add gradle.properties + git commit -m "Release version 0.1.0" git push ``` 2. **Create Release Tag** ```bash # Create and push tag - git tag -a v1.0.0 -m "Release version 1.0.0" - git push origin v1.0.0 + git tag -a v0.1.0 -m "Release version 0.1.0" + git push origin v0.1.0 ``` 3. **Monitor Workflow** - Watch GitHub Actions for progress - Release workflow publishes to Maven Central - - Verify artifact on https://repo1.maven.org/maven2/ + - Verify artifact on https://central.sonatype.com/artifact/zm.hashcode/open-payments-java 4. **Post-Release** ```bash - # Update to next snapshot version - version = "1.1.0-SNAPSHOT" + # Update to next version + version=0.2.0 - git add build.gradle.kts - git commit -m "chore: prepare for next development iteration" + git add gradle.properties + git commit -m "Bump version to 0.2.0" git push ``` @@ -201,7 +208,7 @@ Update README badges with actual values once integrated: ```markdown [![CI](https://github.com/boniface/open-payments-java/workflows/CI/badge.svg)](https://github.com/boniface/open-payments-java/actions) -[![codecov](https://codecov.io/gh/yourusername/open-payments-java/branch/main/graph/badge.svg)](https://codecov.io/gh/boniface/open-payments-java) +[![codecov](https://codecov.io/gh/boniface/open-payments-java/branch/main/graph/badge.svg)](https://codecov.io/gh/boniface/open-payments-java) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=boniface_open-payments-java&metric=alert_status)](https://sonarcloud.io/dashboard?id=boniface_open-payments-java) [![Maven Central](https://img.shields.io/maven-central/v/zm.hashcode/open-payments-java.svg)](https://search.maven.org/artifact/zm.hashcode/open-payments-java) ``` @@ -226,22 +233,22 @@ open build/reports/jacoco/test/html/index.html # Commit formatted code git add . -git commit -m "style: apply code formatting" +git commit -m "Apply code formatting" ``` ### Signing Fails ```bash # Verify GPG key is configured -echo $GPG_PRIVATE_KEY | base64 -d | gpg --import +echo $GPG_PRIVATE_KEY | gpg --import # Test signing locally ./gradlew signMavenJavaPublication ``` ### Maven Central Sync Issues -- Wait 10-30 minutes after release +- Wait 15-30 minutes after release - Check https://repo1.maven.org/maven2/zm/hashcode/open-payments-java/ -- Verify in Sonatype Nexus: https://s01.oss.sonatype.org/ +- Verify in Central Portal: https://central.sonatype.com/publishing ## Best Practices @@ -263,13 +270,15 @@ Follow [Conventional Commits](https://www.conventionalcommits.org/): ### Versioning Follow [Semantic Versioning](https://semver.org/): -- MAJOR: Breaking changes -- MINOR: New features (backward compatible) -- PATCH: Bug fixes (backward compatible) +- **0.x.y** - Pre-1.0 development (API may change) +- **1.0.0** - First stable release +- **MAJOR** - Breaking changes +- **MINOR** - New features (backward compatible) +- **PATCH** - Bug fixes (backward compatible) ## Resources -- [Maven Central Publishing Guide](https://central.sonatype.org/publish/publish-guide/) +- [Maven Central Portal Documentation](https://central.sonatype.org/publish/publish-portal-gradle/) - [GitHub Actions Documentation](https://docs.github.com/en/actions) - [JaCoCo Documentation](https://www.jacoco.org/jacoco/trunk/doc/) - [SpotBugs Manual](https://spotbugs.readthedocs.io/) diff --git a/docs/GITHUB_ACTIONS_SETUP.md b/docs/GITHUB_ACTIONS_SETUP.md index 27161f3..6c4f6e5 100644 --- a/docs/GITHUB_ACTIONS_SETUP.md +++ b/docs/GITHUB_ACTIONS_SETUP.md @@ -2,7 +2,7 @@ ## What's Been Configured -This project has CI/CD following Maven Central. +This project has automated CI/CD following Maven Central Portal best practices. ## Workflows Created @@ -25,12 +25,12 @@ Runs on every push and PR to `main`/`develop` - Java 25 ### 2. **Release Workflow** (`.github/workflows/release.yml`) -Triggers on version tags (e.g., `v1.0.0`) +Triggers on version tags (e.g., `v0.1.0`) **Steps:** - Run all quality checks - Build and sign artifacts (GPG) -- Publish to Maven Central (Sonatype OSSRH) +- Publish to Maven Central (Central Portal) - Create GitHub Release - Deploy JavaDoc to GitHub Pages - Verify Maven Central availability @@ -79,35 +79,41 @@ Configure these in **GitHub Settings → Secrets and variables → Actions**: ### Maven Central Publishing ``` -SONATYPE_USERNAME - Your Sonatype JIRA username -SONATYPE_PASSWORD - Your Sonatype JIRA password -GPG_PRIVATE_KEY - Base64 encoded GPG private key -GPG_PASSPHRASE - Your GPG key passphrase +CENTRAL_PORTAL_USERNAME - Central Portal token username +CENTRAL_PORTAL_PASSWORD - Central Portal token password +GPG_PRIVATE_KEY - ASCII armored GPG private key +GPG_PASSPHRASE - Your GPG key passphrase +SIGNING_KEY_ID - Last 8 characters of GPG key ID ``` ### Code Coverage ``` -CODECOV_TOKEN - Token from codecov.io +CODECOV_TOKEN - Token from codecov.io ``` ### Code Quality ``` -SONAR_TOKEN - Token from sonarcloud.io +SONAR_TOKEN - Token from sonarcloud.io ``` ## Setup Checklist ### Before First Release -- [ ] **Create Sonatype JIRA account** - - Register at https://issues.sonatype.org - - Create New Project ticket for `zm.hashcode` - - Wait for approval (~2 business days) +- [ ] **Create Central Portal account** + - Register at https://central.sonatype.com + - **Note:** `zm.hashcode` namespace is already verified ✓ + +- [ ] **Generate Central Portal token** + - Go to https://central.sonatype.com/account + - Click "Generate User Token" + - Save username and password - [ ] **Generate GPG key** ```bash gpg --gen-key - gpg --export-secret-keys YOUR_KEY_ID | base64 > private-key.txt + gpg --export-secret-keys --armor YOUR_KEY_ID + gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID ``` @@ -123,8 +129,8 @@ SONAR_TOKEN - Token from sonarcloud.io - Update `sonar.projectKey` and `sonar.organization` in build.gradle.kts - [ ] **Configure GitHub Secrets** - - Add all 6 secrets listed above - - Test with a snapshot build first + - Add all 7 secrets listed above + - See [RELEASE_GUIDE.md](../RELEASE_GUIDE.md) for details - [ ] **Update Repository Settings** - Enable GitHub Pages (Settings → Pages → Source: gh-pages branch) @@ -136,9 +142,9 @@ SONAR_TOKEN - Token from sonarcloud.io Replace placeholders with actual values: ```markdown -[![CI](https://github.com/YOUR_USERNAME/open-payments-java/workflows/CI/badge.svg)](https://github.com/YOUR_USERNAME/open-payments-java/actions) -[![codecov](https://codecov.io/gh/YOUR_USERNAME/open-payments-java/branch/main/graph/badge.svg)](https://codecov.io/gh/YOUR_USERNAME/open-payments-java) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=YOUR_ORG_open-payments-java&metric=alert_status)](https://sonarcloud.io/dashboard?id=YOUR_ORG_open-payments-java) +[![CI](https://github.com/boniface/open-payments-java/workflows/CI/badge.svg)](https://github.com/boniface/open-payments-java/actions) +[![codecov](https://codecov.io/gh/boniface/open-payments-java/branch/main/graph/badge.svg)](https://codecov.io/gh/boniface/open-payments-java) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=boniface_open-payments-java&metric=alert_status)](https://sonarcloud.io/dashboard?id=boniface_open-payments-java) [![Maven Central](https://img.shields.io/maven-central/v/zm.hashcode/open-payments-java.svg)](https://search.maven.org/artifact/zm.hashcode/open-payments-java) ``` @@ -146,32 +152,32 @@ Replace placeholders with actual values: ### 1. Prepare Release ```bash -# Update version in build.gradle.kts -version = "1.0.0" +# Update version in gradle.properties +version=0.1.0 -git add build.gradle.kts -git commit -m "chore: prepare release 1.0.0" +git add gradle.properties +git commit -m "Release version 0.1.0" git push ``` ### 2. Create Tag ```bash -git tag -a v1.0.0 -m "Release version 1.0.0" -git push origin v1.0.0 +git tag -a v0.1.0 -m "Release version 0.1.0" +git push origin v0.1.0 ``` ### 3. Monitor - Watch GitHub Actions tab - Release workflow runs automatically -- Artifact published to Maven Central in ~10-30 minutes +- Artifact published to Maven Central in ~15-30 minutes ### 4. Post-Release ```bash -# Bump to next snapshot version -version = "1.1.0-SNAPSHOT" +# Bump to next version +version=0.2.0 -git add build.gradle.kts -git commit -m "chore: prepare for next development iteration" +git add gradle.properties +git commit -m "Bump version to 0.2.0" git push ``` @@ -214,7 +220,7 @@ After setup, you'll have: ## 📚 Documentation -See [docs/CI_CD_SETUP.md](docs/CI_CD_SETUP.md) for detailed documentation. +See [docs/CI_CD_SETUP.md](CI_CD_SETUP.md) for detailed documentation. ## What Gets Checked on Every PR diff --git a/docs/SDK_STRUCTURE.md b/docs/SDK_STRUCTURE.md index 6a2ccb8..8def4d1 100644 --- a/docs/SDK_STRUCTURE.md +++ b/docs/SDK_STRUCTURE.md @@ -14,11 +14,24 @@ This document provides a detailed breakdown of the package structure and file or zm.hashcode.openpayments/ ├── client/ # Main client API ├── auth/ # Authentication & Authorization (GNAP) +│ ├── exception/ # Auth-specific exceptions +│ ├── grant/ # GNAP grant protocol +│ ├── keys/ # Client key management +│ ├── signature/ # HTTP message signatures +│ └── token/ # Token management ├── wallet/ # Wallet Address operations ├── payment/ # Payment operations │ ├── incoming/ # Incoming payments │ ├── outgoing/ # Outgoing payments │ └── quote/ # Payment quotes +├── http/ # HTTP abstraction layer +│ ├── config/ # HTTP client configuration +│ ├── core/ # Core HTTP interfaces +│ ├── factory/ # HTTP client factory +│ ├── impl/ # HTTP client implementations +│ ├── interceptor/ # Request/response interceptors +│ └── resilience/ # Retry and resilience +├── util/ # Cross-cutting utilities └── model/ # Shared models and exceptions ``` diff --git a/gradle.properties b/gradle.properties index b6b19e4..a4d464a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,21 @@ # Project group=zm.hashcode -version=1.0.0-SNAPSHOT +version=0.1.0 + +# Versioning Strategy (Semantic Versioning 2.0.0): +# - 0.x.y = Initial development (pre-production, API may change) +# - 0.1.0 = First release +# - 0.2.0, 0.3.0 = Subsequent releases with new features/fixes +# - 1.0.0 = First stable, production-ready release +# +# Publishing Notes: +# - Central Portal does NOT support SNAPSHOT deployments +# - Version is automatically updated by CI/CD after each release +# - Manual releases: Update version here, create git tag, push + +# Kotlin +kotlin.version=2.2.20 +kotlin.code.style=official # Kotlin kotlin.version=2.2.20 @@ -15,9 +30,16 @@ org.gradle.configuration-cache=true # Java kotlin.stdlib.default.dependency=false -# Maven Central Publishing -# signing.keyId=YOUR_KEY_ID -# signing.password=YOUR_KEY_PASSWORD -# signing.secretKeyRingFile=/path/to/secring.gpg -# ossrhUsername=YOUR_SONATYPE_USERNAME -# ossrhPassword=YOUR_SONATYPE_PASSWORD \ No newline at end of file +# Maven Central Publishing (Central Portal) +# ================================================================ +# Configure in ~/.gradle/gradle.properties (NOT in project files) +# +# GPG Signing: +# signing.keyId=YOUR_GPG_KEY_ID +# signing.password=YOUR_GPG_KEY_PASSWORD +# signing.secretKeyRingFile=/path/to/secring.gpg +# +# Central Portal Token (get from https://central.sonatype.com/account): +# centralPortalUsername=YOUR_TOKEN_USERNAME +# centralPortalPassword=YOUR_TOKEN_PASSWORD +# ================================================================ \ No newline at end of file