diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08247c8..38b217c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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) 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 } @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c806b..08051ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md index 4209891..ab1ae2f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 23c0782..86e6962 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -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 @@ -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 @@ -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. diff --git a/gradle.properties b/gradle.properties index 4a0f39b..079f661 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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