Skip to content

Commit 001065d

Browse files
authored
Merge branch 'main' into emaher-feat-filter-override-schemas-prompts
2 parents 25a887b + b5a5221 commit 001065d

27 files changed

Lines changed: 1120 additions & 533 deletions
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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+
**⚠️ Do NOT trust the previous version's git tag as the lower bound, and do NOT trust the new version's git tag (if it exists) as the upper bound.** Tags in this repo have historically been created out-of-order, prematurely, or left orphaned. The only reliable approach is to enumerate merged PRs by **merge date** on `main`.
53+
54+
#### Step 4a. Establish the date range
55+
56+
```powershell
57+
# Find the previous release's tag and its commit date
58+
git tag --sort=-v:refname | Select-Object -First 5
59+
$prevTag = "v<PREV_VERSION>" # e.g. v0.2.1-alpha.0 — confirm with the team if uncertain
60+
$prevDate = git --no-pager log -1 --format="%cI" $prevTag
61+
Write-Host "Previous release tag: $prevTag at $prevDate"
62+
```
63+
64+
**Sanity check: is the tag stale?** If the tag's commit isn't on `main` HEAD or close to it, the tag may be premature/orphaned and shouldn't be used as an upper bound for the *next* release either:
65+
66+
```powershell
67+
$commitsSinceTag = (git --no-pager log --oneline "$prevTag..main" | Measure-Object -Line).Lines
68+
Write-Host "$commitsSinceTag commits on main since $prevTag"
69+
# If this is suspiciously large (50+) and you weren't expecting that, investigate
70+
# before assuming the tag represents the last *real* release.
71+
```
72+
73+
If you find a tag that exists but has no matching `## [VERSION]` entry in `CHANGELOG.md` and no GitHub Release, treat it as **orphaned** — ignore it and use the most recent tag that *does* appear in `CHANGELOG.md` as your lower bound.
74+
75+
#### Step 4b. Enumerate all merged PRs in the date range
76+
77+
This is the authoritative list. **Do not** use `git log tag..tag` as a substitute — see "Why this matters" below.
78+
79+
```powershell
80+
# Pull every PR merged on/after the previous release date.
81+
# Strip the time portion to be inclusive of release-day PRs.
82+
$sinceDate = ($prevDate -split "T")[0]
83+
gh pr list --state merged --base main --search "merged:>=$sinceDate" --limit 100 `
84+
--json number,title,mergedAt,author `
85+
--jq '.[] | "\(.mergedAt) #\(.number) [\(.author.login)] \(.title)"' `
86+
| Sort-Object
87+
```
88+
89+
Cross-check against the commit log (sanity, not source of truth — `gh pr list` is canonical):
90+
91+
```powershell
92+
git --no-pager log --merges --pretty=format:"%ci %s" "$prevTag..main"
93+
```
94+
95+
If the two lists disagree (e.g. a PR in `gh pr list` doesn't appear in `git log`), trust `gh pr list`. A PR can be missing from `git log tag..tag` because the tag was placed prematurely, the PR was rebased/squashed, or the PR landed on a branch that was later merged via another commit.
96+
97+
#### Step 4c. Categorize the PRs
98+
99+
Walk each PR and place it into one of these buckets. Use `gh pr view <N> --json title,body` to read details when the title alone isn't enough:
100+
101+
- **Features** — new user-visible capabilities
102+
- **Bug Fixes** — corrections to existing behavior
103+
- **Docs & Testing** — documentation, examples, test improvements
104+
- **Breaking Changes** — anything that requires user action (add this section only if non-empty; call it out loudly even for pre-1.0)
105+
- **Skip from changelog** — internal repo tooling (issue templates, agentic workflows, label sync, squad/agent upgrades, CI hardening that doesn't affect end users). These don't belong in a user-facing changelog. If you're unsure, **ask the user** rather than guessing.
106+
107+
#### Step 4d. Confirm the categorization with the user before writing the entry
108+
109+
Show the user the categorized list and confirm:
110+
111+
- Which PRs belong in the user-facing changelog vs. are internal-only
112+
- Whether any PR title is misleading and needs a clearer headline
113+
- Whether any change is breaking (especially CLI flag removals, schema changes, default-value changes)
114+
115+
Only after this confirmation, proceed to draft the entry using the existing changelog style (short bolded headline → plain-language explanation → PR link):
116+
117+
```markdown
118+
- **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))
119+
```
120+
121+
#### Why this matters (don't repeat past mistakes)
122+
123+
A prior release attempt used `git log v<prev>..v<new>` to compile changes and found only 1 PR — when in reality **18 PRs** had merged in the range. Root cause: the `v<new>` tag had been placed on a commit 9 days before the actual release-bump PR merged, so `git log` only saw commits up to that stale tag. The user caught the mistake by eyeballing the GitHub PR list.
124+
125+
**Lesson:** tags are not a reliable boundary in this repo. Always enumerate by `gh pr list --search "merged:>=<date>"` and cross-check against the screen the user sees on github.com/Azure/apiops-cli/pulls?q=is%3Amerged.
126+
127+
### 5. Suggest a version and let the user pick
128+
129+
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.
130+
131+
Decision rules (pre-1.0 era):
132+
133+
- **Breaking change** → bump MINOR (`0.3.0-alpha.0``0.4.0-alpha.0`)
134+
- **New feature, no breaking change** → bump MINOR (`0.3.0-alpha.0``0.4.0-alpha.0`)
135+
- **Bug fix / docs only** → bump PATCH (`0.3.0-alpha.0``0.3.1-alpha.0`)
136+
- **Iterating on an unreleased pre-release** → bump the alpha number (`0.3.0-alpha.0``0.3.0-alpha.1`)
137+
138+
Always validate the chosen version is real semver before continuing:
139+
140+
```powershell
141+
node -e "const v=process.argv[1]; const s=require('semver'); if(!s.valid(v)){process.exit(1)}; console.log(v)" <NEW_VERSION>
142+
```
143+
144+
If it returns non-zero, STOP and ask the user for a different value.
145+
146+
### 6. Update CHANGELOG.md
147+
148+
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`):
149+
150+
```markdown
151+
## [0.3.1-alpha.0] — 2026-06-25
152+
153+
### Features
154+
155+
- ...
156+
157+
### Bug Fixes
158+
159+
- ...
160+
161+
### Docs & Testing
162+
163+
- ...
164+
```
165+
166+
**Also add a compare-link footer at the bottom of the file.** CHANGELOG.md uses [Keep a Changelog](https://keepachangelog.com/) reference-style links so every `## [VERSION]` heading renders as a clickable link to the GitHub compare view between this and the previous release. The footer lives at the very bottom of the file and looks like:
167+
168+
```markdown
169+
[0.3.1-alpha.0]: https://github.com/Azure/apiops-cli/compare/v0.3.0-alpha.0...v0.3.1-alpha.0
170+
[0.3.0-alpha.0]: https://github.com/Azure/apiops-cli/compare/v0.2.1-alpha.0...v0.3.0-alpha.0
171+
...
172+
```
173+
174+
Add a new line for the new version at the **top** of the footer block (most recent first), comparing against the **immediately preceding** version's tag.
175+
176+
Verify both the workflow validator will accept the heading **and** the compare link is in place:
177+
178+
```powershell
179+
Select-String -Path CHANGELOG.md -Pattern "^## \[<NEW_VERSION>\]"
180+
Select-String -Path CHANGELOG.md -Pattern "^\[<NEW_VERSION>\]:"
181+
```
182+
183+
Both `Select-String` calls must return a hit. If the first is empty the workflow will fail; if the second is empty the version heading won't render as a link in the published changelog (a pre-existing convention violation, not a workflow failure — but still wrong).
184+
185+
### 7. Bump the version with `npm version`
186+
187+
`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.
188+
189+
```powershell
190+
# Use --no-git-tag-version so we control the commit + tag separately
191+
# Cheat sheet (run ONE of these, not all):
192+
193+
# Pre-release patch: 0.3.0-alpha.0 -> 0.3.0-alpha.1 (increments pre-release identifier)
194+
npm version prerelease --preid=alpha --no-git-tag-version
195+
196+
# Patch: 0.3.0 -> 0.3.1 (only if you've dropped the pre-release suffix)
197+
npm version patch --no-git-tag-version
198+
199+
# Minor (pre-release): 0.3.0-alpha.0 -> 0.4.0-alpha.0
200+
npm version preminor --preid=alpha --no-git-tag-version
201+
202+
# Major (pre-release): 0.3.0-alpha.0 -> 1.0.0-alpha.0
203+
npm version premajor --preid=alpha --no-git-tag-version
204+
205+
# Explicit (recommended when you already chose the version in Step 5):
206+
npm version <NEW_VERSION> --no-git-tag-version
207+
```
208+
209+
Verify the result:
210+
211+
```powershell
212+
node -p "require('./package.json').version"
213+
# Must equal <NEW_VERSION>
214+
```
215+
216+
### 8. Commit and open the pull request
217+
218+
```powershell
219+
git add package.json package-lock.json CHANGELOG.md
220+
221+
# Use -F with a temp file so newlines render correctly (see CONTRIBUTING.md)
222+
$msg = @"
223+
chore: release v<NEW_VERSION>
224+
225+
Updates package.json and CHANGELOG.md for the v<NEW_VERSION> release.
226+
The squad-release workflow will create the git tag and GitHub Release
227+
once this PR merges to main.
228+
229+
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
230+
"@
231+
$msg | Out-File -Encoding utf8 commit-msg.txt
232+
git commit -F commit-msg.txt
233+
Remove-Item commit-msg.txt
234+
235+
git push -u origin HEAD
236+
```
237+
238+
Then open the PR. Use the `create_pull_request` tool (preferred — renders a card in the UI) with:
239+
240+
- **Title:** `chore: release v<NEW_VERSION>`
241+
- **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."
242+
243+
### 9. After merge
244+
245+
The squad-release workflow (`.github/workflows/squad-release.yml`) will:
246+
247+
1. Validate the version is present in `CHANGELOG.md`
248+
2. Create the `v<NEW_VERSION>` git tag
249+
3. Create the matching GitHub Release
250+
251+
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.
252+
253+
## Pitfalls
254+
255+
- **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.
256+
- **Don't reformat unrelated changelog entries.** Limit the diff to the new section + the version bump.
257+
- **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.
258+
- **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.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Bug Report
2+
description: Report a bug with apiops CLI
3+
labels: ["bug"]
4+
body:
5+
- type: input
6+
id: command
7+
attributes:
8+
label: Command that triggered the bug
9+
description: Which CLI command were you running?
10+
placeholder: "apiops init"
11+
validations:
12+
required: true
13+
14+
- type: textarea
15+
id: expected-behavior
16+
attributes:
17+
label: Expected behavior
18+
description: What did you expect to happen?
19+
validations:
20+
required: true
21+
22+
- type: textarea
23+
id: actual-behavior
24+
attributes:
25+
label: Actual behavior
26+
description: What actually happened?
27+
validations:
28+
required: true
29+
30+
- type: input
31+
id: version
32+
attributes:
33+
label: apiops CLI version
34+
description: "Run `apiops --version` (or `npm list -g @peterhauge/apiops-cli`) to find the installed version."
35+
validations:
36+
required: true
37+
38+
- type: textarea
39+
id: environment
40+
attributes:
41+
label: Environment details
42+
description: "OS, Node.js version, shell, and any other relevant info."
43+
validations:
44+
required: true
45+
46+
- type: dropdown
47+
id: ci-cd
48+
attributes:
49+
label: CI/CD environment
50+
description: If this bug occurs in a CI/CD pipeline, which platform are you using?
51+
options:
52+
- "N/A (running locally)"
53+
- "GitHub Actions"
54+
- "Azure DevOps"
55+
- "Other"
56+
validations:
57+
required: false
58+
59+
- type: dropdown
60+
id: blocking
61+
attributes:
62+
label: Is this bug blocking you?
63+
options:
64+
- "Yes"
65+
- "No"
66+
validations:
67+
required: false
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Feature Request
2+
description: Suggest a new feature or enhancement
3+
labels: ["type:feature"]
4+
body:
5+
- type: textarea
6+
id: problem
7+
attributes:
8+
label: Problem or use case
9+
description: What problem does this solve?
10+
validations:
11+
required: true
12+
13+
- type: textarea
14+
id: solution
15+
attributes:
16+
label: Proposed solution
17+
description: Describe the solution you'd like
18+
validations:
19+
required: true
20+
21+
- type: input
22+
id: affected-command
23+
attributes:
24+
label: Affected command
25+
description: Which CLI command does this feature relate to (if any)?
26+
placeholder: "apiops extract"
27+
validations:
28+
required: false
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Question
2+
description: Ask a usage question or request clarification
3+
labels: ["question"]
4+
body:
5+
- type: textarea
6+
id: question
7+
attributes:
8+
label: Question
9+
description: What would you like to know?
10+
validations:
11+
required: true
12+
13+
- type: textarea
14+
id: context
15+
attributes:
16+
label: Context
17+
description: Any additional context that might help us answer
18+
validations:
19+
required: false

0 commit comments

Comments
 (0)