diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..de859af --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,29 @@ +name: PR Title + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: + pull-requests: read + +jobs: + check: + name: Validate Conventional Commit Title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + docs + style + refactor + test + ci + perf + revert diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b841e6d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-go@v5 + with: + go-version: "1.24" + + - name: Install golangci-lint + uses: golangci/golangci-lint-action@v7 + with: + version: v2.10 + install-mode: binary + args: --help + + - name: Run CI + run: make ci + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Generate changelog + run: | + npx conventional-changelog-cli -p conventionalcommits -r 1 -o RELEASE_NOTES.md + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create ${{ github.ref_name }} \ + --title "${{ github.ref_name }}" \ + --notes-file RELEASE_NOTES.md diff --git a/Makefile b/Makefile index 37a911f..01960f5 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,16 @@ GO := go GOFLAGS := -v TIMEOUT := 120s +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none") +DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +LDFLAGS := -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE) + .PHONY: build test lint vet fmt-check fmt ci clean -## build: Compile the questcore binary +## build: Compile the questcore binary with version info build: - $(GO) build $(GOFLAGS) -o $(BINARY) ./cmd/questcore + $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BINARY) ./cmd/questcore ## test: Run all tests with race detection test: diff --git a/cmd/questcore/main.go b/cmd/questcore/main.go index 1ae91eb..514288d 100644 --- a/cmd/questcore/main.go +++ b/cmd/questcore/main.go @@ -1,5 +1,5 @@ // QuestCore is a deterministic, data-driven game engine for text adventures. -// Usage: questcore [--plain] +// Usage: questcore [--version] [--plain] package main import ( @@ -12,12 +12,22 @@ import ( "github.com/nathoo/questcore/tui" ) +// Set via -ldflags at build time. +var ( + version = "dev" + commit = "none" + date = "unknown" +) + func main() { plain := false var gameDir string for _, arg := range os.Args[1:] { switch arg { + case "--version": + fmt.Printf("questcore %s (commit %s, built %s)\n", version, commit, date) + return case "--plain": plain = true default: @@ -28,7 +38,7 @@ func main() { } if gameDir == "" { - fmt.Fprintf(os.Stderr, "Usage: questcore [--plain] \n") + fmt.Fprintf(os.Stderr, "Usage: questcore [--version] [--plain] \n") os.Exit(1) } diff --git a/docs/plans/release-pipeline.md b/docs/plans/release-pipeline.md new file mode 100644 index 0000000..981f1be --- /dev/null +++ b/docs/plans/release-pipeline.md @@ -0,0 +1,35 @@ +# Release Pipeline Plan + +## Goal + +Automate versioning, changelog, and GitHub Releases from conventional commits. + +## How It Works + +1. PRs use conventional commit titles (`feat:`, `fix:`, `chore:`, etc.) +2. Squash merge with "PR title + commit details" — the PR title becomes the + conventional commit message on main +3. When ready to release, push a semver tag (`git tag v0.1.0 && git push --tags`) +4. Release workflow runs `make ci`, then generates CHANGELOG via + conventional-changelog and creates a GitHub Release +5. PR title format is enforced by the semantic-pull-request action + +## Files + +| File | Action | +|------|--------| +| `.github/workflows/release.yml` | Create — tag-triggered release workflow | +| `.github/workflows/pr-title.yml` | Create — enforce conventional commit PR titles | +| `cmd/questcore/main.go` | Update — version vars + `--version` flag | +| `Makefile` | Update — ldflags in build target | +| `package.json` | Create — conventional-changelog dependency | + +## Task List + +- [ ] Add version vars and `--version` flag to `cmd/questcore/main.go` +- [ ] Update Makefile build target with ldflags +- [ ] Create `.github/workflows/pr-title.yml` (semantic-pull-request) +- [ ] Set up conventional-changelog +- [ ] Create `.github/workflows/release.yml` +- [ ] Verify `make ci` passes +- [ ] Push branch, open PR diff --git a/docs/TUI_PLAN.md b/docs/plans/tui-roadmap.md similarity index 100% rename from docs/TUI_PLAN.md rename to docs/plans/tui-roadmap.md