Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 0 additions & 55 deletions .github/release-drafter.yml

This file was deleted.

48 changes: 10 additions & 38 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
publish:
Expand Down Expand Up @@ -48,48 +49,19 @@ jobs:
run: mvn --batch-mode deploy -DaltDeploymentRepository=github::default::https://maven.pkg.github.com/${{ github.repository }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release:
needs: publish
if: github.event_name == 'push'
needs:
- publish
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 5
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Fetch annotated tag message
run: git fetch --force origin "refs/tags/${GITHUB_REF_NAME}:refs/tags/${GITHUB_REF_NAME}"
- name: Publish GitHub Release
- name: Create GitHub release if missing
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_REF="refs/tags/${GITHUB_REF_NAME}"
HIGHLIGHTS=""
if [ "$(git cat-file -t "$TAG_REF" 2>/dev/null || true)" = "tag" ]; then
HIGHLIGHTS=$(git for-each-ref "$TAG_REF" --format='%(contents)' | sed '/^-----BEGIN PGP SIGNATURE-----/,$d')
if [ -z "$(printf '%s' "$HIGHLIGHTS" | tr -d '[:space:]')" ]; then
HIGHLIGHTS=""
fi
fi

RELEASE_ID=$(gh api "repos/${GITHUB_REPOSITORY}/releases?per_page=100" --jq 'map(select(.draft))[0].id // empty')
if [ -n "$RELEASE_ID" ]; then
BODY=$(gh api "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" --jq '.body // ""')
if [ -n "$HIGHLIGHTS" ]; then
BODY=$(printf '## Highlights\n\n%s\n\n%s' "$HIGHLIGHTS" "$BODY")
fi
jq -n \
--arg tag_name "$GITHUB_REF_NAME" \
--arg name "$GITHUB_REF_NAME" \
--arg body "$BODY" \
'{tag_name: $tag_name, name: $name, body: $body, draft: false, make_latest: "true"}' \
> /tmp/release.json
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" --input /tmp/release.json
elif [ -n "$HIGHLIGHTS" ]; then
gh release create "$GITHUB_REF_NAME" --verify-tag --generate-notes \
--title "$GITHUB_REF_NAME" --latest \
--notes "$(printf '## Highlights\n\n%s' "$HIGHLIGHTS")"
else
gh release create "$GITHUB_REF_NAME" --verify-tag --generate-notes \
--title "$GITHUB_REF_NAME" --latest
fi
TAG: ${{ github.ref_name }}
# Manual `git push origin v*` tags have no release yet; release-please
# dispatch never reaches this job (it only runs on push).
run: gh release view "$TAG" > /dev/null 2>&1 || gh release create "$TAG" --generate-notes --verify-tag
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
name: Release Drafter
name: Release Please

on:
push:
branches:
- master
workflow_dispatch: {}
branches: [master]

permissions:
contents: read
actions: write
contents: write
pull-requests: write

jobs:
update_release_draft:
release-please:
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
id: release
with:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Trigger publish workflow
if: ${{ steps.release.outputs.release_created }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release.outputs.tag_name }}
run: gh workflow run publish.yml --repo "$GITHUB_REPOSITORY" --ref "$TAG"

openapi-diff:
if: ${{ needs.release-please.outputs.release_created }}
needs: release-please
runs-on: blacksmith-2vcpu-ubuntu-2404
permissions:
contents: write
pull-requests: write
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout Repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
fetch-depth: 0

- name: Install oasdiff
Expand All @@ -30,8 +50,10 @@ jobs:
tar -xzf /tmp/oasdiff.tar.gz -C /usr/local/bin oasdiff

- name: Generate OpenAPI diff since last release
env:
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
LATEST_TAG=$(git tag -l 'v*' --sort=-v:refname | grep -v "^${GITHUB_REF_NAME}$" | head -1)
LATEST_TAG=$(git tag -l 'v*' --sort=-v:refname | grep -v "^${TAG}$" | head -1)
if [ -n "$LATEST_TAG" ] && git show "${LATEST_TAG}:api/openapi.yaml" > /tmp/base-openapi.yaml 2>/dev/null; then
CHANGELOG=$(oasdiff changelog --format markdown /tmp/base-openapi.yaml api/openapi.yaml || true)
if [ -z "$CHANGELOG" ] || echo "$CHANGELOG" | grep -q "No changes detected"; then
Expand Down Expand Up @@ -67,27 +89,29 @@ jobs:
echo "$SUMMARY" > /tmp/openapi-summary.txt
echo "${CHANGELOG:-}" > /tmp/openapi-diff.md

