-
Notifications
You must be signed in to change notification settings - Fork 1
Pipeline Plan 59
The .claude/ directory has security restrictions preventing writes. The plan is complete — let me present it directly.
Add scripts/sw-release.sh — a new standalone script that automates batched releases with semantic versioning, changelog generation, GitHub Release creation, and daemon-integrated scheduling. Follows all existing Shipwright patterns: set -euo pipefail, Bash 3.2 compatibility, emit_event, PASS/FAIL test harness, and daemon config flags.
| File | Action | Purpose |
|---|---|---|
scripts/sw-release.sh |
Create | Release train orchestration — version determination, changelog generation, GitHub Release creation, schedule management |
scripts/sw-release-test.sh |
Create | Test suite — 20+ tests covering version determination, changelog generation, batching, config, schedule triggers |
scripts/sw |
Edit | Add release command to CLI router + help text |
scripts/sw-daemon.sh |
Edit | Add release train scheduling to patrol loop + config loading |
scripts/sw-fleet.sh |
Edit | Add downstream release notification when upstream repo releases |
package.json |
Edit | Add sw-release-test.sh to test chain |
.github/workflows/release.yml |
Edit | Add optional workflow_dispatch trigger |
Standard Shipwright boilerplate (set -euo pipefail, VERSION="1.10.0", colors, info()/success()/warn()/error(), emit_event, compat.sh sourcing).
Subcommands: status, plan, create, changelog, config, history, schedule-check, help
Core functions:
-
release_collect_prs()—gh pr list --state merged --base main --json number,title,labels,author,mergedAtfiltered bymergedAt > last_tag_date. Last tag found viagit describe --tags --abbrev=0 2>/dev/null. Returns JSON array. -
release_categorize_prs()— Classify PRs using jq. Priority order:-
breaking— labelbreaking-changeor title containsBREAKING -
feature— labelenhancement/feature, or title starts withfeat: -
fix— labelbug/fix, or title starts withfix: -
docs— labeldocumentationor title starts withdocs: -
refactor— labelrefactoror title starts withrefactor: -
other— everything else
-
-
release_determine_version()— Read current version frompackage.jsonvia jq. ParseMAJOR.MINOR.PATCH. If anybreakingPR → MAJOR+1, minor=0, patch=0. Else if anyfeature→ MINOR+1, patch=0. Else → PATCH+1. Support--force-bump major|minor|patchoverride. -
release_generate_changelog()— Build markdown section grouped by category. Insert after## [Unreleased]line in existingCHANGELOG.md. Format:- Title (#number) — @author. Atomic write: tmp file +mv. -
release_create()— Full flow:- Collect → categorize → determine version
- Call
scripts/update-version.sh $new_version - Generate changelog
git add -A && git commit -m "release: v${new_version}"git tag "v${new_version}"- If
--pushorauto_push=true:git push origin main --tags - If not
NO_GITHUB:gh release create "v${new_version}" --title "Shipwright v${new_version}" --notes-file <tmp_notes> - Emit
release.created,release.tagged,release.published
-
release_check_eligible()— PR count >=batch_size(default 1), no active pipelines (check daemon state), CI passing on main. -
release_schedule_check()— Exit 0 if eligible. Modes:manual(always exit 1),pr_count(merged PRs >= batch_size),daily(24h since last tag),weekly(7d since last tag).
Add to .claude/daemon-config.json schema under release_train:
{
"release_train": {
"enabled": false,
"schedule": "manual",
"batch_size": 5,
"auto_push": false,
"auto_publish": false,
"changelog_path": "CHANGELOG.md",
"version_source": "package.json"
}
}In sw-daemon.sh load_config() (~line 416, after patrol settings), add:
RELEASE_TRAIN_ENABLED=$(jq -r '.release_train.enabled // false' "$config_file") RELEASE_TRAIN_SCHEDULE=$(jq -r '.release_train.schedule // "manual"' "$config_file") RELEASE_TRAIN_BATCH_SIZE=$(jq -r '.release_train.batch_size // 5' "$config_file")
After the patrol block (~line 4914) and inside the now_e scope, add:
# Release train check (independent of patrol) if [[ "${RELEASE_TRAIN_ENABLED:-false}" == "true" ]]; then if [[ $((now_e - LAST_RELEASE_CHECK_EPOCH)) -ge "${RELEASE_TRAIN_CHECK_INTERVAL:-300}" ]]; then if "$SCRIPT_DIR/sw-release.sh" schedule-check 2>/dev/null; then daemon_log INFO "Release train triggered" "$SCRIPT_DIR/sw-release.sh" create --auto || daemon_log WARN "Release creation failed" fi LAST_RELEASE_CHECK_EPOCH=$now_e fi fi
Initialize LAST_RELEASE_CHECK_EPOCH=0 at top alongside LAST_PATROL_EPOCH.
In sw-fleet.sh, add fleet_notify_release():
- Reads fleet config repos array
- For each repo with a GitHub remote, creates an issue or dispatches event
- Guarded by
notify_releasesconfig flag - Emit
fleet.release_notificationevent
Add workflow_dispatch trigger to .github/workflows/release.yml:
on: push: tags: ["v*"] workflow_dispatch: inputs: version: description: "Version to release (e.g., 1.11.0)" required: true
In scripts/sw:
- Add help line:
${CYAN}release${RESET} <cmd> ${BOLD}Release train${RESET} — batched releases with changelogs - Add route:
release) exec "$SCRIPT_DIR/sw-release.sh" "$@" ;;
scripts/sw-release-test.sh following sw-fleet-test.sh pattern. 20+ tests with mock gh/git binaries:
| # | Test | Assertion |
|---|---|---|
| 1 | Version: patch only |
1.10.0 → 1.10.1
|
| 2 | Version: minor (feature) |
1.10.0 → 1.11.0
|
| 3 | Version: major (breaking) |
1.10.0 → 2.0.0
|
| 4 | Version: force override |
--force-bump minor overrides auto |
| 5 | Categorize: label-based |
enhancement → feature |
| 6 | Categorize: title-based |
feat: → feature |
| 7 | Categorize: mixed types | Multiple categories correct |
| 8 | Changelog: format | Keep a Changelog markdown |
| 9 | Changelog: PR links |
(#123) format |
| 10 | Changelog: empty categories | Omitted from output |
| 11 | Changelog: insertion | After [Unreleased], preserves existing |
| 12 | Eligible: batch size | Triggers at N PRs |
| 13 | Eligible: daily schedule | Triggers after 24h |
| 14 | Eligible: weekly schedule | Triggers after 7d |
| 15 | Eligible: no PRs | Returns 1 |
| 16 | Config: defaults | Missing config → sensible defaults |
| 17 | Config: custom values | Custom config respected |
| 18 | Status: output | Shows pending PRs + version |
| 19 | Plan: dry run | Correct format, no side effects |
| 20 | Events: emission |
release.created in events.jsonl |
Add && bash scripts/sw-release-test.sh to the test script chain.
- Task 1: Create
scripts/sw-release.shwith boilerplate, subcommand routing, help text - Task 2: Implement
release_collect_prs()— query merged PRs since last tag - Task 3: Implement
release_categorize_prs()— label/title-based PR classification - Task 4: Implement
release_determine_version()— semantic version bump logic - Task 5: Implement
release_generate_changelog()— Keep a Changelog format - Task 6: Implement
release_create()— full release flow: version bump → changelog → tag → GitHub Release - Task 7: Implement
release_check_eligible()andrelease_schedule_check()— schedule/threshold triggers - Task 8: Implement
status,plan,config,historysubcommands - Task 9: Add daemon config loading for
release_train.*keys insw-daemon.sh - Task 10: Add release train scheduling to daemon poll loop in
sw-daemon.sh - Task 11: Add
fleet_notify_release()tosw-fleet.shfor downstream notifications - Task 12: Add
releasecommand to CLI router inscripts/sw(help + route) - Task 13: Add
workflow_dispatchtrigger to.github/workflows/release.yml - Task 14: Create
scripts/sw-release-test.shwith 20+ tests - Task 15: Register
sw-release-test.shinpackage.jsontest chain
All tests use mocked binaries (gh, git) in a temp directory with NO_GITHUB=true. No real API calls. Tests cover:
-
PR collection — Mock
gh pr listreturns canned JSON; verify parsing - Categorization — Feed labeled/titled PRs; verify category assignment
-
Version math —
1.10.0→1.10.1(patch),1.11.0(minor),2.0.0(major) - Changelog format — Verify Keep a Changelog markdown structure
-
Changelog insertion — New section after
[Unreleased], existing entries preserved - Schedule logic — Mock timestamps/PR counts; verify trigger/no-trigger
- Config — Default values when missing; custom values when present
-
Event emission —
release.*events inevents.jsonlwith correct fields
Integration: npm test runs all 23 test suites including the new one.
-
shipwright release statusshows merged PRs since last release with version preview -
shipwright release plangenerates a dry-run (no side effects) -
shipwright release createexecutes full release: version bump, changelog, tag, GitHub Release - Semantic version auto-determined from PR labels/titles (breaking→major, feature→minor, fix→patch)
- Changelog generated in Keep a Changelog format with proper categorization
- GitHub Release created with tag and auto-generated notes
- Release schedule configurable via
daemon-config.json(manual,pr_count,daily,weekly) - Daemon poll loop checks release eligibility when
release_train.enabled=true - Fleet repos notified of upstream releases when configured
- CLI router dispatches
shipwright release <cmd>correctly - 20+ tests pass in
sw-release-test.shcovering version determination and changelog generation -
npm testpasses with the new test suite included - All scripts maintain Bash 3.2 compatibility
- Events emitted:
release.created,release.tagged,release.published,fleet.release_notification