diff --git a/.ci/release.sh b/.ci/release.sh index 38d52c2..aeda5ea 100755 --- a/.ci/release.sh +++ b/.ci/release.sh @@ -1,21 +1,75 @@ #!/bin/bash -set -e +set -euo pipefail -if ! [ "$(git rev-list --count origin/master..HEAD)" -eq 0 ]; then - echo "There are commits in this branch. Please merge them first." - echo "CHANGELOG template needs master commit ID." - exit 1 +ensure_full_history() { + if git rev-parse --is-shallow-repository 2>/dev/null | grep -q "true"; then + echo "Shallow clone detected. Fetching full history..." + git fetch --unshallow --quiet + fi + echo "Fetching tags..." + git fetch --tags --quiet +} + +ensure_clean_state() { + if ! git diff --quiet || ! git diff --cached --quiet; then + echo "Working tree is not clean. Commit or stash changes first." + exit 1 + fi + + BRANCH=$(git rev-parse --abbrev-ref HEAD) + if [ "$BRANCH" != "master" ]; then + echo "Not on master branch. Current branch: $BRANCH" + exit 1 + fi + + git pull origin master --quiet + + if [ "$(git rev-list --count origin/master..HEAD 2>/dev/null || echo 1)" -ne 0 ]; then + echo "There are commits ahead of origin/master. Push or merge them first." + echo "CHANGELOG template needs master commit ID." + exit 1 + fi +} + +echo "=== Checking repository state ===" +ensure_clean_state +ensure_full_history + +echo "" +echo "=== Recent commits since last release ===" +LATEST_TAG=$(git tag --sort=-creatordate | head -1) +if [ -n "$LATEST_TAG" ]; then + git log "$LATEST_TAG"..HEAD --oneline +else + git log --oneline -10 fi -# bump version +echo "" +echo "=== Bumping version ===" vim ./Cargo.toml +VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' ./Cargo.toml | head -n1) +echo "New version: $VERSION" + +echo "" +echo "=== Updating dependencies ===" make update-version +echo "" +echo "=== Updating changelog ===" make update-changelog +echo "" +echo "=== Committing release ===" git add . -VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' ./Cargo.toml | head -n1) git commit -m "release: Version $VERSION" -echo "After merging the PR, tag and release are automatically done" +echo "" +echo "=== Creating release branch and pushing ===" +git checkout -b "release/v$VERSION" +git push -u origin "release/v$VERSION" + +echo "" +echo "Release v$VERSION prepared successfully." +echo "Create a PR to merge release/v$VERSION into master." +echo "After merge, tag and GitHub release are created automatically." diff --git a/.opencode/skills/release/SKILL.md b/.opencode/skills/release/SKILL.md index 3cabe44..e0efce1 100644 --- a/.opencode/skills/release/SKILL.md +++ b/.opencode/skills/release/SKILL.md @@ -1,36 +1,47 @@ --- name: release -description: Guide the release process for passless, including version bumping, changelog updates, and creating release branches. +description: Prepare and publish a new release. Use when the user asks to release, cut a release, or publish a new version. --- ## Purpose -Provide step-by-step instructions for releasing a new version of passless, ensuring proper versioning, changelog updates, and release commit management. +Release a new version of passless using the release script and CI pipeline. ## When to use -Use this skill when asked to: -- Create a new release -- Bump the version -- Update the changelog for a release -- Prepare a release commit +Use this skill when: +- The user asks to release a new version +- The user asks to cut a release or publish +- The user asks to tag a new version ## Prerequisites -Before starting a release: -1. Ensure you are on the `master` branch -2. Ensure the working tree is clean (no uncommitted changes) -3. Ensure local master is up to date with origin/master -4. Ensure HEAD is at origin/master (no unpushed commits) +Before releasing, verify: + +1. **Working tree is clean** — no uncommitted changes +2. **You are on `master`** — releases only happen from master +3. **Local master is up to date with origin/master** +4. **No commits ahead of origin/master** (output of `git rev-list --count origin/master..HEAD` must be 0) +5. **Repository is not a shallow clone** — git-cliff needs full history for accurate changelogs + +Check with: +```bash +git status --short +git rev-parse --abbrev-ref HEAD +git pull origin master +git rev-list --count origin/master..HEAD +git tag --sort=-creatordate | head -3 +git log --oneline v..HEAD +``` ## Version Decision Guide -Use Semantic Versioning (MAJOR.MINOR.PATCH). Determine the bump type by analyzing commits since the last release: +Use Semantic Versioning (MAJOR.MINOR.PATCH). Determine the bump type by analyzing commits since the last release. ### Major Version (X.0.0) Bump MAJOR when: -- Breaking changes to the CLI interface (removed/renamed commands or flags) +- Breaking changes to CLI interface or arguments - Breaking changes to configuration format - Breaking changes to public APIs - Commit message contains `BREAKING CHANGE:` or `!` (e.g., `feat!: ...`) @@ -49,12 +60,11 @@ Bump PATCH when: - Bug fixes (`fix:` commits) - Documentation updates (`docs:` commits) - Internal refactoring (`refactor:` commits) -- Performance improvements without API changes -- Dependency updates +- Dependency updates (`chore(deps):` commits) ### Decision Process -1. Run: `git log v$(sed -n 's/^version = "\(.*\)"/\1/p' ./Cargo.toml | head -n1)..HEAD --oneline` +1. Run: `git log v..HEAD --oneline` 2. Check commit messages for: - `!` or `BREAKING CHANGE:` -> MAJOR - `feat:` -> MINOR @@ -63,9 +73,9 @@ Bump PATCH when: ## Release Process -### Step 1: Verify Clean State and Sync +### Step 1: Verify Clean State -Ensure HEAD is at origin/master with no uncommitted or unpushed changes: +Ensure you're on master with no uncommitted changes, up to date with origin, and no commits ahead: ```bash git checkout master @@ -73,19 +83,30 @@ git pull origin master git status # Should show "nothing to commit, working tree clean" ``` -Check for unpushed commits: +Verify no commits ahead of origin/master: ```bash git rev-list --count origin/master..HEAD # Should output 0 ``` -If there are local commits not on origin/master, they must be merged first. The changelog template needs the master commit ID. +**Unshallow check** — shallow clones produce incomplete changelogs: + +```bash +git rev-parse --is-shallow-repository +``` + +If this outputs `true`, unshallow the repo before proceeding: + +```bash +git fetch --unshallow origin +git fetch --tags origin +``` ### Step 2: Determine Version 1. Get current version: ```bash - grep '^version =' Cargo.toml + grep '^version =' Cargo.toml | head -1 ``` 2. Review commits since last release: @@ -97,17 +118,13 @@ If there are local commits not on origin/master, they must be merged first. The ### Step 3: Create Release Branch -Create a branch named `release/v{NEW_VERSION}`: - ```bash git checkout -b release/v ``` -Example: `git checkout -b release/v0.3.0` - -### Step 4: Update Version in Cargo.toml +### Step 4: Update Version -Edit `Cargo.toml` and update the version field in the `[package]` section: +Edit `Cargo.toml` and update the version in the `[workspace.package]` section: ```toml version = "" @@ -115,42 +132,30 @@ version = "" ### Step 5: Update Dependencies -Run the update-version make target to update version references and Cargo.lock: - ```bash make update-version ``` -This command: -- Updates version references in workspace Cargo.toml files -- Runs `cargo update --workspace` +This updates version references in workspace Cargo.toml files and runs `cargo update --workspace`. ### Step 6: Update Changelog -Generate the changelog using git-cliff: - ```bash make update-changelog ``` This runs: `git cliff -t v -u -p CHANGELOG.md` -The changelog will be automatically updated with commits since the last release, grouped by type. - ### Step 7: Commit Changes -Stage and commit all changes: - ```bash git add . -VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' ./Cargo.toml | head -n1) +VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1) git commit -m "release: Version $VERSION" ``` ### Step 8: Push Branch and Create PR -Push the release branch: - ```bash git push -u origin release/v ``` @@ -159,33 +164,103 @@ Create a pull request to merge into master. ### Step 9: After Merge -After the commit is merged to master: -1. Create and push a tag: `git tag v && git push origin v` -2. The CI workflow automatically builds release artifacts, publishes to crates.io, and creates a GitHub Release +After the PR is merged to master, tagging and releasing is done **automatically** by CI: -## Quick Reference +1. `auto-tag.yaml` reads the new version from `CHANGELOG.md` +2. Creates a signed GPG tag `v` +3. Pushes the tag to GitHub -| Step | Command | -|------|---------| -| Check current version | `grep '^version =' Cargo.toml` | -| View recent commits | `git log v..HEAD --oneline` | -| Check unpushed commits | `git rev-list --count origin/master..HEAD` | -| Create branch | `git checkout -b release/v` | -| Update version refs | `make update-version` | -| Update changelog | `make update-changelog` | -| Commit | `git commit -m "release: Version "` | -| Push branch | `git push -u origin release/v` | +The tag then triggers: +- `rust.yml`: Builds, tests, publishes to crates.io, creates GitHub Release +- `aur-publish.yml`: Publishes AUR packages (`passless` and `passless-bin`) + +Monitor with: +```bash +gh run list --limit 5 +``` + +**Note:** Do not manually create tags — CI handles this automatically. + +## What NOT to Do + +| Mistake | Why it's wrong | Fix | +|---------|---------------|-----| +| Manually editing CHANGELOG.md | git-cliff generates it from conventional commits | Use `make update-changelog` | +| Creating git tags manually | `auto-tag.yaml` creates signed tags automatically | Just push to master | +| Releasing from a feature branch | Changelog generation needs master commit IDs | Checkout master first | +| Releasing with dirty working tree | Script will fail or produce incomplete release | Commit or stash changes first | +| Skipping the unshallow check | Shallow clones produce incomplete changelogs | Always check and unshallow if needed | +| Forgetting `make update-version` after Cargo.toml edit | Version won't propagate to workspace members | Always run `make update-version` | +| Using `--amend` on a commit | May amend the wrong parent commit after hook failures | Just commit again normally | + +## Troubleshooting + +### "There are commits ahead of origin/master" +Merge or push them first before starting the release. + +### Shallow clone detected +```bash +git fetch --unshallow origin +git fetch --tags origin +``` + +### git-cliff not installed +```bash +cargo install git-cliff +``` + +### Auto-tag workflow didn't trigger +Ensure: +- The CHANGELOG.md has a new version entry as the first `## [v...]` heading +- The tag doesn't already exist: `git tag -l | grep ` +- The `PAT` and `GPG_PRIVATE_KEY` secrets are configured in GitHub + +### Commit failed due to pre-commit hooks +Do NOT use `--amend`. Simply stage the changes and commit again: +```bash +git add . +git commit -m "release: Version " +``` + +## Key Files + +| File | Role | +|------|------| +| `Cargo.toml` | Workspace version (single source of truth) | +| `cliff.toml` | git-cliff configuration for changelog generation | +| `CHANGELOG.md` | Generated changelog (auto-tag reads version from here) | +| `.github/workflows/auto-tag.yaml` | Creates signed GPG tag on push to master | +| `.github/workflows/rust.yml` | Builds, tests, publishes to crates.io and GitHub on tag | +| `.github/workflows/aur-publish.yml` | Publishes AUR packages on tag | +| `Makefile` | `update-version` and `update-changelog` targets | +| `.ci/release.sh` | Full release script | ## Checklist - [ ] On master branch, clean working tree - [ ] Pulled latest from origin/master -- [ ] No unpushed commits (HEAD at origin/master) +- [ ] No commits ahead of origin/master +- [ ] Repository is not a shallow clone (or has been unshallowed) - [ ] Determined version bump type (MAJOR/MINOR/PATCH) - [ ] Created release branch `release/v` -- [ ] Updated version in Cargo.toml +- [ ] Updated version in `Cargo.toml` - [ ] Ran `make update-version` - [ ] Ran `make update-changelog` - [ ] Committed with message `release: Version ` -- [ ] Pushed branch and created PR -- [ ] After merge: created tag `v` +- [ ] Pushed and created PR +- [ ] After merge: CI automatically creates tag and release + +## Quick Reference + +| Step | Command | +|------|---------| +| Check current version | `grep '^version =' Cargo.toml \| head -1` | +| View recent commits | `git log v..HEAD --oneline` | +| Check commits ahead | `git rev-list --count origin/master..HEAD` | +| Check shallow clone | `git rev-parse --is-shallow-repository` | +| Unshallow repo | `git fetch --unshallow origin && git fetch --tags origin` | +| Update version refs | `make update-version` | +| Update changelog | `make update-changelog` | +| Commit | `git commit -m "release: Version "` | +| Monitor CI | `gh run list --limit 5` | +| Run release script | `.ci/release.sh` |