- id: release_drafter
uses: release-drafter/release-drafter@978bb0b30c5ee8cdd332ced253758157150d43f0 # v7.10.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Inject OpenAPI diff into release body
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
TAG: ${{ needs.release-please.outputs.tag_name }}
with:
script: |
const fs = require('fs');
const releaseId = ${{ steps.release_drafter.outputs.id }};
const hasChanges = '${{ env.OPENAPI_HAS_CHANGES }}' === 'true';
const summary = fs.readFileSync('/tmp/openapi-summary.txt', 'utf8').trim();

const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: process.env.TAG,
});

let inlineContent = summary;

if (hasChanges) {
const { data: assets } = await github.rest.repos.listReleaseAssets({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
release_id: release.id,
});
const existing = assets.find(a => a.name === 'openapi-diff.md');
if (existing) {
Expand All @@ -102,30 +126,24 @@ jobs:
const { data: asset } = await github.rest.repos.uploadReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
release_id: release.id,
name: 'openapi-diff.md',
data: diff,
});

inlineContent = summary.replace('<!-- ASSET_URL -->', asset.browser_download_url);
}

const { data: release } = await github.rest.repos.getRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
});

const bodyLimit = 125000;
let newBody = release.body.replace('<!-- OPENAPI_DIFF -->', inlineContent);
let newBody = (release.body || '') + '\n\n## API changes\n\n' + inlineContent;
if (newBody.length > bodyLimit) {
const fallback = inlineContent.replace(/<details>[\s\S]*<\/details>/, '_Breaking and potentially breaking changes omitted — see attached `openapi-diff.md` for details._');
newBody = release.body.replace('<!-- OPENAPI_DIFF -->', fallback);
newBody = (release.body || '') + '\n\n## API changes\n\n' + fallback;
}

await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
release_id: release.id,
body: newBody,
});
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "3.0.0"
}
52 changes: 14 additions & 38 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,65 +160,41 @@ The project uses **Spotless** with **Google Java Format (AOSP style)**:
GitHub Actions workflows:
- **test.yml** - Runs tests on all branches using JDK 17
- **maven.yml** - Maven-based build pipeline
- **publish.yml** - Publishes artifacts to Maven Central and GitHub Packages, then publishes the most recent Release Drafter draft under the pushed tag (triggered on tag push)
- **release-please.yml** - Opens release PRs on merge to master; merging one tags the release and dispatches publish.yml
- **publish.yml** - Publishes artifacts to Maven Central and GitHub Packages and creates the GitHub release (dispatched by release-please, or triggered on a manual tag push)

## Publishing & Releases

See [PUBLISHING.md](PUBLISHING.md) for detailed instructions on publishing releases to Maven Central and GitHub Packages.

### Release Process

The release process uses Makefile targets to automate version bumping and tagging:
Releases are driven by **Release Please** on every merge to `master`
(conventional commits determine the bump). Merging the release PR tags
`vX.Y.Z`, creates the GitHub release, and dispatches `publish.yml` to push
artifacts to Maven Central and GitHub Packages.

```bash
# Check current version
make version

# Bump version (updates pom.xml and build.gradle, creates tag)
make bump-patch # 0.0.1 -> 0.0.2
make bump-minor # 0.0.1 -> 0.1.0
make bump-major # 0.0.1 -> 1.0.0

# Push the tag to trigger automated workflows
make push-tag
```
**Manual fallback (full release):**

Review the Release Drafter draft and its OpenAPI diff before tagging. Label merged pull requests `breaking` for major versions and `enhancement` for minor versions. The tag publishes the most recent draft regardless of its current tag, renames it to the pushed tag, and prepends the annotated tag message under “Highlights”. For custom highlights, use an annotated tag such as `git tag -a vX.Y.Z -m "<highlights>"`.

