Skip to content
Merged
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
234 changes: 234 additions & 0 deletions .agents/skills/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
---
name: photofield-release
description: >-
Follow the Photofield release process: batch changelog entries, create the
release commit and tag, and push. Use when the user says "release", "cut a
release", "create a release", or "prepare vX.Y.Z". Always ask before pushing.
---

# Photofield Release Process

Follow these steps to create a new Photofield release.

## 1. Prerequisites

Before starting, verify all of these:

```bash
# Must be on main
git branch --show-current | grep -q '^main$'
```

```bash
# Working directory must be clean
git diff-index --quiet HEAD -- || { echo "NOT CLEAN"; exit 1; }
```

```bash
# CI must be passing on current HEAD
curl -s "https://api.github.com/repos/SmilyOrg/photofield/commits/$(git rev-parse HEAD)/status" | grep -q '"state":"success"' && echo "CI PASSING" || echo "CI NOT PASSING"
Comment on lines +28 to +29
```

If any check fails, stop and report the issue to the user. Do not proceed.

## 2. Check for Unreleased Changes

```bash
ls -la .changes/unreleased/
```

If the directory contains only `.gitkeep`, there are no unreleased entries.
Tell the user: *"No unreleased changelog entries found. Nothing to release."*

List each entry for the user (read the YAML files):

```bash
for f in .changes/unreleased/*.yaml; do [ -e "$f" ] || continue
kind=$(grep '^kind:' "$f" | sed 's/kind: //')
body=$(grep '^body:' "$f" | sed 's/body: //')
echo "- [$kind] $body"
done
```

## 3. Determine Next Version

Changie's auto-bump rules (from `.changie.yaml`):

| Kind | Bump type | Semantic meaning (at 0.x) |
|--------------------|-----------|---------------------------|
| `Breaking Changes` | minor | `0.X.0 → 0.(X+1).0` |
| `Added` | minor | `0.X.0 → 0.(X+1).0` |
| `Removed` | minor | `0.X.0 → 0.(X+1).0` |
| `Deprecated` | minor | `0.X.0 → 0.(X+1).0` |
| `Fixed` | patch | `0.X.Y → 0.X.(Y+1)` |
| `Security` | patch | `0.X.Y → 0.X.(Y+1)` |

**Rule:** If *any* entry is a minor-bump kind, bump **minor**. Otherwise bump **patch**.

```bash
CURRENT=$(changie latest | sed 's/^v//')
MAJOR=$(echo "$CURRENT" | cut -d. -f1)
MINOR=$(echo "$CURRENT" | cut -d. -f2)
PATCH=$(echo "$CURRENT" | cut -d. -f3)
```

Check if any entry is a minor-bump kind:

```bash
# Sets BUMP to "MINOR" if any entry is a minor-bump kind, otherwise "PATCH"
BUMP=$(grep -l -E '^kind: (Breaking Changes|Added|Removed|Deprecated)' .changes/unreleased/*.yaml 2>/dev/null | grep -qv '^$' && echo "MINOR" || echo "PATCH")
```

```bash
if [ "$BUMP" = "MINOR" ]; then
NEXT="v${MAJOR}.$((MINOR+1)).0"
else
NEXT="v${MAJOR}.${MINOR}.$((PATCH+1))"
fi
```

## 4. Ask the User

Show the user:

- Current version (`$CURRENT`)
- Unreleased entries (list each kind + body)
- Suggested next version (`$NEXT`)
- Suggested release title (see §5)

**Do not proceed until the user confirms.**

## 5. Set Release Title

Replace the default date-based title with a descriptive one:

| Release type | Title convention |
|---|---|
| Patch | `"Security patches"`, `"Bug fixes and dependency updates"`, `"Stability improvements"` |
| Minor | Short phrase summarizing the main feature(s) |

Example edit for `.changes/$NEXT.md`:

```yaml
oldText: "## [$NEXT] - 2026-06-16"
newText: "## [$NEXT] - Security patches"
```

## 6. Batch and Merge

```bash
# Merge unreleased fragments into version file
changie batch "$NEXT"

# Review the generated file
cat .changes/$NEXT.md

# Set a descriptive title (see §5)
# Use edit to replace the title line in .changes/$NEXT.md

# Merge into CHANGELOG.md
changie merge
```

## 7. Commit and Tag

⚠️ The `task release` target has `changie batch` and `changie merge`
**commented out** — it expects the agent to run them first. Do **not** run
`task release` (it will fail on a dirty tree).

```bash
git add CHANGELOG.md .changes/$NEXT.md
git commit -m "Release $NEXT"
git tag -a "$NEXT" -m "Release $NEXT"
```

## 8. Review

```bash
git show HEAD
task release:changelog
```

Verify:
- Changelog looks correct and well-formatted
- Tag message matches
- Commit only contains expected files (CHANGELOG.md + .changes/$NEXT.md)

Show the review output to the user.

## 9. Push (Only With Approval)

**Always ask before pushing.** Show:

```
Release ready to push:
- Commit: <hash> — Release $NEXT
- Tag: $NEXT
- Changelog: <summary>

Push? (yes/no)
```

Once confirmed:

```bash
git push
git push --tags
```

Then report:

> Release pushed ✓
> - Commit: `<hash>` → `main`
> - Tag: `$NEXT`
> - CI running: https://github.com/SmilyOrg/photofield/actions

## 10. Undo Procedures

### Before push

```bash
git tag -d "$NEXT"
git reset --hard HEAD~1
```

### After push (Only With Explicit Approval)

```bash
git tag -d "$NEXT"
git push origin :refs/tags/$NEXT
git reset --hard HEAD~1
git push --force-with-lease origin main
```

Then fix the issue and restart from §6.

## Quick Reference

| Command | Purpose |
|---------|---------|
| `changie latest` | Get current latest version |
| `ls .changes/unreleased/` | List unreleased entries |
| `changie batch <v>` | Batch entries into version file |
| `changie merge` | Merge into CHANGELOG.md |
| `changie new -k <kind> -b <body>` | Create a changelog entry inline |
| `git add CHANGELOG.md .changes/<v>.md` | Stage for commit |
| `git commit -m "Release <v>"` | Create release commit |
| `git tag -a <v> -m "Release <v>"` | Create annotated tag |
| `git push && git push --tags` | Push release |
| `task release:changelog` | Preview changelog |
| `task release:undo` | Undo last release (only if not pushed) |

## Common Pitfalls

1. **Dirty working directory** — `task release` will fail. Stage and commit
changie outputs first.
2. **Wrong version bump** — If you have *any* minor-bump entries but only bump
patch, the version is wrong. Always check all entry kinds.
3. **Missing `.changes/<v>.md`** — This file must be committed alongside
`CHANGELOG.md`.
4. **Changie `-e` flag** — `changie new -e` opens an editor. Use `-b` to set
body inline: `changie new -k Fixed -b "Fix description"`.
5. **`git push` bypasses branch rules** — The repo requires PRs for non-release
commits. The release commit is exempted (remote logs "Bypassed rule
violations for refs/heads/main"). This is expected.
Loading