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
88 changes: 88 additions & 0 deletions .github/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Release flow

Short guide for cutting a release.

## Branching model (GitHub Flow)

One long-lived branch — `master` (or `main`, whichever the repo is on).
Feature work lives on `feature/**`, `task/**`, `fix/**` branches that are
merged back via pull requests. `develop` is kept for staging pre-release
work before a tag.

- `master` — always deployable, tagged on every release
- `develop` — integration branch for the next release
- `feature/*`, `fix/*`, `task/*` — short-lived, merged into `develop` (or
straight to `master` for hotfixes) through a PR

Both `master` and `develop` are protected by the [Build workflow](.github/workflows/build.yml)
— every push and pull request runs `./gradlew build shadowJar` and publishes
the JAR as an artifact, plus a JUnit test report.

## Cutting a release

1. **Bump the version.** Edit `build.gradle`:
```gradle
version '1.19.0'
```
Commit with a message like `chore(release): 1.19.0`. The [Release workflow](.github/workflows/release.yml)
compares the tag against this field and aborts if they disagree.

2. **Merge `develop` → `master`** via PR (or push directly if you are the
sole maintainer).

3. **Create a release on GitHub.** Either through the web UI or the CLI:
```bash
gh release create 1.19.0 \
--title "AbstractMenus 1.19.0" \
--notes-file CHANGELOG.md
```
Tag format: either `1.19.0` or `v1.19.0` — the workflow strips the
leading `v` before comparing.

4. **Release workflow fires automatically** (`on: release: types:
[published]`), rebuilds the shaded JAR from the tagged commit, verifies
the version matches, and uploads `AbstractMenus-1.19.0.jar` as a release
asset.

5. Done. The release page now has the JAR; users pull it straight from
there.

## Re-attaching a JAR to an existing release

If the build failed or the asset was deleted, trigger the Release workflow
manually:

- GitHub UI → Actions → Release → Run workflow → supply the existing tag
(e.g. `1.18.0`).
- Or via CLI: `gh workflow run release.yml -f tag=1.18.0`.

The workflow will rebuild from that tag's commit and re-upload. If an
asset with the same name already exists, `softprops/action-gh-release`
overwrites it.

## Hotfix flow

For a patch release that doesn't touch `develop`:

1. Branch from `master`: `git checkout -b fix/bad-null-deref master`
2. Fix, PR, merge.
3. Bump `version` on `master`: `1.19.0` → `1.19.1`. Commit.
4. Cut the GitHub release as in the main flow. The workflow picks up the
new version automatically.
5. Cherry-pick the fix commit back to `develop` so it isn't lost.

## Pre-releases

GitHub supports a "Set as a pre-release" checkbox on the release form.
That marks the tag as a pre-release (e.g. `1.19.0-rc1`, `1.19.0-beta.2`)
— the Release workflow still builds and attaches the JAR, users just see
the pre-release badge on the release page.

For a pre-release version bump, use Gradle-compatible syntax:

```gradle
version '1.19.0-rc1'
```

The workflow's version-match check strips only a leading `v`, so
`1.19.0-rc1` and `v1.19.0-rc1` both work.
62 changes: 62 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Build

on:
push:
branches:
- master
- main
- develop
- 'feature/**'
- 'task/**'
- 'fix/**'
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
name: Build + test on JDK 21
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-home-cache-cleanup: true

- name: Build + tests
run: ./gradlew --no-daemon build shadowJar

- name: Publish test report
if: always()
uses: mikepenz/action-junit-report@v5
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
fail_on_failure: true
require_tests: false

- name: Resolve JAR path
id: jar
run: echo "path=$(ls build/libs/AbstractMenus-*.jar | head -n1)" >> "$GITHUB_OUTPUT"

- name: Upload shaded JAR
uses: actions/upload-artifact@v4
with:
name: AbstractMenus-jar
path: ${{ steps.jar.outputs.path }}
if-no-files-found: error
retention-days: 14
65 changes: 65 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Release

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Existing release tag to attach a rebuilt JAR to'
required: true
type: string

permissions:
contents: write

jobs:
attach-jar:
name: Build + attach JAR to release
runs-on: ubuntu-latest
steps:
- name: Checkout (release tag)
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag }}
fetch-depth: 0

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Verify tag matches project version
run: |
TAG="${{ github.event.release.tag_name || inputs.tag }}"
# Strip a leading 'v' if present (v1.18.0 → 1.18.0)
TAG_VERSION="${TAG#v}"
GRADLE_VERSION=$(grep -E "^version\s+['\"]" build.gradle | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
echo "release tag: $TAG"
echo "tag version: $TAG_VERSION"
echo "gradle version: $GRADLE_VERSION"
if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
echo "::error::Release tag version ($TAG_VERSION) does not match build.gradle version ($GRADLE_VERSION). Bump build.gradle before tagging, or retag."
exit 1
fi

- name: Build shaded JAR (skip tests — already run in Build workflow on the tagged commit)
run: ./gradlew --no-daemon shadowJar

- name: Resolve JAR path
id: jar
run: |
JAR=$(ls build/libs/AbstractMenus-*.jar | head -n1)
echo "path=$JAR" >> "$GITHUB_OUTPUT"
echo "name=$(basename "$JAR")" >> "$GITHUB_OUTPUT"

- name: Attach JAR to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.release.tag_name || inputs.tag }}
files: ${{ steps.jar.outputs.path }}
fail_on_unmatched_files: true
Loading