**Automated workflow (triggered by `make push-tag`):**
1. `make bump-patch` (or minor/major) - Updates version in both pom.xml and build.gradle, commits, and creates tag locally
2. `make push-tag` - Pushes the tag to GitHub, which automatically triggers **publish.yml**:
- Runs tests
- Signs artifacts with GPG
- Publishes to Maven Central
- Publishes to GitHub Packages
- Publishes the most recent Release Drafter draft, renamed to the pushed tag, with the annotated tag message under “Highlights”; or creates a release with GitHub-generated notes if no draft exists (only if all publishing succeeds)

**One-command releases:**
```bash
# These combine bump + push-tag (fully automated!)
make release-patch # Bump patch and push tag → triggers publish + release
make release-minor # Bump minor and push tag → triggers publish + release
make release-major # Bump major and push tag → triggers publish + release
make release-patch # Bump patch, tag, push → publish + release
make release-minor
make release-major
```

**What happens automatically:**
- Runs all tests
- Signs artifacts with GPG
- Deploys to Maven Central Portal (auto-publishes)
- Deploys to GitHub Packages
- Publishes the most recent Release Drafter draft, renamed to the pushed tag, with the annotated tag message under “Highlights”; or creates a release with GitHub-generated notes if no draft exists
- Creates a GitHub release with generated notes (the OpenAPI diff job appends
the API changelog to it)
- Available in Maven Central within 30 minutes

**No manual steps needed!** Just run `make release-patch` and everything is automated.

### Version Management

Version is tracked in two files (automatically updated by Makefile):
- `pom.xml` - Line 7: `<version>0.0.1</version>`
- `build.gradle` - Line 7: `version = '0.0.1'`

Git tags follow semver format: `v0.0.1`, `v0.1.0`, `v1.0.0`
The version is tracked in `pom.xml` and `build.gradle` (release-please bumps
both via `extra-files`). Git tags follow semver: `v0.0.1`, `v0.1.0`.

## Important Notes

Expand Down
13 changes: 9 additions & 4 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ rm gpg-key.txt

## Publishing Process

The publish workflow runs automatically when you push a version tag. Release Drafter maintains a draft with merged pull request notes and an OpenAPI diff; review the draft before tagging. Use the `breaking` and `enhancement` labels for major and minor version resolution. The tag publishes the most recent draft regardless of its current tag, renames it to the pushed tag, and prepends the annotated tag message under “Highlights”. Use an annotated tag such as `git tag -a vX.Y.Z -m "<highlights>"` when providing release highlights.
Releases are driven by **Release Please**: every merge to `master` updates a
release PR (conventional commits determine the version bump). Merging the
release PR tags `vX.Y.Z`, creates the GitHub release, and dispatches
`publish.yml`. The OpenAPI diff job appends the API changelog to the release.

The manual tag-push path below still works as a fallback.

### Step 1: Update Version and Create Tag

Expand All @@ -108,7 +113,7 @@ make push-tag
3. ✅ Deploy to Maven Central Portal
4. ✅ Auto-publish to Maven Central (no manual UI steps needed!)
5. ✅ Deploy to GitHub Packages
6. ✅ Publish the most recent Release Drafter draft, renamed to the pushed tag, with the annotated tag message under “Highlights”; or create a release with GitHub-generated notes if no draft exists
6. ✅ Create a GitHub release with generated notes if none exists

### One-Command Release (Recommended)

Expand All @@ -125,11 +130,11 @@ The **publish.yml** workflow runs automatically on tag push and performs these s
1. Runs all tests
2. Publishes to Maven Central (with GPG signing)
3. Publishes to GitHub Packages
4. Publishes the most recent Release Drafter draft, renamed to the pushed tag, with the annotated tag message under “Highlights”; or creates a release with generated notes if no draft exists (only if steps 1-3 succeed)
4. Creates a GitHub release with generated notes if none exists (only if steps 1-3 succeed); the OpenAPI diff job appends the API changelog

Check the Actions tab: https://github.com/rootlyhq/rootly-java/actions

**Important:** The Release Drafter draft is published only after successful publishing to both registries.
**Important:** The GitHub release is created only after successful publishing to both registries.

### Step 4: Verify Publication

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apply plugin: 'java'
apply plugin: 'com.diffplug.spotless'

group = 'com.rootly.client'
version = '3.0.0'
version = '3.0.0' // x-release-please-version

buildscript {
repositories {
Expand Down
Loading
Loading