feat: add release pipeline and auto-reinstall#20
Conversation
GitHub Actions workflow triggered on push to main. Parses conventional commits for version bump, updates all version files, creates GitHub Release with tarball, and updates Homebrew formula — single atomic push. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the watchdog watcher with SHA-based staleness detection to auto-update the local devflow installation when main gets new commits. Opt-in via devflow watch setup, handles symlink/copy/Homebrew modes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9 tasks across 5 chunks: conventional commit parser, semver arithmetic, version bump script, CLI wiring, GitHub Actions workflow, install mode detection, auto-reinstall check, documentation, and final verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_parse_conventional_commits scans commits since last tag for feat:/fix:/ feat!:/BREAKING CHANGE: prefixes. _semver_bump does pure-bash version arithmetic. [skip release] only checked on HEAD commit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bump_all_versions uses portable sed for all 5+ version files. Standalone scripts/bump-version.sh for CI use. bin/devflow routes version-bump and release commands. Formula update test added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Triggers on push to main, parses conventional commits, bumps versions, creates tarball, updates Homebrew formula, and creates GitHub Release. Single atomic push with [skip release] guard. Supports workflow_dispatch for manual overrides and dry-run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detects install mode (symlink/copy/Homebrew), compares origin/main SHA against last-installed SHA, runs make install/link when stale. Opt-in via devflow watch setup. Supports dry-run, error logging with retry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Release Process and Auto-Reinstall sections to CLAUDE.md. Add release.sh to project structure tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add watch, check-version, version-bump, and release to the CLI commands reference table. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New skill that guides deep technical walkthroughs before implementation: - Traces end-to-end flows with actual code and file paths - Shows side effects (events, billing, observability) branching off main flow - Creates prioritized files-to-read lists - Verifies assumptions against tickets before stating them Added as optional step 5 in the new-feature flow, offered before brainstorming. Registered in skills/registry.json and converted to plugin command. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a conventional-commit–driven release pipeline (CLI + GitHub Actions) and extends the watchdog to optionally auto-reinstall devflow when origin/main advances, along with supporting docs, tests, and plugin/skill updates.
Changes:
- Introduces
lib/release.sh+.github/workflows/release.ymlto compute semver bumps from conventional commits and publish releases automatically. - Adds
scripts/bump-version.shand amake version-bumptarget to update all version-bearing files consistently. - Extends
lib/watch.shwith install-mode detection and an opt-in auto-reinstall step; updates skills/plugin command docs accordingly.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| visualizations/architecture/devflow-ecosystem.md | Documents new CLI commands in the ecosystem diagram/table. |
| tests/unit/watch.bats | Adds unit tests for install-mode detection and auto-reinstall guards. |
| tests/unit/release.bats | Adds unit tests for conventional commit parsing, semver bumping, and version bump behavior. |
| skills/worktree-flow/new-feature.md | Adds optional “codebase walkthrough” step before brainstorming. |
| skills/registry.json | Registers the new codebase-walkthrough skill. |
| skills/process-discipline/codebase-walkthrough.md | Adds the new walkthrough skill definition/content. |
| scripts/bump-version.sh | Adds a standalone version bump script used by CLI/CI. |
| Makefile | Adds version-bump phony target and wiring to bump script. |
| lib/watch.sh | Adds auto-reinstall functionality and setup opt-in; integrates into watcher run steps. |
| lib/release.sh | Adds conventional commit parsing, semver bumping, and CLI wrappers for release/version bump. |
| docs/plans/2026-03-19-release-and-reinstall-plan.md | Adds an implementation plan doc for the feature set. |
| docs/plans/2026-03-18-release-pipeline-design.md | Adds design doc for the release pipeline. |
| docs/plans/2026-03-18-auto-reinstall-design.md | Adds design doc for the auto-reinstall extension. |
| devflow-plugin/hooks/hooks.json | Adds a Stop hook to remind about session wrap-up commands. |
| devflow-plugin/commands/writing-plans.md | Updates the writing-plans command content (agent-deck handoff flow). |
| devflow-plugin/commands/visualizations-config.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/update-visualizations.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/spec-feature.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/session-summary.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/scope-check.md | Deletes this command doc. |
| devflow-plugin/commands/retain-learning.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/reflect-session.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/pre-push-check.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/pr-strategy.md | Deletes this command doc. |
| devflow-plugin/commands/dependency-update.md | Removes version badge from description; keeps command intent. |
| devflow-plugin/commands/create-pr.md | Simplifies PR creation guidance to GitHub-only flow. |
| devflow-plugin/commands/new-feature.md | Simplifies new-feature flow; adds optional walkthrough hook. |
| devflow-plugin/commands/finish-feature.md | Simplifies finish-feature flow; removes VCS-aware/MR branching. |
| devflow-plugin/commands/task-prioritize.md | Deletes this command doc. |
| devflow-plugin/commands/task-complete.md | Deletes this command doc. |
| devflow-plugin/commands/check-sensitive.md | Deletes this command doc. |
| devflow-plugin/commands/codebase-walkthrough.md | Adds plugin command file for the walkthrough (mirrors skill content). |
| CLAUDE.md | Documents release process + auto-reinstall; adds lib/release.sh to structure. |
| bin/devflow | Sources lib/release.sh and adds version-bump / release command dispatch + help. |
| .github/workflows/release.yml | Adds automated release workflow for pushes to main and manual dispatch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Determine commit range | ||
| local range | ||
| if [[ -n "$last_tag" ]]; then | ||
| range="${last_tag}..HEAD" | ||
| else | ||
| # No previous tag — scan from initial commit | ||
| local initial_commit | ||
| initial_commit="$(git -C "$project_dir" rev-list --max-parents=0 HEAD 2>/dev/null | head -1)" | ||
| range="${initial_commit}..HEAD" | ||
| fi | ||
|
|
||
| # Get full commit messages (subject + body) | ||
| local commits | ||
| commits="$(git -C "$project_dir" log "$range" --format='%B---COMMIT_SEP---' 2>/dev/null || echo "")" |
There was a problem hiding this comment.
When no previous tag exists, the computed range ${initial_commit}..HEAD excludes the initial (root) commit itself. For a repo with only one commit (the root), git log <root>..HEAD returns nothing, so the parser will incorrectly return none and skip the first release. Consider scanning all commits when last_tag is empty (e.g., git log --format ... HEAD), or using an inclusive range (e.g., --reverse --format ... $(git rev-list --max-parents=0 HEAD)), so the root commit is included.
| # Determine commit range | |
| local range | |
| if [[ -n "$last_tag" ]]; then | |
| range="${last_tag}..HEAD" | |
| else | |
| # No previous tag — scan from initial commit | |
| local initial_commit | |
| initial_commit="$(git -C "$project_dir" rev-list --max-parents=0 HEAD 2>/dev/null | head -1)" | |
| range="${initial_commit}..HEAD" | |
| fi | |
| # Get full commit messages (subject + body) | |
| local commits | |
| commits="$(git -C "$project_dir" log "$range" --format='%B---COMMIT_SEP---' 2>/dev/null || echo "")" | |
| # Determine log target | |
| local log_target | |
| if [[ -n "$last_tag" ]]; then | |
| log_target="${last_tag}..HEAD" | |
| else | |
| # No previous tag — scan all commits reachable from HEAD, including the root commit | |
| log_target="HEAD" | |
| fi | |
| # Get full commit messages (subject + body) | |
| local commits | |
| commits="$(git -C "$project_dir" log "$log_target" --format='%B---COMMIT_SEP---' 2>/dev/null || echo "")" |
| # Get full commit messages (subject + body) | ||
| local commits | ||
| commits="$(git -C "$project_dir" log "$range" --format='%B---COMMIT_SEP---' 2>/dev/null || echo "")" | ||
| if [[ -z "$commits" ]]; then |
There was a problem hiding this comment.
Commit parsing currently stores the full log output in a bash variable and then tries to inject NUL separators via sed 's/.../\x00/g'. BSD/macOS sed does not reliably support \xNN escapes, and bash variables cannot hold NUL bytes anyway, so this approach is fragile across platforms. Prefer streaming directly from git log using git’s built-in %x00 to emit NUL separators (and avoid the intermediate variable), then iterate with read -d ''.
| # Log full make output for debugging | ||
| mkdir -p "${HOME}/.devflow" | ||
| echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] make ${make_target} FAILED output:" >> "${HOME}/.devflow/watch.log" | ||
| echo "$make_output" >> "${HOME}/.devflow/watch.log" | ||
| _watch_notify "devflow auto-update FAILED: make ${make_target} exited with code $?" "$headless" |
There was a problem hiding this comment.
In the failure path, the notification uses $? after writing to the log, so it will report the exit code of the last echo/mkdir, not the failing make command. Capture make's exit status immediately (e.g., into a local make_rc) before doing any other commands, and use that value in the notify/log message.
| # Log full make output for debugging | |
| mkdir -p "${HOME}/.devflow" | |
| echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] make ${make_target} FAILED output:" >> "${HOME}/.devflow/watch.log" | |
| echo "$make_output" >> "${HOME}/.devflow/watch.log" | |
| _watch_notify "devflow auto-update FAILED: make ${make_target} exited with code $?" "$headless" | |
| local make_rc=$? | |
| # Log full make output for debugging | |
| mkdir -p "${HOME}/.devflow" | |
| echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] make ${make_target} FAILED output:" >> "${HOME}/.devflow/watch.log" | |
| echo "$make_output" >> "${HOME}/.devflow/watch.log" | |
| _watch_notify "devflow auto-update FAILED: make ${make_target} exited with code ${make_rc}" "$headless" |
| feat:*|feat\(*) | ||
| [[ "$bump" != "major" ]] && bump="minor" | ||
| feat_msgs+="feat|${subject}\n" | ||
| ;; | ||
| fix:*|fix\(*) | ||
| [[ "$bump" == "none" ]] && bump="patch" | ||
| fix_msgs+="fix|${subject}\n" | ||
| ;; | ||
| docs:*|chore:*|refactor:*|test:*|ci:*|style:*|perf:*) |
There was a problem hiding this comment.
The categorization case patterns only match types with a literal : (e.g., refactor:) or type(, so conventional-commit variants with breaking markers like refactor!: / docs!: won’t be captured into any of the feat|fix|other lists. This leads to missing entries in the preview/release notes even though the bump may be detected as major. Consider allowing an optional ! before : (and after scope) for the non-feat/fix categories as well, or normalizing the subject before matching.
| feat:*|feat\(*) | |
| [[ "$bump" != "major" ]] && bump="minor" | |
| feat_msgs+="feat|${subject}\n" | |
| ;; | |
| fix:*|fix\(*) | |
| [[ "$bump" == "none" ]] && bump="patch" | |
| fix_msgs+="fix|${subject}\n" | |
| ;; | |
| docs:*|chore:*|refactor:*|test:*|ci:*|style:*|perf:*) | |
| feat:*|feat!:*|feat\(*) | |
| [[ "$bump" != "major" ]] && bump="minor" | |
| feat_msgs+="feat|${subject}\n" | |
| ;; | |
| fix:*|fix!:*|fix\(*) | |
| [[ "$bump" == "none" ]] && bump="patch" | |
| fix_msgs+="fix|${subject}\n" | |
| ;; | |
| docs:*|docs!:*|docs\(*|chore:*|chore!:*|chore\(*|refactor:*|refactor!:*|refactor\(*|test:*|test!:*|test\(*|ci:*|ci!:*|ci\(*|style:*|style!:*|style\(*|perf:*|perf!:*|perf\(*) |
- Use `HEAD` instead of `initial_commit..HEAD` when no tags exist, so the root commit is included in the first release - Use git's `%x00` format for NUL-separated commit parsing instead of piping through `sed 's/.../\x00/g'` (portable, no bash NUL issues) - Capture `make` exit code immediately with `local make_rc=$?` before logging commands overwrite `$?` - Add `type!:` breaking-change variants to all case patterns so `refactor!:`, `docs!:`, etc. are categorized in release notes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
review the code after the changes and if you find any required change, do the changes yourself! Also, resolve the conversations if not needed anymore @copilot |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
5 similar comments
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
|
@AndreJorgeLopes The model is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it. You can try again without specifying a model (just If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: |
Summary
Changes
Release Pipeline (
lib/release.sh,.github/workflows/release.yml)_parse_conventional_commits) determines version bump fromfeat:/fix:/feat!:/BREAKING CHANGE:bump_all_versionsupdates Makefile, utils.sh, plugin.json, marketplace.json, and all command badges using portable sed[skip release]support,workflow_dispatchfor manual overridesdevflow version-bump <version>,devflow release(preview)Auto-Reinstall (
lib/watch.sh)_detect_install_mode: distinguishes symlink (make link), copy (make install), Homebrew (warn only)_auto_reinstall_check: SHA-based staleness detection, opt-in viadevflow watch setupTest plan
bats tests/unit/)make test)make check-version)devflow releasepreview worksdevflow helpshows all new commands