Skip to content
Back to skills

Patch Deployer

ASecurity

Use when origin/dev is ahead of origin/main and the cumulative bump is patch-only — drafts a curated release PR (no Features section), runs scripts/ship.sh, and reports the new release. Refuses to run for minor / major bumps; those still go through ship-release manually.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
toolsgobashgitapi

Works with

  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add normalled/apijack --skill patch-deployer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Patch Deployer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Patch Deployer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/normalled-patch-deployer/badge)](https://www.skillsdirectory.com/skills/normalled-patch-deployer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: patch-deployer
description: Use when origin/dev is ahead of origin/main and the cumulative bump is patch-only — drafts a curated release PR (no Features section), runs scripts/ship.sh, and reports the new release. Refuses to run for minor / major bumps; those still go through ship-release manually.
---

# Patch Deployer

Phase 5 of the apijack issue workflow, scoped to **patch releases only**. Runs the same pipeline as `ship-release` but constrained to bumps where every commit is a `fix:` / `chore:` / `docs:` / `test:` / `refactor:` / `ci:` (no `feat:`, no `BREAKING CHANGE`).

This is the "trickle out the small stuff" automation. Anything bigger keeps the human in the loop via `ship-release`.

## When to Use

- Cron tick fires
- `find-patch-deploy-candidate.sh` returns 0 (origin/dev ahead of origin/main, patch-only)

If the bump is `minor` or `major`, this skill must abort and leave the work for a human. **Do not lower the bump level to fit.**

## Steps

### 1. Verify the gate (defense-in-depth — preflight already checked)

```bash
.claude/skills/patch-deployer/scripts/find-patch-deploy-candidate.sh
```

Expected: prints the commit count, exits 0. If it exits non-zero, STOP — something changed between preflight and now (e.g., a `feat:` commit landed during the run).

### 2. Verify local working state and gather commits

```bash
COMMITS_FILE=$(./scripts/gather-release-commits.sh)
cat "$COMMITS_FILE"
```

`gather-release-commits.sh` verifies the branch is `dev` and the working tree is clean, runs `git fetch origin`, and writes `origin/main..HEAD` (default format `%h %s`) to `/tmp/apijack-ship-commits.txt`. The printed path is the canonical commit list for the rest of this skill — step 4 reads from it instead of re-running `git log`.

If the script exits non-zero, STOP. Also STOP if local HEAD ≠ origin/dev (the cron should not silently switch branches or stash user WIP).

### 3. Categorize commits

```bash
cat "$COMMITS_FILE"   # written by gather-release-commits.sh in step 2
```

Group into buckets, **skipping merge commits and any prior `chore(release):` bump**:

| Bucket | Matches | PR section |
|---|---|---|
| Fixes | `fix(...)`, `fix: ...` | `## 🐛 Fixes` |
| Internal | `chore:`, `docs:`, `test:`, `refactor:`, `ci:` | `## 🧹 Internal` |

There must be **zero** entries in a "Features" or "Changed" bucket — the gate would have rejected the run otherwise. If you find one, abort.

For each fix commit, look up the PR number (`(#NN)` in the subject, or `gh pr list --state merged --search "<subject>"`).

### 3a. Collect closing references

```bash
./scripts/collect-closes-refs.sh
```

Prints `Closes #N` lines for every issue closed by the issue PRs in this range. These MUST be placed at the top of the release PR body (step 4) — without them, GitHub will not auto-close the issues when dev → main merges, because the originating PRs landed on `dev` (a non-default branch). Empty output is fine if no tracked issues were resolved.

### 4. Draft the PR body

Use the `Write` tool — never inline in a heredoc — to avoid backtick-escaping. Write to `.claude-jobs/release-bodies/dev-to-main.md`.

#### Title format

`vX.Y.Z: <short comma-separated highlights>` — pick the 1–3 most user-visible fixes. Example: `v1.9.1: plugins-loader array filter, post-review path traversal hardening`.

#### Body template

```markdown
Closes #N
Closes #M
...

<1-sentence thesis describing the focus of the patch — usually "Bug fixes and internal cleanup since vX.Y.Z-1." or similar.>

## 🐛 Fixes

- **<fix summary>** (#NN) — <what was broken, what now works>.
- ...

## 🧹 Internal

- <one line if any non-fix commits landed; omit the section otherwise>.

---

Shipped via `scripts/ship.sh`.
```

Concision rules from `ship-release`:
- Every fix bullet ends in `(#NN)`
- One bullet per fix (combine related commits)
- Internal section is one line — `"misc CI and skill tweaks"` beats listing every chore
- If the body is longer than ~15 lines, cut

### 5. Create / update the PR

```bash
.claude/skills/patch-deployer/scripts/upsert-release-pr.sh \
    "<title>" \
    .claude-jobs/release-bodies/dev-to-main.md
```

The script edits the open `dev → main` PR if one exists, otherwise pushes `dev` and creates it. PR number is printed on stdout.

### 6. Run ship.sh

```bash
./scripts/ship.sh
```

`ship.sh` will:
1. Bump the patch version (`chore(release): vX.Y.Z` commit on dev, pushed)
2. Find the existing PR (won't overwrite the curated title/body)
3. Wait for CI
4. Merge via `gh pr merge --merge --admin`
5. Watch the publish workflow create the release + publish to npm
6. Sync main and rebase dev

If ship.sh exits non-zero, leave the PR in place for a human and stop.

### 7. Report

When ship.sh succeeds, report:
- New tag (`vX.Y.Z`)
- Release URL (`https://github.com/normalled/apijack/releases/tag/vX.Y.Z`)
- npm package URL

The publish workflow's release-notes pipeline (post-#54) uses the merged PR body verbatim, so the GitHub release will match what was drafted in step 4.

## Red Flags — Do Not Ship

- "There's a `feat:` commit but it's small" → STOP, it's a minor bump, hand to `ship-release`
- "Working tree is slightly dirty (untracked file)" → STOP, the user has WIP
- "CI looks like it'll pass, just merge" → STOP, ship.sh waits on CI for a reason
- "Existing PR has a body but it's bland — overwrite without asking" → edit the PR, don't `gh pr close && pr create`; preserve any reviewer comments

## Output

Either: a published patch release with the curated body shipped to GitHub + npm, or: STOP at the first failed gate without modifying anything.

Files in this skill

  • SKILL.md5.5 KB
  • scripts/find-patch-deploy-candidate.sh1.6 KB
  • scripts/upsert-release-pr.sh1.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.