|
| 1 | +# Skill: Release apiops-cli Version |
| 2 | + |
| 3 | +**Confidence:** high |
| 4 | +**Scope:** Any agent (or human) cutting a new release of `@peterhauge/apiops-cli` |
| 5 | + |
| 6 | +## What |
| 7 | + |
| 8 | +End-to-end checklist for releasing a new version of apiops-cli. Walks through cleanliness checks, branch creation, changelog authoring, version bumping with `npm version`, and PR creation. |
| 9 | + |
| 10 | +## Why |
| 11 | + |
| 12 | +The release workflow (`.github/workflows/squad-release.yml`) **fails the build** if `package.json` `version` is not headed in `CHANGELOG.md` as `## [VERSION]`. Releases that skip the changelog never tag. This skill keeps version, changelog, and tag in lockstep so consumers can always answer "what changed since the version I have installed?" by reading one file. |
| 13 | + |
| 14 | +## How |
| 15 | + |
| 16 | +Follow these steps in order. Do not skip the cleanliness check — switching branches with a dirty tree loses work. |
| 17 | + |
| 18 | +### 1. Confirm the working tree is clean |
| 19 | + |
| 20 | +```powershell |
| 21 | +git status --short |
| 22 | +``` |
| 23 | + |
| 24 | +- If output is empty (or only contains `??` untracked files you've verified are safe to leave behind), you can proceed. |
| 25 | +- If there are modified or staged tracked files, **STOP**. Tell the user: |
| 26 | + > "There are uncommitted changes in the working tree. Please commit, stash, or discard them before starting a release. Run `git status` to see what's pending." |
| 27 | +
|
| 28 | +Do not run `git stash`, `git reset`, or `git checkout -- .` on the user's behalf. |
| 29 | + |
| 30 | +### 2. Switch to main and sync |
| 31 | + |
| 32 | +```powershell |
| 33 | +git checkout main |
| 34 | +git pull --ff-only origin main |
| 35 | +``` |
| 36 | + |
| 37 | +If the pull fails (non-fast-forward), STOP and ask the user how to reconcile. |
| 38 | + |
| 39 | +### 3. Create the release branch |
| 40 | + |
| 41 | +Use a predictable name so reviewers know it's a release PR: |
| 42 | + |
| 43 | +```powershell |
| 44 | +git checkout -b release/v<NEW_VERSION> |
| 45 | +# e.g. git checkout -b release/v0.3.1-alpha.0 |
| 46 | +``` |
| 47 | + |
| 48 | +Don't commit the version bump yet — the changelog has to be written first (Step 6) so it lands in the same commit. |
| 49 | + |
| 50 | +### 4. Compile the list of changes |
| 51 | + |
| 52 | +Find the previous release tag, then walk every merged PR since: |
| 53 | + |
| 54 | +```powershell |
| 55 | +# Most recent tag (sorted by semver) |
| 56 | +git tag --sort=-v:refname | Select-Object -First 1 |
| 57 | +
|
| 58 | +# Commits since that tag |
| 59 | +git log <previous-tag>..main --pretty=format:"%h %s" --no-merges |
| 60 | +
|
| 61 | +# PRs merged since that tag (more useful for changelog entries) |
| 62 | +gh pr list --state merged --base main --limit 100 --json number,title,mergedAt,url ` |
| 63 | + --jq '[.[] | select(.mergedAt > "<previous-tag-date>")] | .[] | "#\(.number) \(.title)"' |
| 64 | +``` |
| 65 | + |
| 66 | +Group the entries into the sections used by `CHANGELOG.md`: |
| 67 | + |
| 68 | +- **Features** — new user-visible capabilities |
| 69 | +- **Bug Fixes** — corrections to existing behavior |
| 70 | +- **Docs & Testing** — documentation, examples, test improvements |
| 71 | +- **Breaking Changes** — anything that requires user action (add this section only if non-empty; call it out loudly) |
| 72 | + |
| 73 | +Use the existing changelog as a style guide — short bolded headline, then plain-language explanation, then PR link(s): |
| 74 | + |
| 75 | +```markdown |
| 76 | +- **Token substitution in publish pipelines** — `{#[TOKEN_NAME]#}` placeholders are now resolved during publish, matching APIOps Toolkit behavior ([#127](https://github.com/Azure/apiops-cli/pull/127)) |
| 77 | +``` |
| 78 | + |
| 79 | +### 5. Suggest a version and let the user pick |
| 80 | + |
| 81 | +Based on the compiled changes, suggest the next version following SemVer with the project's `-alpha.N` pre-release convention. Show the user a small menu via `ask_user` — always include an "other" option for a custom version. |
| 82 | + |
| 83 | +Decision rules (pre-1.0 era): |
| 84 | + |
| 85 | +- **Breaking change** → bump MINOR (`0.3.0-alpha.0` → `0.4.0-alpha.0`) |
| 86 | +- **New feature, no breaking change** → bump MINOR (`0.3.0-alpha.0` → `0.4.0-alpha.0`) |
| 87 | +- **Bug fix / docs only** → bump PATCH (`0.3.0-alpha.0` → `0.3.1-alpha.0`) |
| 88 | +- **Iterating on an unreleased pre-release** → bump the alpha number (`0.3.0-alpha.0` → `0.3.0-alpha.1`) |
| 89 | + |
| 90 | +Always validate the chosen version is real semver before continuing: |
| 91 | + |
| 92 | +```powershell |
| 93 | +node -e "const v=process.argv[1]; const s=require('semver'); if(!s.valid(v)){process.exit(1)}; console.log(v)" <NEW_VERSION> |
| 94 | +``` |
| 95 | + |
| 96 | +If it returns non-zero, STOP and ask the user for a different value. |
| 97 | + |
| 98 | +### 6. Update CHANGELOG.md |
| 99 | + |
| 100 | +Insert a new section directly under the `# Changelog` header (above the previous most-recent entry). Use ISO date, exactly match the version format used by the release workflow's grep (`## [VERSION] — YYYY-MM-DD`): |
| 101 | + |
| 102 | +```markdown |
| 103 | +## [0.3.1-alpha.0] — 2026-06-25 |
| 104 | + |
| 105 | +### Features |
| 106 | + |
| 107 | +- ... |
| 108 | + |
| 109 | +### Bug Fixes |
| 110 | + |
| 111 | +- ... |
| 112 | + |
| 113 | +### Docs & Testing |
| 114 | + |
| 115 | +- ... |
| 116 | +``` |
| 117 | + |
| 118 | +Verify the workflow validator will accept it: |
| 119 | + |
| 120 | +```powershell |
| 121 | +Select-String -Path CHANGELOG.md -Pattern "^## \[<NEW_VERSION>\]" |
| 122 | +``` |
| 123 | + |
| 124 | +If `Select-String` returns nothing, the header is malformed — fix the brackets, spacing, or em-dash. |
| 125 | + |
| 126 | +### 7. Bump the version with `npm version` |
| 127 | + |
| 128 | +`npm version` updates `package.json` and `package-lock.json` atomically. By default it also creates a git tag and commit — **disable that** because the release workflow creates the tag for us once the PR merges. |
| 129 | + |
| 130 | +```powershell |
| 131 | +# Use --no-git-tag-version so we control the commit + tag separately |
| 132 | +# Cheat sheet (run ONE of these, not all): |
| 133 | +
|
| 134 | +# Pre-release patch: 0.3.0-alpha.0 -> 0.3.0-alpha.1 (increments pre-release identifier) |
| 135 | +npm version prerelease --preid=alpha --no-git-tag-version |
| 136 | +
|
| 137 | +# Patch: 0.3.0 -> 0.3.1 (only if you've dropped the pre-release suffix) |
| 138 | +npm version patch --no-git-tag-version |
| 139 | +
|
| 140 | +# Minor (pre-release): 0.3.0-alpha.0 -> 0.4.0-alpha.0 |
| 141 | +npm version preminor --preid=alpha --no-git-tag-version |
| 142 | +
|
| 143 | +# Major (pre-release): 0.3.0-alpha.0 -> 1.0.0-alpha.0 |
| 144 | +npm version premajor --preid=alpha --no-git-tag-version |
| 145 | +
|
| 146 | +# Explicit (recommended when you already chose the version in Step 5): |
| 147 | +npm version <NEW_VERSION> --no-git-tag-version |
| 148 | +``` |
| 149 | + |
| 150 | +Verify the result: |
| 151 | + |
| 152 | +```powershell |
| 153 | +node -p "require('./package.json').version" |
| 154 | +# Must equal <NEW_VERSION> |
| 155 | +``` |
| 156 | + |
| 157 | +### 8. Commit and open the pull request |
| 158 | + |
| 159 | +```powershell |
| 160 | +git add package.json package-lock.json CHANGELOG.md |
| 161 | +
|
| 162 | +# Use -F with a temp file so newlines render correctly (see CONTRIBUTING.md) |
| 163 | +$msg = @" |
| 164 | +chore: release v<NEW_VERSION> |
| 165 | +
|
| 166 | +Updates package.json and CHANGELOG.md for the v<NEW_VERSION> release. |
| 167 | +The squad-release workflow will create the git tag and GitHub Release |
| 168 | +once this PR merges to main. |
| 169 | +
|
| 170 | +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
| 171 | +"@ |
| 172 | +$msg | Out-File -Encoding utf8 commit-msg.txt |
| 173 | +git commit -F commit-msg.txt |
| 174 | +Remove-Item commit-msg.txt |
| 175 | +
|
| 176 | +git push -u origin HEAD |
| 177 | +``` |
| 178 | + |
| 179 | +Then open the PR. Use the `create_pull_request` tool (preferred — renders a card in the UI) with: |
| 180 | + |
| 181 | +- **Title:** `chore: release v<NEW_VERSION>` |
| 182 | +- **Body:** Paste the new CHANGELOG section verbatim, plus a line: "Once merged, `.github/workflows/squad-release.yml` will tag `v<NEW_VERSION>` and create the GitHub Release." |
| 183 | + |
| 184 | +### 9. After merge |
| 185 | + |
| 186 | +The squad-release workflow (`.github/workflows/squad-release.yml`) will: |
| 187 | + |
| 188 | +1. Validate the version is present in `CHANGELOG.md` |
| 189 | +2. Create the `v<NEW_VERSION>` git tag |
| 190 | +3. Create the matching GitHub Release |
| 191 | + |
| 192 | +No manual `git tag` or `gh release create` is needed. If the workflow fails, read the logs — the most common cause is a typo in the `## [VERSION]` header that fails the grep validator. |
| 193 | + |
| 194 | +## Pitfalls |
| 195 | + |
| 196 | +- **Don't run `npm version` without `--no-git-tag-version`.** It will create an unsigned tag on your release branch that conflicts with the one the workflow makes. |
| 197 | +- **Don't reformat unrelated changelog entries.** Limit the diff to the new section + the version bump. |
| 198 | +- **Don't squash-merge the release commit into something else.** It needs to land on `main` as a recognizable `chore: release v...` commit so reviewers can find it. |
| 199 | +- **Pre-1.0 breaking changes still deserve a callout.** Add a `### Breaking Changes` section even if SemVer technically allows the change in a MINOR bump. |
0 commit comments