From 9f731447cb1733a2752f9fc25748b5c870dbe272 Mon Sep 17 00:00:00 2001 From: Jory Irving Date: Mon, 22 Jun 2026 08:29:33 -0600 Subject: [PATCH] feat(release): automate releases --- .github/workflows/manual-release.yml | 75 ++++++++++++++++++++++++++++ AGENTS.md | 21 ++------ 2 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/manual-release.yml diff --git a/.github/workflows/manual-release.yml b/.github/workflows/manual-release.yml new file mode 100644 index 0000000..1e10925 --- /dev/null +++ b/.github/workflows/manual-release.yml @@ -0,0 +1,75 @@ +name: Manual Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (for example 0.0.20 or v0.0.20)' + required: true + type: string + +concurrency: + group: manual-release + cancel-in-progress: false + +jobs: + publish-release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Generate bot token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ secrets.BOT_CLIENT_ID }} + private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} + + - name: Checkout protected main + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: main + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Normalize release metadata + id: release + env: + RAW_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + VERSION="${RAW_VERSION#v}" + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version: $RAW_VERSION" + exit 1 + fi + echo "tag=$VERSION" >> "$GITHUB_OUTPUT" + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Create tag at protected main + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + SHA: ${{ steps.release.outputs.sha }} + TAG: ${{ steps.release.outputs.tag }} + run: | + set -euo pipefail + if EXISTING_SHA=$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$TAG" --jq .object.sha 2>/dev/null); then + if [ "$EXISTING_SHA" != "$SHA" ]; then + echo "Tag $TAG already points to $EXISTING_SHA" + exit 1 + fi + else + gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$TAG" -f sha="$SHA" >/dev/null + fi + + - name: Create GitHub release + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + TAG: ${{ steps.release.outputs.tag }} + run: | + set -euo pipefail + if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo "Release $TAG already exists" + else + gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --title "$TAG" --generate-notes + fi diff --git a/AGENTS.md b/AGENTS.md index 0084283..efc3718 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,21 +46,13 @@ Avoid these regressions: - Do not cut a release after every small change; discuss between releases when practical. ### Release Process -Releases are tag-driven. No version bump CI script exists — just tag and release. +Releases are tag-driven and automated. No source version bump is required. #### Steps -```bash -# Ensure main is up-to-date -git checkout main -git pull --ff-only --tags origin main - -git tag -git push origin - -# Create release -gh release create --repo misospace/windowstead --title "" --generate-notes -``` +1. Confirm the intended changes are merged and CI is green on `main`. +2. Open **Actions → Manual Release → Run workflow** and enter a version such as `0.0.20` (`v0.0.20` is also accepted). +3. The workflow tags protected `main` and creates the GitHub release. The published release triggers `.github/workflows/release.yml`: Godot export builds + asset upload. @@ -71,10 +63,7 @@ The published release triggers `.github/workflows/release.yml`: Godot export bui #### Validation gates -Before pushing a release tag: -- CI passes on `main` (`.github/workflows/test.yml`) -- Test suite passes -- No known regressions from the last release +Before running the release workflow, CI must pass on `main` (`.github/workflows/test.yml`) and there must be no known regressions from the last release. - Detailed GitHub issues are used to delegate work to a local model, so issue text should be concrete and implementation-oriented.