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
64 changes: 49 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
# Creates a GitHub Release whenever a version tag (v*) is pushed.
# The release body is the matching section from CHANGELOG.md and the
# built plugin jar is attached as an asset.
# Creates a GitHub Release when a new plugin_version lands on master.
# The tag (v<plugin_version>) is created by the release itself, so releases
# are published in the repository the commit is merged into, not in the fork
# the pull request came from. Forks never release. The release body is the
# matching section from CHANGELOG.md and the built plugin jar is attached.

name: release
on:
push:
tags:
- 'v*'
branches:
- master
paths:
- gradle.properties
workflow_dispatch:

permissions:
contents: write

concurrency:
group: release
cancel-in-progress: false

jobs:
release:
if: ${{ !github.event.repository.fork }}
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@v6
- name: setup jdk
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'microsoft'
- name: make gradle wrapper executable
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: read plugin version
id: version
run: |
VERSION="$(sed -n 's/^plugin_version=//p' gradle.properties | tr -d '[:space:]')"
if [ -z "$VERSION" ]; then
echo "::error::plugin_version is missing from gradle.properties"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if git ls-remote --exit-code --tags origin "refs/tags/v$VERSION" > /dev/null; then
echo "Release v$VERSION already exists, nothing to do."
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: extract changelog section
if: steps.version.outputs.exists == 'false'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
VERSION="${GITHUB_REF_NAME#v}"
awk -v ver="$VERSION" '
$0 == "## Version " ver { found = 1; next }
found && /^## / { exit }
Expand All @@ -38,8 +56,24 @@ jobs:
echo "::error::No changelog section found for version $VERSION in CHANGELOG.md"
exit 1
fi
- name: setup jdk
if: steps.version.outputs.exists == 'false'
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'microsoft'
- name: make gradle wrapper executable
if: steps.version.outputs.exists == 'false'
run: chmod +x ./gradlew
- name: build
if: steps.version.outputs.exists == 'false'
run: ./gradlew build
- name: create release
if: steps.version.outputs.exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.version.outputs.version }}
target_commitish: ${{ github.sha }}
name: v${{ steps.version.outputs.version }}
body_path: RELEASE_NOTES.md
files: build/libs/*.jar
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Unreleased

## Version 0.1.1

### Technical Details

#### Misc

+ Releases are now published by the repository a version bump is merged into, instead of by pushing a tag.
+ The release workflow creates the `vX.Y.Z` tag itself, skips forks, and can be re-run by hand.

## Version 0.1.0

### New Features
Expand Down
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,12 @@ Extend `Menu` (or `PagedMenu` for lists) and implement `build()`, which runs on
flows into the jar filename and, through `processResources`, into `plugin.yml` (`version: ${projectVersion}`) — never
hardcode a version in `plugin.yml` or `build.gradle.kts`.
- [.github/workflows/build.yml](.github/workflows/build.yml) builds every push and pull request.
- Releases are made by tagging `vX.Y.Z`: [.github/workflows/release.yml](.github/workflows/release.yml) builds the jar,
uses the matching `## Version X.Y.Z` section of `CHANGELOG.md` as the release notes, and attaches the jar. See
[docs/RELEASING.md](docs/RELEASING.md). **Never tag or push tags unless explicitly asked** — pushing a tag publishes a
release.
- Releases are published when a commit that changes `plugin_version` lands on `master`:
[.github/workflows/release.yml](.github/workflows/release.yml) builds the jar, uses the matching
`## Version X.Y.Z` section of `CHANGELOG.md` as the release notes, creates the `vX.Y.Z` tag, and attaches the jar.
It skips forks, so the release appears in the organization repository the pull request is merged into. It does
nothing if the tag already exists. See [docs/RELEASING.md](docs/RELEASING.md). **Never create tags, and never merge
a version bump into the organization repository, unless explicitly asked** — that publishes a release.

## Commit Convention

Expand Down
58 changes: 32 additions & 26 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# TownyMenu - Changelog & Releasing

This guide covers how to maintain [CHANGELOG.md](../CHANGELOG.md) during development and how to publish a release on
GitHub. The release itself is automated by [.github/workflows/release.yml](../.github/workflows/release.yml) — pushing a
version tag builds the plugin, extracts the matching changelog section, and creates the GitHub Release with the jar
attached. Every push and pull request is also built by [.github/workflows/build.yml](../.github/workflows/build.yml).
GitHub. The release itself is automated by [.github/workflows/release.yml](../.github/workflows/release.yml) — when a
commit that changes `plugin_version` lands on `master`, the workflow builds the plugin, extracts the matching changelog
section, and creates the `vX.Y.Z` tag and GitHub Release with the jar attached. Every push and pull request is also built
by [.github/workflows/build.yml](../.github/workflows/build.yml).

Releases are created in the repository the commit is merged into. Development happens on a fork, and the release
workflow skips forks entirely, so merging a version-bump pull request into the organization repository publishes the
release there — not in the fork it came from.

## Changelog format

Expand Down Expand Up @@ -53,7 +58,7 @@ Rules of thumb:
- Write entries for players and server owners, not developers: "Added a town bank menu", not
"Refactored TownMenuGUI". Developer-facing changes go under `Technical Details`.
- With outside contributors, append attribution like SkyHanni does:
`+ Added X. - Name (https://github.com/Trilleo/TownyMenu/pull/123)`
`+ Added X. - Name (https://github.com/TriBinary/TownyMenu/pull/123)`

## Versioning

Expand Down Expand Up @@ -87,41 +92,42 @@ Example: releasing version `1.1.0`.
./gradlew build
```

4. **Commit and tag** — the tag must be the version prefixed with `v`:
4. **Commit and open a pull request** — commit the bump on your fork and open a pull request against the organization
repository:

```
git commit -am "Update: Plugin version 1.1.0 release"
git tag v1.1.0
git push
```

5. **Push** — this is the publish step; the release workflow fires on the tag:
Do **not** create the tag yourself — the workflow creates `v1.1.0` when it publishes the release.

```
git push && git push --tags
```
5. **Merge** — merging the pull request into `master` is the publish step; the release workflow fires because
`gradle.properties` changed.

6. **Check the result** — the `release` workflow under the repo's *Actions* tab builds the jar and creates the GitHub
Release. Verify the release page shows the changelog text and has `TownyMenu-1.1.0.jar` attached.
6. **Check the result** — the `release` workflow under the organization repo's *Actions* tab builds the jar and creates
the GitHub Release. Verify the release page shows the changelog text and has `TownyMenu-1.1.0.jar` attached.

> **Never tag or push tags unless you have been explicitly asked to.** Pushing a tag publishes a release.
> **Never merge a version bump into the organization repository unless you have been explicitly asked to.** Merging it
> publishes a release.

## How the automation matches things up

- The workflow strips the `v` from the tag (`v1.1.0` → `1.1.0`) and extracts everything between `## Version 1.1.0` and
the next `## ` heading in `CHANGELOG.md`. That text becomes the release body.
- The workflow runs on pushes to `master` that touch `gradle.properties`, and can be started by hand from the *Actions*
tab (*Run workflow*). It never runs its release steps in a fork.
- It reads `plugin_version` (e.g. `1.1.0`). If the tag `v1.1.0` already exists, it does nothing — changing other
properties such as `towny_version` never re-releases.
- Otherwise it extracts everything between `## Version 1.1.0` and the next `## ` heading in `CHANGELOG.md`. That text
becomes the release body.
- **If no matching section exists, the workflow fails** — a release cannot ship with an empty changelog. Fix the heading
(exact match: `## Version 1.1.0`) and re-run the workflow, or delete and re-push the tag.
- The plugin jar from `build/libs/` is attached.
(exact match: `## Version 1.1.0`) and re-run the workflow.
- The plugin jar from `build/libs/` is attached, and the `v1.1.0` tag is created on the merged commit.

## Fixing a botched release

- **Wrong changelog / missing section**: fix `CHANGELOG.md`, commit, then move the tag and re-push it:

```
git tag -f v1.1.0
git push -f origin v1.1.0
```

Delete the draft/failed release on GitHub first if one was created.
- **Wrong changelog / missing section**: if the workflow failed, fix `CHANGELOG.md` through another pull request, then
start the `release` workflow by hand (*Actions* → *release* → *Run workflow*). If a release was already published with
the wrong text, edit the release notes on GitHub.

- **Wrong version in the jar**: you tagged before bumping `plugin_version`. Bump it, commit, move the tag as above.
- **Wrong version in the jar / release on the wrong commit**: delete the release and its tag on the organization
repository (the *Releases* page offers both), fix things through a pull request, then run the workflow by hand.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
kotlin.code.style=official

# Plugin Properties
plugin_version=0.1.0
plugin_version=0.1.1

# Dependency Versions
towny_version=0.103.2.7