Stable Release #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Stable Release | |
| # Fires when a vX.Y.Z tag is pushed, or can be triggered | |
| # manually with a tag. | |
| on: | |
| push: | |
| tags: | |
| - 'v[0-9]*.[0-9]*.[0-9]*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to release (e.g. v1.4.0)' | |
| required: true | |
| jobs: | |
| release: | |
| name: Stable Release | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # Required for creating the GitHub Release | |
| steps: | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 1. CHECKOUT | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| # For push:tags: github.ref is the tag ref (e.g. refs/tags/v1.4.0). | |
| # For workflow_dispatch: github.ref is the branch (e.g. refs/heads/main). | |
| # We never use the tag input here because the tag may not exist yet — | |
| # it is created in a later step. | |
| ref: ${{ github.ref }} | |
| fetch-depth: 0 | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 2. RESOLVE VERSION | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Resolve version from tag | |
| id: meta | |
| run: | | |
| REF="${{ github.event.inputs.tag || github.ref_name }}" | |
| VERSION="${REF#v}" | |
| echo "ref=${REF}" >> "$GITHUB_OUTPUT" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "Releasing version: ${VERSION}" | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 3. CREATE TAG (if it doesn't exist yet) | |
| # | |
| # When triggered via workflow_dispatch the tag may not exist yet. | |
| # This step creates and pushes it so the GitHub Release can reference it. | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Create and push tag if missing | |
| run: | | |
| REF="${{ steps.meta.outputs.ref }}" | |
| if git rev-parse --verify --quiet "refs/tags/${REF}" > /dev/null; then | |
| echo "Tag ${REF} already exists; skipping creation." | |
| else | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag "${REF}" | |
| git push origin "${REF}" | |
| echo "Created and pushed tag ${REF}." | |
| fi | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 4. BUILD & VERSION | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: '21' | |
| cache: maven | |
| - name: Set release version in POM | |
| run: | | |
| mvn -B -ntp versions:set \ | |
| -DnewVersion="${{ steps.meta.outputs.version }}" \ | |
| -DgenerateBackupPoms=false | |
| - name: Build | |
| run: mvn -B -ntp -DskipTests package | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 5. EXTRACT CHANGELOG | |
| # | |
| # Reads the section for this version from CHANGELOG.md. | |
| # Falls back to a plain message when the section is missing. | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Extract changelog for this version | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| HEADER="## [${VERSION}]" | |
| # Fixed-string match via index() avoids regex issues with dots in versions. | |
| NOTES=$(awk \ | |
| -v hdr="$HEADER" \ | |
| '/^## \[/ && p { exit } | |
| index($0, hdr) == 1 { p=1; next } | |
| p { print }' \ | |
| CHANGELOG.md) | |
| if [[ -z "$NOTES" ]]; then | |
| NOTES="No changelog entry found for ${VERSION}." | |
| fi | |
| printf '%s\n' "$NOTES" > release-notes.md | |
| echo "=== Release notes preview ===" && cat release-notes.md | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 6. GITHUB RELEASE | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create "${{ steps.meta.outputs.ref }}" \ | |
| --title "EzCountdown ${{ steps.meta.outputs.version }}" \ | |
| --notes-file release-notes.md \ | |
| "target/EzCountdown-${{ steps.meta.outputs.version }}.jar" | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 7. MODRINTH RELEASE | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Release to Modrinth (stable) | |
| env: | |
| MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| JAR="target/EzCountdown-${VERSION}.jar" | |
| JAR_NAME="$(basename "$JAR")" | |
| # Resolve all MC release versions >= 1.13 from Modrinth | |
| GAME_VERSIONS=$(python3 -c "import json,urllib.request; r=urllib.request.urlopen('https://api.modrinth.com/v2/tag/game_version'); vs=json.loads(r.read()); K=lambda v:[int(x) for x in v.split('.') if x.isdigit()]; print(json.dumps(sorted([v['version'] for v in vs if v['version_type']=='release' and K(v['version'])>=[1,13]],key=K)))") | |
| # Build version metadata JSON | |
| jq -n \ | |
| --arg name "EzCountdown ${VERSION}" \ | |
| --arg ver "${VERSION}" \ | |
| --rawfile changelog release-notes.md \ | |
| --arg jar_name "${JAR_NAME}" \ | |
| --argjson game_versions "${GAME_VERSIONS}" \ | |
| '{ | |
| name: $name, | |
| version_number: $ver, | |
| changelog: $changelog, | |
| dependencies: [], | |
| game_versions: $game_versions, | |
| version_type: "release", | |
| loaders: ["bukkit","spigot","paper","purpur","folia"], | |
| featured: true, | |
| project_id: "rCku0I4l", | |
| primary_file: $jar_name, | |
| file_parts: [$jar_name] | |
| }' > /tmp/version_data.json | |
| # Upload to Modrinth v2 API | |
| HTTP_CODE=$(curl -s \ | |
| -o /tmp/modrinth_resp.json \ | |
| -w "%{http_code}" \ | |
| -X POST "https://api.modrinth.com/v2/version" \ | |
| -H "Authorization: ${MODRINTH_TOKEN}" \ | |
| -F "data=@/tmp/version_data.json;type=application/json" \ | |
| -F "${JAR_NAME}=@${JAR};type=application/java-archive") | |
| if [[ "$HTTP_CODE" -lt 200 || "$HTTP_CODE" -ge 300 ]]; then | |
| echo "::error::Modrinth publish failed (HTTP ${HTTP_CODE}):" | |
| cat /tmp/modrinth_resp.json | |
| exit 1 | |
| fi | |
| echo "Published to Modrinth (HTTP ${HTTP_CODE})." | |
| python3 -c "import json; d=json.load(open('/tmp/modrinth_resp.json')); print(f' ID: {d.get(\"id\",\"?\")} | {d.get(\"name\",\"?\")} | {len(d.get(\"game_versions\",[]))} game version(s)')" | |
| # ────────────────────────────────────────────────────────────────────── | |
| # 8. DISCORD NOTIFICATION | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Post Discord notification | |
| env: | |
| DISCORD_WEBHOOK: ${{ secrets.EZCOUNTDOWN_RELEASE_DISCORD_WEBHOOK }} | |
| run: | | |
| REPO="${{ github.repository }}" | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| GH_URL="https://github.com/${REPO}/releases/tag/${{ steps.meta.outputs.ref }}" | |
| # Truncate changelog to fit Discord's 4096-char embed description limit. | |
| CHANGELOG=$(head -c 3800 release-notes.md || true) | |
| MODRINTH_URL="https://modrinth.com/plugin/ezcountdown/version/${VERSION}" | |
| PAYLOAD=$(jq -n \ | |
| --arg version "$VERSION" \ | |
| --arg gh_url "$GH_URL" \ | |
| --arg modrinth_url "$MODRINTH_URL" \ | |
| --arg changelog "$CHANGELOG" \ | |
| '{ | |
| embeds: [{ | |
| title: ("🚀 EzCountdown " + $version + " — Stable Release"), | |
| url: $gh_url, | |
| color: 3447003, | |
| description: $changelog, | |
| fields: [ | |
| { name: "GitHub Release", value: ("[View on GitHub](" + $gh_url + ")"), inline: true }, | |
| { name: "Modrinth", value: ("[Download on Modrinth](" + $modrinth_url + ")"), inline: true } | |
| ], | |
| footer: { text: "Stable release" } | |
| }] | |
| }') | |
| curl --fail -s -X POST "$DISCORD_WEBHOOK" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$PAYLOAD" |