| name | release-manager |
| description | Automate Blogr package releases: version bumping, CHANGELOG updates, tagging, and publishing to GitHub. |
When to use
Trigger phrases: "release", "tag a new version", "publish vX.Y.Z", "cut a release", "bump version".
Workflow
0. Pre-flight check — all PRs must be merged
Before any release work, verify that no open PRs target main:
git fetch origin main
OPEN_PRS=$(gh pr list --base main --state open --json number,title --jq '.[] | "\(.number) \(.title)"')
If $OPEN_PRS is not empty, abort immediately and display:
## ⛔ Release blocked — open PRs detected
The following PRs must be merged into `main` before releasing:
| # | Title |
|---|-------|
| 12 | feat: ... |
| 14 | fix: ... |
Options:
1. Merge them now (`gh pr merge <number> --squash`)
2. Cancel the release and come back later
Do not proceed until all open PRs targeting main are merged.
This ensures the release captures the intended changes and avoids
accidental version bumps with unmerged work.
1. Preview changes since the last release
- Run:
git log $(git describe --tags --abbrev=0)..HEAD --oneline --no-decorate
- If no tags exist yet, use:
git log --oneline --no-decorate
- Present the output to the user so they can see what changed before choosing a bump type
2. Determine the new version
-
Read current version from composer.json ("version": "0.x.y")
-
Ask user for bump type: patch, minor, major, or an explicit version like 0.19.0
-
Compute the new version using semver rules correctly:
| Current | patch (Z+1) | minor (Y+1, Z=0) | major (X+1, Y=Z=0) |
|---|
0.22.0 | 0.22.1 | 0.23.0 | 1.0.0 |
0.22.5 | 0.22.6 | 0.23.0 | 1.0.0 |
1.0.0 | 1.0.1 | 1.1.0 | 2.0.0 |
⚠️ Common mistake: patch is NOT 0.22.0 → 0.23.0 — that is a minor bump. Patch only increments the last digit.
-
Present the computed version to the user for confirmation
3. Organize uncommitted changes into feature-grouped commits
-
Run git status --short to list changed/new files
-
If there are no uncommitted changes, skip this step
-
If there are uncommitted changes, group files by feature area using path heuristics:
| Pattern | Suggested commit message |
|---|
src/Services/LocaleService*, src/Traits/ClearsLocaleCache*, src/Helpers/LocaleHelper*, view composer changes, route pattern changes in provider | feat: locale auto-detection with cache invalidation |
config/blogr.php (disabled keys), CmsPageController*, src/Models/CmsPage* (availableLocales) | feat: disabled locales return 404 on frontend |
src/Filament/Pages/BlogrSettings* | feat: redesign multilingual settings UI |
src/Filament/Resources/CmsPage*, CmsBlockBuilder* (non-import/export) | feat: per-translation CMS page editing |
resources/views/components/* (flag emojis) | feat: flag emojis in navigation and language-switcher |
resources/views/cms/pages/*, resources/views/layouts/* | feat: CMS content rendering |
src/Commands/*Import* | fix: CLI import delegates to CmsPageImportExportService |
INSTALL.md, storage/app/blogr-exports/* | docs: install guide and install page translations |
tests/* | test: add tests for new features (attach to relevant feature commit if possible, otherwise a single test commit) |
-
Heuristics:
- Files matching multiple patterns go with the first matching feature group
- Config-only changes to keys unrelated to above →
chore: update config
- Dependabot / lockfile changes →
chore(deps): update dependencies
-
For each group, stage and commit:
git add <file1> <file2> ...
git commit -m "<type>(<scope>): <description>"
4. Generate and present release notes
- Use the commit log from step 1 to format as markdown with conventional commit categories (Features, Bug Fixes, Dependencies, etc.)
- First output the full release notes as a text message (not inside a question tool) so the user sees them. Use a markdown code block.
- Then ask for approval using the
question tool with a simple question like "Do you approve these release notes?" and options ["Yes, proceed", "No, cancel"].
- Do NOT put the release notes inside the question's description field — the tool does not display long markdown content reliably.
- Include a "Cancel" option in case the user wants to abort.
- Only proceed when the user explicitly approves.
5. Run tests (ZERO TOLERANCE)
6. Update version files (atomic commit)
7. Update CHANGELOG.md (atomic commit)
Insert the new entry in correct semver descending order (highest version first), NOT at the top of the file.
Algorithm:
-
Read CHANGELOG.md and find all ## [vX.Y.Z] headers with their line numbers
-
Parse each version into (major, minor, patch) tuples
-
Compare the new version against existing versions:
- Walk through the file from top to bottom
- Find the first existing version that is semantically LOWER than the new one
- Insert the new entry immediately before that version's header
- If ALL existing versions are higher than the new one, append at the end of the version list (before
[Unreleased] if present)
-
Format the entry exactly as:
## [v{version}](https://github.com/happytodev/blogr/compare/v{version}...v{previous}) - {date}
### ✨ Features (or 🐛 Bug Fixes | ⬆️ Dependencies)
- **{title}**: {description}
8. Sync with remote before tagging
- Critical: Run
git fetch origin main to get the latest remote state.
- Verify that your local
main matches origin/main:
if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then
echo "⚠️ Local and remote main diverge. Run 'git pull --rebase origin main' first."
exit 1
fi
- If they diverge, run
git pull --rebase origin main and git push origin main before proceeding.
- Check that the tag does not already exist locally or remotely:
if git tag -l "v{version}" | grep -q . || git ls-remote --tags origin "refs/tags/v{version}" | grep -q .; then
echo "⚠️ Tag v{version} already exists. Create a new patch version instead."
exit 1
fi
- ⚠️ NEVER re-tag a version that has already been pushed. Once a tag is published, it is immutable for Packagist. If you need to fix a tag, bump to a new patch version.
9. Push main first, then tag
git push origin main
git tag v{version}
if [ "$(git rev-parse v{version})" != "$(git rev-parse HEAD)" ]; then
echo "⚠️ Tag does not match HEAD. Delete and re-tag."
git tag -d v{version}
exit 1
fi
git push origin v{version}
➡ Push main BEFORE creating the tag. This prevents the tag from pointing to a stale commit after a rebase.
10. Create GitHub Release
- Set
RELEASE_NOTES to the exact markdown the user approved in step 4 (the body of the CHANGELOG entry, without the heading/date line)
- Run:
gh release create v{version} --title "v{version}" --notes "$RELEASE_NOTES"
11. Confirm
- Inform the user the release was published with the URL and commit hash