From 61d08aca922e2207c75e54d587cb2c6df9779594 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:30:17 +0000 Subject: [PATCH 1/8] Initial plan From c9426eab69b092e43da59d0ad4b168b01fb5459e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:42:59 +0000 Subject: [PATCH 2/8] Add automated release and publish workflow with axion-release and nexus-publish plugins Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- .github/workflows/loxone-java-release.yml | 58 +++++++++++++++++++++++ build.gradle.kts | 48 ++++++++++++------- gradle.properties | 2 - 3 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/loxone-java-release.yml diff --git a/.github/workflows/loxone-java-release.yml b/.github/workflows/loxone-java-release.yml new file mode 100644 index 0000000..23d08b6 --- /dev/null +++ b/.github/workflows/loxone-java-release.yml @@ -0,0 +1,58 @@ +--- +name: Loxone Java release + +on: + workflow_dispatch: + inputs: + versionIncrement: + description: "What version number to increment" + required: true + type: choice + default: incrementPatch + options: + - incrementPatch + - incrementMinor + - incrementMajor + +jobs: + release: + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + token: ${{ secrets.SMARTEON_GIT_TOKEN }} + + - name: Setup JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + + - name: Set up git configuration + run: | + git config --global user.name 'Smarteon Git' + git config --global user.email 'accounts+git@smarteon.cz' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Release and push tag + shell: bash + run: | + # Fetch a full copy of the repo, as required by release plugin: + git fetch --tags --unshallow + # Run release: + ./gradlew release -Prelease.versionIncrementer=${{ inputs.versionIncrement }} + + - name: Publish to OSS Sonatype + shell: bash + env: + OSS_USER: ${{ secrets.OSS_USER }} + OSS_PASS: ${{ secrets.OSS_PASS }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASS: ${{ secrets.SIGNING_PASS }} + run: | + ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository diff --git a/build.gradle.kts b/build.gradle.kts index 9e1422e..4f86b2a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,21 @@ group = "cz.smarteon" +scmVersion { + tag { + prefix.set("") + versionSeparator.set("") + } +} + +project.version = scmVersion.version + plugins { `java-library` signing `maven-publish` jacoco - id("net.researchgate.release") version "2.6.0" + id("pl.allegro.tech.build.axion-release") version "1.14.4" + id("io.github.gradle-nexus.publish-plugin") version "1.3.0" id("ru.vyarus.quality") version "4.8.0" kotlin("jvm") version "1.7.10" } @@ -78,8 +88,8 @@ dependencies { testImplementation("org.awaitility:awaitility-kotlin:4.1.1") } -val ossUser: String? by project -val ossPass: String? by project +val ossUser: String? = System.getenv("OSS_USER") +val ossPass: String? = System.getenv("OSS_PASS") publishing { publications { @@ -128,22 +138,30 @@ publishing { } } } +} - repositories { - maven { - name = "oss" - url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - authentication { - credentials { - username = ossUser - password = ossPass - } +if (ossUser != null && ossPass != null) { + nexusPublishing { + repositories { + sonatype { + username.set(ossUser) + password.set(ossPass) } } } } -if (hasProperty("signing.keyId")) { +val signingKey: String? = System.getenv("SIGNING_KEY") +val signingPassword: String? = System.getenv("SIGNING_PASS") +if (signingKey != null && signingPassword != null) { + signing { + setRequired({ + !project.version.toString().endsWith("-SNAPSHOT") + }) + useInMemoryPgpKeys(signingKey, signingPassword) + sign(publishing.publications["library"]) + } +} else if (hasProperty("signing.keyId")) { signing { setRequired({ !project.version.toString().endsWith("-SNAPSHOT") @@ -182,8 +200,4 @@ tasks { check { dependsOn(jacocoTestReport) } - - afterReleaseBuild { - dependsOn(getByName("publishLibraryPublicationToOssRepository")) - } } diff --git a/gradle.properties b/gradle.properties index f637d47..6612600 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,2 @@ -version=2.11.1-SNAPSHOT - # do not auto add kotlin stdlib to library dependencies kotlin.stdlib.default.dependency=false From 71e66ef9531551f9c2042ff1da9f86a7e75b7465 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:46:09 +0000 Subject: [PATCH 3/8] Add comprehensive release documentation to README and RELEASE_SETUP guide Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- README.md | 48 ++++++++++++++ RELEASE_SETUP.md | 163 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 RELEASE_SETUP.md diff --git a/README.md b/README.md index 9029228..c6d88f2 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,54 @@ MiniserverDiscovererTest > should discover() FAILED It is adviced to install the lombok plugin for your IDE of choice, this makes coding & debugging easier, you can find the instructions for installing the plugin for various IDE's on the following location https://projectlombok.org/setup/ +### Releasing + +This project uses automated releases via GitHub Actions with the [axion-release](https://github.com/allegro/axion-release-plugin) plugin and publishes to [OSS Sonatype](https://oss.sonatype.org/). + +#### Release Process + +To create a new release: + +1. Navigate to **Actions** → **Loxone Java release** in the GitHub repository +2. Click **Run workflow** +3. Select the version increment type: + - `incrementPatch` - for bug fixes (e.g., 2.9.2 → 2.9.3) + - `incrementMinor` - for new features (e.g., 2.9.2 → 2.10.0) + - `incrementMajor` - for breaking changes (e.g., 2.9.2 → 3.0.0) +4. Click **Run workflow** + +The workflow will: +- Create and push a new version tag +- Build the project +- Sign the artifacts +- Publish to OSS Sonatype +- Automatically close and release the staging repository + +#### Required GitHub Secrets + +The following secrets must be configured in the repository settings: + +- `OSS_USER` - Sonatype OSS username (JIRA account) +- `OSS_PASS` - Sonatype OSS password +- `SIGNING_KEY` - GPG private key for signing artifacts (ASCII-armored, base64 encoded) +- `SIGNING_PASS` - Passphrase for the GPG key +- `SMARTEON_GIT_TOKEN` - GitHub Personal Access Token with `repo` scope for pushing tags + +#### Local Release Testing + +To test the release process locally: + +```bash +# Check current version +./gradlew currentVersion + +# Test release process (dry-run) +./gradlew release -Prelease.dryRun + +# Test publishing to local Maven repository +./gradlew publishToMavenLocal +``` + ### Writing unit tests We prefer [Junit5 with Kotlin](https://www.baeldung.com/kotlin/junit-5-kotlin), [Strikt](https://strikt.io/) and [Mockk](https://mockk.io/) for writing unit testing. Please follow the guidelines and documentation on the internet. Kotlin is easy to grab fro Java programmers and in many cases it allows more concise code. However Java + Junit5 unit diff --git a/RELEASE_SETUP.md b/RELEASE_SETUP.md new file mode 100644 index 0000000..1f3e4c9 --- /dev/null +++ b/RELEASE_SETUP.md @@ -0,0 +1,163 @@ +# Release Setup Guide + +This document describes how to set up the automated release process for loxone-java. + +## Prerequisites + +Before you can publish releases, you need: + +1. **Sonatype JIRA account** with permissions to publish to `cz.smarteon` group +2. **GPG key** for signing artifacts +3. **GitHub repository admin access** to configure secrets + +## Step 1: Sonatype OSS Setup + +If you don't already have access to publish to `cz.smarteon`: + +1. Create a Sonatype JIRA account at https://issues.sonatype.org +2. Request access to the `cz.smarteon` group ID (or create a new ticket if this is the first time) +3. Wait for approval (usually takes 1-2 business days) + +Reference: [Sonatype OSSRH Guide](https://central.sonatype.org/publish/publish-guide/) + +## Step 2: GPG Key Setup + +### Generate a GPG Key (if you don't have one) + +```bash +# Generate a new key +gpg --gen-key + +# Follow the prompts: +# - Use RSA and RSA +# - Key size: 4096 bits +# - Expiration: choose appropriate duration +# - Enter your name and email +# - Set a strong passphrase +``` + +### Export the GPG Key + +```bash +# List your keys to find the key ID +gpg --list-secret-keys --keyid-format=long + +# Export the private key (replace KEY_ID with your actual key ID) +gpg --export-secret-keys -a KEY_ID | base64 -w0 > signing-key.txt + +# The signing-key.txt now contains your base64-encoded private key +``` + +### Publish Your Public Key + +```bash +# Publish to key servers (replace KEY_ID with your actual key ID) +gpg --keyserver keyserver.ubuntu.com --send-keys KEY_ID +gpg --keyserver keys.openpgp.org --send-keys KEY_ID +``` + +Reference: [Working with PGP Signatures](https://central.sonatype.org/publish/requirements/gpg/) + +## Step 3: Configure GitHub Secrets + +Add the following secrets in your GitHub repository settings (**Settings** → **Secrets and variables** → **Actions**): + +### Required Secrets + +| Secret Name | Description | How to Obtain | +|------------|-------------|---------------| +| `OSS_USER` | Sonatype JIRA username | Your JIRA account username | +| `OSS_PASS` | Sonatype JIRA password | Your JIRA account password | +| `SIGNING_KEY` | GPG private key (base64) | Content of `signing-key.txt` from Step 2 | +| `SIGNING_PASS` | GPG key passphrase | The passphrase you set when creating the GPG key | +| `SMARTEON_GIT_TOKEN` | GitHub Personal Access Token | See below | + +### Creating GitHub Personal Access Token + +1. Go to GitHub **Settings** → **Developer settings** → **Personal access tokens** → **Tokens (classic)** +2. Click **Generate new token (classic)** +3. Select scopes: + - `repo` (Full control of private repositories) +4. Click **Generate token** +5. Copy the token and add it as `SMARTEON_GIT_TOKEN` secret + +## Step 4: Verify Setup + +### Test Gradle Configuration + +```bash +# Verify the build works +./gradlew clean build -x test + +# Check version detection +./gradlew currentVersion + +# Test signing configuration (will skip if not on a release version) +SIGNING_KEY="$(cat signing-key.txt)" SIGNING_PASS="your-passphrase" ./gradlew build +``` + +### Test Publishing (Optional) + +```bash +# Publish to local Maven repository +./gradlew publishToMavenLocal + +# Check the published artifacts +ls ~/.m2/repository/cz/smarteon/loxone-java/ +``` + +## Step 5: First Release + +Once all secrets are configured: + +1. Go to **Actions** → **Loxone Java release** +2. Click **Run workflow** +3. Select `incrementPatch` for the first automated release +4. Click **Run workflow** + +The workflow will: +- Create a new git tag (e.g., `2.11.1`) +- Build and test the project +- Sign all artifacts with your GPG key +- Publish to OSS Sonatype staging repository +- Automatically close and release the staging repository +- Artifacts will be available on Maven Central within ~10 minutes + +## Troubleshooting + +### Build Fails with "invalid signature" + +- Verify `SIGNING_KEY` is base64-encoded correctly +- Ensure `SIGNING_PASS` matches your GPG key passphrase + +### Publish Fails with "401 Unauthorized" + +- Verify `OSS_USER` and `OSS_PASS` are correct +- Ensure your Sonatype account has permission for `cz.smarteon` group + +### Tag Already Exists + +- The release process will fail if the version tag already exists +- Delete the tag if needed: `git push --delete origin ` + +### Staging Repository Not Closing + +- Check Sonatype Nexus UI: https://oss.sonatype.org/#stagingRepositories +- Look for validation errors in the "Activity" tab +- Common issues: missing signatures, invalid POM metadata + +## Version Management + +This project uses [axion-release](https://github.com/allegro/axion-release-plugin) for version management: + +- Versions are derived from git tags +- Current version: `./gradlew currentVersion` +- When on a tagged commit: release version (e.g., `2.11.1`) +- When ahead of a tag: snapshot version (e.g., `2.11.2-SNAPSHOT`) + +## Additional Resources + +- [Axion Release Plugin Documentation](https://axion-release-plugin.readthedocs.io/) +- [Nexus Publish Plugin](https://github.com/gradle-nexus/publish-plugin) +- [Maven Central Publishing Guide](https://central.sonatype.org/publish/) +- [Sonatype Nexus Repository Manager](https://oss.sonatype.org/) From 4f9dfa38cbfda144c86140f074c855becc629989 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:48:39 +0000 Subject: [PATCH 4/8] Add comprehensive environment variables reference and release checklist documentation Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- ENVIRONMENT_VARIABLES.md | 177 ++++++++++++++++++++++++++++++++++++ RELEASE_CHECKLIST.md | 188 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 ENVIRONMENT_VARIABLES.md create mode 100644 RELEASE_CHECKLIST.md diff --git a/ENVIRONMENT_VARIABLES.md b/ENVIRONMENT_VARIABLES.md new file mode 100644 index 0000000..1e01ed5 --- /dev/null +++ b/ENVIRONMENT_VARIABLES.md @@ -0,0 +1,177 @@ +# Environment Variables and Secrets Summary + +This document provides a quick reference for all environment variables and GitHub secrets required for the automated release process. + +## GitHub Secrets (Required) + +These must be configured in **Settings → Secrets and variables → Actions** in the GitHub repository: + +### 1. OSS_USER +- **Purpose**: Sonatype OSS username for publishing to Maven Central +- **Type**: Sonatype JIRA account username +- **How to obtain**: Create account at https://issues.sonatype.org/ +- **Example**: `your-jira-username` + +### 2. OSS_PASS +- **Purpose**: Sonatype OSS password for authentication +- **Type**: Sonatype JIRA account password +- **How to obtain**: Password from your JIRA account +- **Security**: Keep this secret secure, rotate regularly + +### 3. SIGNING_KEY +- **Purpose**: GPG private key for signing Maven artifacts +- **Type**: ASCII-armored GPG private key, base64 encoded +- **How to obtain**: + ```bash + gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0 + ``` +- **Requirements**: + - Must be a valid GPG/PGP key + - Public key must be published to key servers + - Must be base64 encoded for storage + +### 4. SIGNING_PASS +- **Purpose**: Passphrase for the GPG signing key +- **Type**: String passphrase +- **How to obtain**: The passphrase you set when creating your GPG key +- **Security**: Store securely, never commit to repository + +### 5. SMARTEON_GIT_TOKEN +- **Purpose**: GitHub Personal Access Token for pushing release tags +- **Type**: GitHub PAT with `repo` scope +- **How to obtain**: + 1. GitHub Settings → Developer settings → Personal access tokens + 2. Generate new token (classic) + 3. Select `repo` scope +- **Permissions Required**: Full control of private repositories + +## Local Development Environment Variables (Optional) + +For local testing of the release and publish process: + +### Environment Variables + +```bash +# Sonatype credentials +export OSS_USER="your-jira-username" +export OSS_PASS="your-jira-password" + +# GPG signing +export SIGNING_KEY="$(gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0)" +export SIGNING_PASS="your-gpg-passphrase" +``` + +### Alternative: Gradle Properties + +Instead of environment variables, you can use `~/.gradle/gradle.properties`: + +```properties +# Sonatype credentials (legacy support - environment variables preferred) +ossUser=your-jira-username +ossPass=your-jira-password + +# GPG signing (file-based approach) +signing.keyId=YOUR_KEY_ID +signing.password=your-gpg-passphrase +signing.secretKeyRingFile=/path/to/.gnupg/secring.gpg +``` + +**Note**: The environment variable approach (used in CI/CD) is preferred as it's more secure. + +## Gradle Tasks Reference + +### Version and Release Tasks + +```bash +# Check current version (derived from git tags) +./gradlew currentVersion + +# Create a new release (increment patch version) +./gradlew release -Prelease.versionIncrementer=incrementPatch + +# Create a new release (increment minor version) +./gradlew release -Prelease.versionIncrementer=incrementMinor + +# Create a new release (increment major version) +./gradlew release -Prelease.versionIncrementer=incrementMajor + +# Dry run release (doesn't create tags) +./gradlew release -Prelease.dryRun +``` + +### Publishing Tasks + +```bash +# Publish to local Maven repository (testing) +./gradlew publishToMavenLocal + +# Publish to Sonatype (requires OSS_USER and OSS_PASS) +./gradlew publishToSonatype + +# Close and release staging repository +./gradlew closeAndReleaseSonatypeStagingRepository + +# Full publish workflow (what CI/CD does) +./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository +``` + +## Security Best Practices + +1. **Never commit secrets** to the repository +2. **Rotate credentials regularly**, especially if they may have been exposed +3. **Use environment variables** in CI/CD instead of storing in files +4. **Limit access** to GitHub secrets to repository administrators only +5. **Enable 2FA** on your Sonatype JIRA account +6. **Back up your GPG key** in a secure location +7. **Use a strong passphrase** for your GPG key + +## Troubleshooting + +### Missing Secret Error +``` +Error: Secret OSS_USER not found +``` +**Solution**: Ensure all required secrets are configured in GitHub repository settings + +### GPG Signing Failed +``` +Error: Invalid signing key +``` +**Solution**: +- Verify SIGNING_KEY is base64 encoded +- Verify SIGNING_PASS matches the key passphrase +- Test locally: `echo $SIGNING_KEY | base64 -d | gpg --import` + +### Publishing Unauthorized +``` +401 Unauthorized +``` +**Solution**: +- Verify OSS_USER and OSS_PASS are correct +- Ensure your account has access to `cz.smarteon` group ID +- Check if your Sonatype account is active + +### Version Already Exists +``` +Tag X.Y.Z already exists +``` +**Solution**: +- The version tag already exists in git +- Either delete the tag or increment to the next version +- Check: `git tag -l` to list all tags + +## Support + +For issues with: +- **Gradle build**: Check project documentation +- **Sonatype/Maven Central**: https://issues.sonatype.org/ +- **GPG keys**: https://www.gnupg.org/documentation/ +- **GitHub Actions**: GitHub repository Issues + +## References + +- [Sonatype OSSRH Guide](https://central.sonatype.org/publish/publish-guide/) +- [Maven Central Requirements](https://central.sonatype.org/publish/requirements/) +- [GPG Key Management](https://central.sonatype.org/publish/requirements/gpg/) +- [Axion Release Plugin](https://axion-release-plugin.readthedocs.io/) +- [Nexus Publish Plugin](https://github.com/gradle-nexus/publish-plugin) diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..2d125dc --- /dev/null +++ b/RELEASE_CHECKLIST.md @@ -0,0 +1,188 @@ +# Release Setup Checklist + +Use this checklist to ensure all requirements are met before using the automated release workflow. + +## Pre-requisites Checklist + +### ✓ Sonatype Account Setup +- [ ] Created Sonatype JIRA account at https://issues.sonatype.org/ +- [ ] Requested access to `cz.smarteon` group ID (or verified existing access) +- [ ] Received approval from Sonatype (wait time: 1-2 business days) +- [ ] Can log in to https://oss.sonatype.org/ with JIRA credentials + +### ✓ GPG Key Setup +- [ ] Generated GPG key (or have an existing one) + ```bash + gpg --gen-key + ``` +- [ ] Recorded key ID + ```bash + gpg --list-secret-keys --keyid-format=long + ``` +- [ ] Exported private key to base64 + ```bash + gpg --export-secret-keys -a KEY_ID | base64 -w0 > signing-key.txt + ``` +- [ ] Published public key to key servers + ```bash + gpg --keyserver keyserver.ubuntu.com --send-keys KEY_ID + gpg --keyserver keys.openpgp.org --send-keys KEY_ID + ``` +- [ ] Saved passphrase securely + +### ✓ GitHub Setup +- [ ] Have admin access to GitHub repository +- [ ] Created GitHub Personal Access Token with `repo` scope +- [ ] Configured all required GitHub Secrets: + - [ ] `OSS_USER` - Sonatype username + - [ ] `OSS_PASS` - Sonatype password + - [ ] `SIGNING_KEY` - GPG key (base64 encoded) + - [ ] `SIGNING_PASS` - GPG key passphrase + - [ ] `SMARTEON_GIT_TOKEN` - GitHub PAT + +## Verification Checklist + +### ✓ Local Build Verification +- [ ] Project builds successfully + ```bash + ./gradlew clean build + ``` +- [ ] Current version is detected correctly + ```bash + ./gradlew currentVersion + ``` +- [ ] Can publish to local Maven repository + ```bash + ./gradlew publishToMavenLocal + ``` + +### ✓ Publishing Configuration Verification +- [ ] Sonatype tasks are available (with OSS_USER and OSS_PASS set) + ```bash + OSS_USER=test OSS_PASS=test ./gradlew tasks --group=publishing + ``` +- [ ] Release tasks are available + ```bash + ./gradlew tasks --group=release + ``` + +### ✓ Signing Configuration Verification +- [ ] Can import signing key locally + ```bash + echo "$SIGNING_KEY" | base64 -d | gpg --import + ``` +- [ ] Signing works (on non-SNAPSHOT version) + ```bash + # Create a test tag first + git tag -a test-signing-1.0.0 -m "Test" + SIGNING_KEY="..." SIGNING_PASS="..." ./gradlew build + git tag -d test-signing-1.0.0 + ``` + +## GitHub Actions Workflow Verification + +### ✓ Workflow File +- [ ] Workflow file exists: `.github/workflows/loxone-java-release.yml` +- [ ] YAML syntax is valid +- [ ] All required secrets are referenced in the workflow +- [ ] Workflow is visible in Actions tab + +### ✓ First Release Test +- [ ] Navigate to Actions → Loxone Java release +- [ ] Workflow is available and can be manually triggered +- [ ] All required secrets are configured (workflow won't fail with missing secrets) + +## Post-Setup Checklist + +### ✓ Documentation +- [ ] Read RELEASE_SETUP.md for detailed instructions +- [ ] Read ENVIRONMENT_VARIABLES.md for secrets reference +- [ ] Understand version numbering (semantic versioning) +- [ ] Know how to trigger a release via GitHub Actions + +### ✓ Team Communication +- [ ] Informed team about new release process +- [ ] Documented who has access to GitHub secrets +- [ ] Established process for key rotation +- [ ] Set up notifications for release workflow failures + +## First Release Execution + +When ready to perform the first automated release: + +1. [ ] Ensure all checklist items above are complete +2. [ ] Go to Actions → Loxone Java release +3. [ ] Click "Run workflow" +4. [ ] Select version increment: `incrementPatch` (recommended for first run) +5. [ ] Click "Run workflow" button +6. [ ] Monitor the workflow execution +7. [ ] Verify artifacts appear in Sonatype staging repository +8. [ ] Verify artifacts are released to Maven Central (~10 minutes) +9. [ ] Verify tag is created in GitHub +10. [ ] Test downloading the artifact from Maven Central + +## Troubleshooting Common Issues + +### Issue: "Secret not found" in GitHub Actions +**Solution**: Verify all 5 secrets are configured in repository settings + +### Issue: "401 Unauthorized" when publishing +**Solution**: +- Verify OSS_USER and OSS_PASS are correct +- Check Sonatype account has access to cz.smarteon + +### Issue: "Invalid signature" or signing fails +**Solution**: +- Verify SIGNING_KEY is base64 encoded +- Verify SIGNING_PASS matches the key passphrase +- Test key import locally + +### Issue: "Tag already exists" +**Solution**: +- Tag already exists in repository +- Delete tag if needed: `git push --delete origin TAG_NAME` +- Or increment to next version + +### Issue: Staging repository won't close +**Solution**: +- Check Nexus UI: https://oss.sonatype.org/#stagingRepositories +- Look for validation errors in Activity tab +- Common issues: missing signatures, invalid POM + +## Maintenance Checklist + +Perform these tasks periodically: + +### Monthly +- [ ] Review GitHub Actions workflow runs for failures +- [ ] Check Maven Central for latest published versions +- [ ] Verify all secrets are still valid + +### Quarterly +- [ ] Rotate Sonatype password +- [ ] Review team access to secrets +- [ ] Update GitHub PAT if needed + +### Annually +- [ ] Review and update GPG key if needed +- [ ] Audit release process documentation +- [ ] Update workflow dependencies (GitHub Actions versions) + +## Support and Resources + +- **Gradle Build Issues**: Check project README.md +- **Sonatype Issues**: https://issues.sonatype.org/ +- **GPG Questions**: https://www.gnupg.org/documentation/ +- **GitHub Actions**: Check workflow run logs + +## Completion + +Once all items are checked: +- ✅ Ready to perform automated releases +- ✅ Team is informed about the new process +- ✅ Documentation is complete and accessible +- ✅ First test release can be attempted + +**Date Completed**: _______________ +**Completed By**: _______________ +**Verified By**: _______________ From c0ebf5910d89196f7effb3317541a5da2e149f69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:54:48 +0000 Subject: [PATCH 5/8] Add security warnings for GPG key handling in documentation Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- ENVIRONMENT_VARIABLES.md | 23 +++++++++++++++++++++-- RELEASE_CHECKLIST.md | 7 +++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/ENVIRONMENT_VARIABLES.md b/ENVIRONMENT_VARIABLES.md index 1e01ed5..7be804b 100644 --- a/ENVIRONMENT_VARIABLES.md +++ b/ENVIRONMENT_VARIABLES.md @@ -23,12 +23,19 @@ These must be configured in **Settings → Secrets and variables → Actions** i - **Type**: ASCII-armored GPG private key, base64 encoded - **How to obtain**: ```bash - gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0 + # WARNING: This exports your private key. Handle with extreme care! + # The output file should be immediately added to GitHub secrets and then securely deleted. + gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0 > signing-key.txt + + # After copying to GitHub secrets: + shred -u signing-key.txt # Linux - securely delete + # rm -P signing-key.txt # macOS - securely delete ``` - **Requirements**: - Must be a valid GPG/PGP key - Public key must be published to key servers - Must be base64 encoded for storage +- **Security**: Never commit this to version control or expose in logs ### 4. SIGNING_PASS - **Purpose**: Passphrase for the GPG signing key @@ -52,11 +59,23 @@ For local testing of the release and publish process: ### Environment Variables ```bash +# WARNING: Setting environment variables in shell exposes them in: +# - Shell history (use `set +o history` to disable) +# - Process lists (visible to other users via ps/top) +# - Parent shell environment +# +# For security, consider: +# 1. Use a .env file that's in .gitignore +# 2. Clear shell history after: history -c +# 3. Use a secure terminal session +# 4. Unset variables after use: unset OSS_USER OSS_PASS SIGNING_KEY SIGNING_PASS + # Sonatype credentials export OSS_USER="your-jira-username" export OSS_PASS="your-jira-password" -# GPG signing +# GPG signing - safer alternative: read from secure file +# export SIGNING_KEY="$(cat /secure/path/signing-key.txt)" export SIGNING_KEY="$(gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0)" export SIGNING_PASS="your-gpg-passphrase" ``` diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md index 2d125dc..4e30519 100644 --- a/RELEASE_CHECKLIST.md +++ b/RELEASE_CHECKLIST.md @@ -21,7 +21,14 @@ Use this checklist to ensure all requirements are met before using the automated ``` - [ ] Exported private key to base64 ```bash + # WARNING: This creates a file containing your private key. + # Store it securely and delete after adding to GitHub secrets. + # Never commit this file to version control. gpg --export-secret-keys -a KEY_ID | base64 -w0 > signing-key.txt + + # After copying to GitHub secrets, securely delete the file: + # shred -u signing-key.txt # On Linux + # rm -P signing-key.txt # On macOS ``` - [ ] Published public key to key servers ```bash From 58c7d9990c8cecfa4d2433b0c5d7328cd50a202c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:55:57 +0000 Subject: [PATCH 6/8] Improve security recommendations for environment variable handling Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- ENVIRONMENT_VARIABLES.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ENVIRONMENT_VARIABLES.md b/ENVIRONMENT_VARIABLES.md index 7be804b..6048672 100644 --- a/ENVIRONMENT_VARIABLES.md +++ b/ENVIRONMENT_VARIABLES.md @@ -60,24 +60,28 @@ For local testing of the release and publish process: ```bash # WARNING: Setting environment variables in shell exposes them in: -# - Shell history (use `set +o history` to disable) +# - Shell history (use `set +o history` BEFORE entering commands) # - Process lists (visible to other users via ps/top) # - Parent shell environment # -# For security, consider: -# 1. Use a .env file that's in .gitignore -# 2. Clear shell history after: history -c -# 3. Use a secure terminal session -# 4. Unset variables after use: unset OSS_USER OSS_PASS SIGNING_KEY SIGNING_PASS +# Recommended secure approach: +# 1. Start a new shell session with history disabled: bash --noprofile --norc -c "set +o history; bash" +# 2. Or disable history before entering commands: set +o history +# 3. Use a .env file that's in .gitignore +# 4. Clear shell history after: history -c && history -w +# 5. Unset variables after use: unset OSS_USER OSS_PASS SIGNING_KEY SIGNING_PASS # Sonatype credentials export OSS_USER="your-jira-username" export OSS_PASS="your-jira-password" -# GPG signing - safer alternative: read from secure file -# export SIGNING_KEY="$(cat /secure/path/signing-key.txt)" -export SIGNING_KEY="$(gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0)" +# GPP signing - RECOMMENDED: read from secure file instead of inline command +# First, create the key file (see RELEASE_CHECKLIST.md for secure creation) +export SIGNING_KEY="$(cat /secure/path/signing-key.txt)" export SIGNING_PASS="your-gpg-passphrase" + +# ALTERNATIVE (less secure - exposes key in process list during execution): +# export SIGNING_KEY="$(gpg --export-secret-keys -a YOUR_KEY_ID | base64 -w0)" ``` ### Alternative: Gradle Properties From 5388482e2bb52fc8a4d58797da3937fa805d2daf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 14:57:43 +0000 Subject: [PATCH 7/8] Fix typo in ENVIRONMENT_VARIABLES.md (GPP -> GPG) Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- ENVIRONMENT_VARIABLES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ENVIRONMENT_VARIABLES.md b/ENVIRONMENT_VARIABLES.md index 6048672..4b45141 100644 --- a/ENVIRONMENT_VARIABLES.md +++ b/ENVIRONMENT_VARIABLES.md @@ -75,7 +75,7 @@ For local testing of the release and publish process: export OSS_USER="your-jira-username" export OSS_PASS="your-jira-password" -# GPP signing - RECOMMENDED: read from secure file instead of inline command +# GPG signing - RECOMMENDED: read from secure file instead of inline command # First, create the key file (see RELEASE_CHECKLIST.md for secure creation) export SIGNING_KEY="$(cat /secure/path/signing-key.txt)" export SIGNING_PASS="your-gpg-passphrase" From ad2aed74125391051de76b3808010c83d587b31f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 15:30:05 +0000 Subject: [PATCH 8/8] Remove RELEASE_CHECKLIST.md as requested Co-authored-by: jimirocks <5468417+jimirocks@users.noreply.github.com> --- ENVIRONMENT_VARIABLES.md | 2 +- RELEASE_CHECKLIST.md | 195 --------------------------------------- 2 files changed, 1 insertion(+), 196 deletions(-) delete mode 100644 RELEASE_CHECKLIST.md diff --git a/ENVIRONMENT_VARIABLES.md b/ENVIRONMENT_VARIABLES.md index 4b45141..790a438 100644 --- a/ENVIRONMENT_VARIABLES.md +++ b/ENVIRONMENT_VARIABLES.md @@ -76,7 +76,7 @@ export OSS_USER="your-jira-username" export OSS_PASS="your-jira-password" # GPG signing - RECOMMENDED: read from secure file instead of inline command -# First, create the key file (see RELEASE_CHECKLIST.md for secure creation) +# First, create the key file (see RELEASE_SETUP.md for instructions) export SIGNING_KEY="$(cat /secure/path/signing-key.txt)" export SIGNING_PASS="your-gpg-passphrase" diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md deleted file mode 100644 index 4e30519..0000000 --- a/RELEASE_CHECKLIST.md +++ /dev/null @@ -1,195 +0,0 @@ -# Release Setup Checklist - -Use this checklist to ensure all requirements are met before using the automated release workflow. - -## Pre-requisites Checklist - -### ✓ Sonatype Account Setup -- [ ] Created Sonatype JIRA account at https://issues.sonatype.org/ -- [ ] Requested access to `cz.smarteon` group ID (or verified existing access) -- [ ] Received approval from Sonatype (wait time: 1-2 business days) -- [ ] Can log in to https://oss.sonatype.org/ with JIRA credentials - -### ✓ GPG Key Setup -- [ ] Generated GPG key (or have an existing one) - ```bash - gpg --gen-key - ``` -- [ ] Recorded key ID - ```bash - gpg --list-secret-keys --keyid-format=long - ``` -- [ ] Exported private key to base64 - ```bash - # WARNING: This creates a file containing your private key. - # Store it securely and delete after adding to GitHub secrets. - # Never commit this file to version control. - gpg --export-secret-keys -a KEY_ID | base64 -w0 > signing-key.txt - - # After copying to GitHub secrets, securely delete the file: - # shred -u signing-key.txt # On Linux - # rm -P signing-key.txt # On macOS - ``` -- [ ] Published public key to key servers - ```bash - gpg --keyserver keyserver.ubuntu.com --send-keys KEY_ID - gpg --keyserver keys.openpgp.org --send-keys KEY_ID - ``` -- [ ] Saved passphrase securely - -### ✓ GitHub Setup -- [ ] Have admin access to GitHub repository -- [ ] Created GitHub Personal Access Token with `repo` scope -- [ ] Configured all required GitHub Secrets: - - [ ] `OSS_USER` - Sonatype username - - [ ] `OSS_PASS` - Sonatype password - - [ ] `SIGNING_KEY` - GPG key (base64 encoded) - - [ ] `SIGNING_PASS` - GPG key passphrase - - [ ] `SMARTEON_GIT_TOKEN` - GitHub PAT - -## Verification Checklist - -### ✓ Local Build Verification -- [ ] Project builds successfully - ```bash - ./gradlew clean build - ``` -- [ ] Current version is detected correctly - ```bash - ./gradlew currentVersion - ``` -- [ ] Can publish to local Maven repository - ```bash - ./gradlew publishToMavenLocal - ``` - -### ✓ Publishing Configuration Verification -- [ ] Sonatype tasks are available (with OSS_USER and OSS_PASS set) - ```bash - OSS_USER=test OSS_PASS=test ./gradlew tasks --group=publishing - ``` -- [ ] Release tasks are available - ```bash - ./gradlew tasks --group=release - ``` - -### ✓ Signing Configuration Verification -- [ ] Can import signing key locally - ```bash - echo "$SIGNING_KEY" | base64 -d | gpg --import - ``` -- [ ] Signing works (on non-SNAPSHOT version) - ```bash - # Create a test tag first - git tag -a test-signing-1.0.0 -m "Test" - SIGNING_KEY="..." SIGNING_PASS="..." ./gradlew build - git tag -d test-signing-1.0.0 - ``` - -## GitHub Actions Workflow Verification - -### ✓ Workflow File -- [ ] Workflow file exists: `.github/workflows/loxone-java-release.yml` -- [ ] YAML syntax is valid -- [ ] All required secrets are referenced in the workflow -- [ ] Workflow is visible in Actions tab - -### ✓ First Release Test -- [ ] Navigate to Actions → Loxone Java release -- [ ] Workflow is available and can be manually triggered -- [ ] All required secrets are configured (workflow won't fail with missing secrets) - -## Post-Setup Checklist - -### ✓ Documentation -- [ ] Read RELEASE_SETUP.md for detailed instructions -- [ ] Read ENVIRONMENT_VARIABLES.md for secrets reference -- [ ] Understand version numbering (semantic versioning) -- [ ] Know how to trigger a release via GitHub Actions - -### ✓ Team Communication -- [ ] Informed team about new release process -- [ ] Documented who has access to GitHub secrets -- [ ] Established process for key rotation -- [ ] Set up notifications for release workflow failures - -## First Release Execution - -When ready to perform the first automated release: - -1. [ ] Ensure all checklist items above are complete -2. [ ] Go to Actions → Loxone Java release -3. [ ] Click "Run workflow" -4. [ ] Select version increment: `incrementPatch` (recommended for first run) -5. [ ] Click "Run workflow" button -6. [ ] Monitor the workflow execution -7. [ ] Verify artifacts appear in Sonatype staging repository -8. [ ] Verify artifacts are released to Maven Central (~10 minutes) -9. [ ] Verify tag is created in GitHub -10. [ ] Test downloading the artifact from Maven Central - -## Troubleshooting Common Issues - -### Issue: "Secret not found" in GitHub Actions -**Solution**: Verify all 5 secrets are configured in repository settings - -### Issue: "401 Unauthorized" when publishing -**Solution**: -- Verify OSS_USER and OSS_PASS are correct -- Check Sonatype account has access to cz.smarteon - -### Issue: "Invalid signature" or signing fails -**Solution**: -- Verify SIGNING_KEY is base64 encoded -- Verify SIGNING_PASS matches the key passphrase -- Test key import locally - -### Issue: "Tag already exists" -**Solution**: -- Tag already exists in repository -- Delete tag if needed: `git push --delete origin TAG_NAME` -- Or increment to next version - -### Issue: Staging repository won't close -**Solution**: -- Check Nexus UI: https://oss.sonatype.org/#stagingRepositories -- Look for validation errors in Activity tab -- Common issues: missing signatures, invalid POM - -## Maintenance Checklist - -Perform these tasks periodically: - -### Monthly -- [ ] Review GitHub Actions workflow runs for failures -- [ ] Check Maven Central for latest published versions -- [ ] Verify all secrets are still valid - -### Quarterly -- [ ] Rotate Sonatype password -- [ ] Review team access to secrets -- [ ] Update GitHub PAT if needed - -### Annually -- [ ] Review and update GPG key if needed -- [ ] Audit release process documentation -- [ ] Update workflow dependencies (GitHub Actions versions) - -## Support and Resources - -- **Gradle Build Issues**: Check project README.md -- **Sonatype Issues**: https://issues.sonatype.org/ -- **GPG Questions**: https://www.gnupg.org/documentation/ -- **GitHub Actions**: Check workflow run logs - -## Completion - -Once all items are checked: -- ✅ Ready to perform automated releases -- ✅ Team is informed about the new process -- ✅ Documentation is complete and accessible -- ✅ First test release can be attempted - -**Date Completed**: _______________ -**Completed By**: _______________ -**Verified By**: _______________