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
70 changes: 62 additions & 8 deletions .ci/release.sh
Original file line number Diff line number Diff line change
@@ -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."
195 changes: 135 additions & 60 deletions .opencode/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -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<latest_tag>..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!: ...`)
Expand All @@ -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<CURRENT_VERSION>..HEAD --oneline`
2. Check commit messages for:
- `!` or `BREAKING CHANGE:` -> MAJOR
- `feat:` -> MINOR
Expand All @@ -63,29 +73,40 @@ 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
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:
Expand All @@ -97,60 +118,44 @@ 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<NEW_VERSION>
```

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 = "<NEW_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<VERSION> -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<NEW_VERSION>
```
Expand All @@ -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<VERSION> && git push origin v<VERSION>`
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<VERSION>`
3. Pushes the tag to GitHub

| Step | Command |
|------|---------|
| Check current version | `grep '^version =' Cargo.toml` |
| View recent commits | `git log v<CUR>..HEAD --oneline` |
| Check unpushed commits | `git rev-list --count origin/master..HEAD` |
| Create branch | `git checkout -b release/v<VER>` |
| Update version refs | `make update-version` |
| Update changelog | `make update-changelog` |
| Commit | `git commit -m "release: Version <VER>"` |
| Push branch | `git push -u origin release/v<VER>` |
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 <version>`
- 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 <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<VERSION>`
- [ ] Updated version in Cargo.toml
- [ ] Updated version in `Cargo.toml`
- [ ] Ran `make update-version`
- [ ] Ran `make update-changelog`
- [ ] Committed with message `release: Version <VERSION>`
- [ ] Pushed branch and created PR
- [ ] After merge: created tag `v<VERSION>`
- [ ] 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<CUR>..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 <VER>"` |
| Monitor CI | `gh run list --limit 5` |
| Run release script | `.ci/release.sh` |
Loading