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
75 changes: 75 additions & 0 deletions .github/workflows/manual-release.yml
Original file line number Diff line number Diff line change
@@ -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
21 changes: 5 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <version>
git push origin <version>

# Create release
gh release create <version> --repo misospace/windowstead --title "<version>" --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.

Expand All @@ -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.


Expand Down