mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-05-27 02:09:14 +02:00
Squad Test
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
---
|
||||
name: "release-process"
|
||||
description: "Step-by-step release checklist for Squad — prevents v0.8.22-style disasters"
|
||||
domain: "release-management"
|
||||
confidence: "high"
|
||||
source: "team-decision"
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
This is the **definitive release runbook** for Squad. Born from the v0.8.22 release disaster (4-part semver mangled by npm, draft release never triggered publish, wrong NPM_TOKEN type, 6+ hours of broken `latest` dist-tag).
|
||||
|
||||
**Rule:** No agent releases Squad without following this checklist. No exceptions. No improvisation.
|
||||
|
||||
---
|
||||
|
||||
## Pre-Release Validation
|
||||
|
||||
Before starting ANY release work, validate the following:
|
||||
|
||||
### 1. Version Number Validation
|
||||
|
||||
**Rule:** Only 3-part semver (major.minor.patch) or prerelease (major.minor.patch-tag.N) are valid. 4-part versions (0.8.21.4) are NOT valid semver and npm will mangle them.
|
||||
|
||||
```bash
|
||||
# Check version is valid semver
|
||||
node -p "require('semver').valid('0.8.22')"
|
||||
# Output: '0.8.22' = valid
|
||||
# Output: null = INVALID, STOP
|
||||
|
||||
# For prerelease versions
|
||||
node -p "require('semver').valid('0.8.23-preview.1')"
|
||||
# Output: '0.8.23-preview.1' = valid
|
||||
```
|
||||
|
||||
**If `semver.valid()` returns `null`:** STOP. Fix the version. Do NOT proceed.
|
||||
|
||||
### 2. NPM_TOKEN Verification
|
||||
|
||||
**Rule:** NPM_TOKEN must be an **Automation token** (no 2FA required). User tokens with 2FA will fail in CI with EOTP errors.
|
||||
|
||||
```bash
|
||||
# Check token type (requires npm CLI authenticated)
|
||||
npm token list
|
||||
```
|
||||
|
||||
Look for:
|
||||
- ✅ `read-write` tokens with NO 2FA requirement = Automation token (correct)
|
||||
- ❌ Tokens requiring OTP = User token (WRONG, will fail in CI)
|
||||
|
||||
**How to create an Automation token:**
|
||||
1. Go to npmjs.com → Settings → Access Tokens
|
||||
2. Click "Generate New Token"
|
||||
3. Select **"Automation"** (NOT "Publish")
|
||||
4. Copy token and save as GitHub secret: `NPM_TOKEN`
|
||||
|
||||
**If using a User token:** STOP. Create an Automation token first.
|
||||
|
||||
### 3. Branch and Tag State
|
||||
|
||||
**Rule:** Release from `main` branch. Ensure clean state, no uncommitted changes, latest from origin.
|
||||
|
||||
```bash
|
||||
# Ensure on main and clean
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git status # Should show: "nothing to commit, working tree clean"
|
||||
|
||||
# Check tag doesn't already exist
|
||||
git tag -l "v0.8.22"
|
||||
# Output should be EMPTY. If tag exists, release already done or collision.
|
||||
```
|
||||
|
||||
**If tag exists:** STOP. Either release was already done, or there's a collision. Investigate before proceeding.
|
||||
|
||||
### 4. Disable bump-build.mjs
|
||||
|
||||
**Rule:** `bump-build.mjs` is for dev builds ONLY. It must NOT run during release builds (it increments build numbers, creating 4-part versions).
|
||||
|
||||
```bash
|
||||
# Set env var to skip bump-build.mjs
|
||||
export SKIP_BUILD_BUMP=1
|
||||
|
||||
# Verify it's set
|
||||
echo $SKIP_BUILD_BUMP
|
||||
# Output: 1
|
||||
```
|
||||
|
||||
**For Windows PowerShell:**
|
||||
```powershell
|
||||
$env:SKIP_BUILD_BUMP = "1"
|
||||
```
|
||||
|
||||
**If not set:** `bump-build.mjs` will run and mutate versions. This causes disasters (see v0.8.22).
|
||||
|
||||
---
|
||||
|
||||
## Release Workflow
|
||||
|
||||
### Step 1: Version Bump
|
||||
|
||||
Update version in all 3 package.json files (root + both workspaces) in lockstep.
|
||||
|
||||
```bash
|
||||
# Set target version (no 'v' prefix)
|
||||
VERSION="0.8.22"
|
||||
|
||||
# Validate it's valid semver BEFORE proceeding
|
||||
node -p "require('semver').valid('$VERSION')"
|
||||
# Must output the version string, NOT null
|
||||
|
||||
# Update all 3 package.json files
|
||||
npm version $VERSION --workspaces --include-workspace-root --no-git-tag-version
|
||||
|
||||
# Verify all 3 match
|
||||
grep '"version"' package.json packages/squad-sdk/package.json packages/squad-cli/package.json
|
||||
# All 3 should show: "version": "0.8.22"
|
||||
```
|
||||
|
||||
**Checkpoint:** All 3 package.json files have identical versions. Run `semver.valid()` one more time to be sure.
|
||||
|
||||
### Step 2: Commit and Tag
|
||||
|
||||
```bash
|
||||
# Commit version bump
|
||||
git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json
|
||||
git commit -m "chore: bump version to $VERSION
|
||||
|
||||
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
|
||||
|
||||
# Create tag (with 'v' prefix)
|
||||
git tag -a "v$VERSION" -m "Release v$VERSION"
|
||||
|
||||
# Push commit and tag
|
||||
git push origin main
|
||||
git push origin "v$VERSION"
|
||||
```
|
||||
|
||||
**Checkpoint:** Tag created and pushed. Verify with `git tag -l "v$VERSION"`.
|
||||
|
||||
### Step 3: Create GitHub Release
|
||||
|
||||
**CRITICAL:** Release must be **published**, NOT draft. Draft releases don't trigger `publish.yml` workflow.
|
||||
|
||||
```bash
|
||||
# Create GitHub Release (NOT draft)
|
||||
gh release create "v$VERSION" \
|
||||
--title "v$VERSION" \
|
||||
--notes "Release notes go here" \
|
||||
--latest
|
||||
|
||||
# Verify release is PUBLISHED (not draft)
|
||||
gh release view "v$VERSION"
|
||||
# Output should NOT contain "(draft)"
|
||||
```
|
||||
|
||||
**If output contains `(draft)`:** STOP. Delete the release and recreate without `--draft` flag.
|
||||
|
||||
```bash
|
||||
# If you accidentally created a draft, fix it:
|
||||
gh release edit "v$VERSION" --draft=false
|
||||
```
|
||||
|
||||
**Checkpoint:** Release is published (NOT draft). The `release: published` event fired and triggered `publish.yml`.
|
||||
|
||||
### Step 4: Monitor Workflow
|
||||
|
||||
The `publish.yml` workflow should start automatically within 10 seconds of release creation.
|
||||
|
||||
```bash
|
||||
# Watch workflow runs
|
||||
gh run list --workflow=publish.yml --limit 1
|
||||
|
||||
# Get detailed status
|
||||
gh run view --log
|
||||
```
|
||||
|
||||
**Expected flow:**
|
||||
1. `publish-sdk` job runs → publishes `@bradygaster/squad-sdk`
|
||||
2. Verify step runs with retry loop (up to 5 attempts, 15s interval) to confirm SDK on npm registry
|
||||
3. `publish-cli` job runs → publishes `@bradygaster/squad-cli`
|
||||
4. Verify step runs with retry loop to confirm CLI on npm registry
|
||||
|
||||
**If workflow fails:** Check the logs. Common issues:
|
||||
- EOTP error = wrong NPM_TOKEN type (use Automation token)
|
||||
- Verify step timeout = npm propagation delay (retry loop should handle this, but propagation can take up to 2 minutes in rare cases)
|
||||
- Version mismatch = package.json version doesn't match tag
|
||||
|
||||
**Checkpoint:** Both jobs succeeded. Workflow shows green checkmarks.
|
||||
|
||||
### Step 5: Verify npm Publication
|
||||
|
||||
Manually verify both packages are on npm with correct `latest` dist-tag.
|
||||
|
||||
```bash
|
||||
# Check SDK
|
||||
npm view @bradygaster/squad-sdk version
|
||||
# Output: 0.8.22
|
||||
|
||||
npm dist-tag ls @bradygaster/squad-sdk
|
||||
# Output should show: latest: 0.8.22
|
||||
|
||||
# Check CLI
|
||||
npm view @bradygaster/squad-cli version
|
||||
# Output: 0.8.22
|
||||
|
||||
npm dist-tag ls @bradygaster/squad-cli
|
||||
# Output should show: latest: 0.8.22
|
||||
```
|
||||
|
||||
**If versions don't match:** Something went wrong. Check workflow logs. DO NOT proceed with GitHub Release announcement until npm is correct.
|
||||
|
||||
**Checkpoint:** Both packages show correct version. `latest` dist-tags point to the new version.
|
||||
|
||||
### Step 6: Test Installation
|
||||
|
||||
Verify packages can be installed from npm (real-world smoke test).
|
||||
|
||||
```bash
|
||||
# Create temp directory
|
||||
mkdir /tmp/squad-release-test && cd /tmp/squad-release-test
|
||||
|
||||
# Test SDK installation
|
||||
npm init -y
|
||||
npm install @bradygaster/squad-sdk
|
||||
node -p "require('@bradygaster/squad-sdk/package.json').version"
|
||||
# Output: 0.8.22
|
||||
|
||||
# Test CLI installation
|
||||
npm install -g @bradygaster/squad-cli
|
||||
squad --version
|
||||
# Output: 0.8.22
|
||||
|
||||
# Cleanup
|
||||
cd -
|
||||
rm -rf /tmp/squad-release-test
|
||||
```
|
||||
|
||||
**If installation fails:** npm registry issue or package metadata corruption. DO NOT announce release until this works.
|
||||
|
||||
**Checkpoint:** Both packages install cleanly. Versions match.
|
||||
|
||||
### Step 7: Sync dev to Next Preview
|
||||
|
||||
After main release, sync dev to the next preview version.
|
||||
|
||||
```bash
|
||||
# Checkout dev
|
||||
git checkout dev
|
||||
git pull origin dev
|
||||
|
||||
# Bump to next preview version (e.g., 0.8.23-preview.1)
|
||||
NEXT_VERSION="0.8.23-preview.1"
|
||||
|
||||
# Validate semver
|
||||
node -p "require('semver').valid('$NEXT_VERSION')"
|
||||
# Must output the version string, NOT null
|
||||
|
||||
# Update all 3 package.json files
|
||||
npm version $NEXT_VERSION --workspaces --include-workspace-root --no-git-tag-version
|
||||
|
||||
# Commit
|
||||
git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json
|
||||
git commit -m "chore: bump dev to $NEXT_VERSION
|
||||
|
||||
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
|
||||
|
||||
# Push
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
**Checkpoint:** dev branch now shows next preview version. Future dev builds will publish to `@preview` dist-tag.
|
||||
|
||||
---
|
||||
|
||||
## Manual Publish (Fallback)
|
||||
|
||||
If `publish.yml` workflow fails or needs to be bypassed, use `workflow_dispatch` to manually trigger publish.
|
||||
|
||||
```bash
|
||||
# Trigger manual publish
|
||||
gh workflow run publish.yml -f version="0.8.22"
|
||||
|
||||
# Monitor the run
|
||||
gh run watch
|
||||
```
|
||||
|
||||
**Rule:** Only use this if automated publish failed. Always investigate why automation failed and fix it for next release.
|
||||
|
||||
---
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If a release is broken and needs to be rolled back:
|
||||
|
||||
### 1. Unpublish from npm (Nuclear Option)
|
||||
|
||||
**WARNING:** npm unpublish is time-limited (24 hours) and leaves the version slot burned. Only use if version is critically broken.
|
||||
|
||||
```bash
|
||||
# Unpublish (requires npm owner privileges)
|
||||
npm unpublish @bradygaster/squad-sdk@0.8.22
|
||||
npm unpublish @bradygaster/squad-cli@0.8.22
|
||||
```
|
||||
|
||||
### 2. Deprecate on npm (Preferred)
|
||||
|
||||
**Preferred approach:** Mark version as deprecated, publish a hotfix.
|
||||
|
||||
```bash
|
||||
# Deprecate broken version
|
||||
npm deprecate @bradygaster/squad-sdk@0.8.22 "Broken release, use 0.8.22.1 instead"
|
||||
npm deprecate @bradygaster/squad-cli@0.8.22 "Broken release, use 0.8.22.1 instead"
|
||||
|
||||
# Publish hotfix version
|
||||
# (Follow this runbook with version 0.8.22.1)
|
||||
```
|
||||
|
||||
### 3. Delete GitHub Release and Tag
|
||||
|
||||
```bash
|
||||
# Delete GitHub Release
|
||||
gh release delete "v0.8.22" --yes
|
||||
|
||||
# Delete tag locally and remotely
|
||||
git tag -d "v0.8.22"
|
||||
git push origin --delete "v0.8.22"
|
||||
```
|
||||
|
||||
### 4. Revert Commit on main
|
||||
|
||||
```bash
|
||||
# Revert version bump commit
|
||||
git checkout main
|
||||
git revert HEAD
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Checkpoint:** Tag and release deleted. main branch reverted. npm packages deprecated or unpublished.
|
||||
|
||||
---
|
||||
|
||||
## Common Failure Modes
|
||||
|
||||
### EOTP Error (npm OTP Required)
|
||||
|
||||
**Symptom:** Workflow fails with `EOTP` error.
|
||||
**Root cause:** NPM_TOKEN is a User token with 2FA enabled. CI can't provide OTP.
|
||||
**Fix:** Replace NPM_TOKEN with an Automation token (no 2FA). See "NPM_TOKEN Verification" above.
|
||||
|
||||
### Verify Step 404 (npm Propagation Delay)
|
||||
|
||||
**Symptom:** Verify step fails with 404 even though publish succeeded.
|
||||
**Root cause:** npm registry propagation delay (5-30 seconds).
|
||||
**Fix:** Verify step now has retry loop (5 attempts, 15s interval). Should auto-resolve. If not, wait 2 minutes and re-run workflow.
|
||||
|
||||
### Version Mismatch (package.json ≠ tag)
|
||||
|
||||
**Symptom:** Verify step fails with "Package version (X) does not match target version (Y)".
|
||||
**Root cause:** package.json version doesn't match the tag version.
|
||||
**Fix:** Ensure all 3 package.json files were updated in Step 1. Re-run `npm version` if needed.
|
||||
|
||||
### 4-Part Version Mangled by npm
|
||||
|
||||
**Symptom:** Published version on npm doesn't match package.json (e.g., 0.8.21.4 became 0.8.2-1.4).
|
||||
**Root cause:** 4-part versions are NOT valid semver. npm's parser misinterprets them.
|
||||
**Fix:** NEVER use 4-part versions. Only 3-part (0.8.22) or prerelease (0.8.23-preview.1). Run `semver.valid()` before ANY commit.
|
||||
|
||||
### Draft Release Didn't Trigger Workflow
|
||||
|
||||
**Symptom:** Release created but `publish.yml` never ran.
|
||||
**Root cause:** Release was created as a draft. Draft releases don't emit `release: published` event.
|
||||
**Fix:** Edit release and change to published: `gh release edit "v$VERSION" --draft=false`. Workflow should trigger immediately.
|
||||
|
||||
---
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before starting ANY release, confirm:
|
||||
|
||||
- [ ] Version is valid semver: `node -p "require('semver').valid('VERSION')"` returns the version string (NOT null)
|
||||
- [ ] NPM_TOKEN is an Automation token (no 2FA): `npm token list` shows `read-write` without OTP requirement
|
||||
- [ ] Branch is clean: `git status` shows "nothing to commit, working tree clean"
|
||||
- [ ] Tag doesn't exist: `git tag -l "vVERSION"` returns empty
|
||||
- [ ] `SKIP_BUILD_BUMP=1` is set: `echo $SKIP_BUILD_BUMP` returns `1`
|
||||
|
||||
Before creating GitHub Release:
|
||||
|
||||
- [ ] All 3 package.json files have matching versions: `grep '"version"' package.json packages/*/package.json`
|
||||
- [ ] Commit is pushed: `git log origin/main..main` returns empty
|
||||
- [ ] Tag is pushed: `git ls-remote --tags origin vVERSION` returns the tag SHA
|
||||
|
||||
After GitHub Release:
|
||||
|
||||
- [ ] Release is published (NOT draft): `gh release view "vVERSION"` output doesn't contain "(draft)"
|
||||
- [ ] Workflow is running: `gh run list --workflow=publish.yml --limit 1` shows "in_progress"
|
||||
|
||||
After workflow completes:
|
||||
|
||||
- [ ] Both jobs succeeded: Workflow shows green checkmarks
|
||||
- [ ] SDK on npm: `npm view @bradygaster/squad-sdk version` returns correct version
|
||||
- [ ] CLI on npm: `npm view @bradygaster/squad-cli version` returns correct version
|
||||
- [ ] `latest` tags correct: `npm dist-tag ls @bradygaster/squad-sdk` shows `latest: VERSION`
|
||||
- [ ] Packages install: `npm install @bradygaster/squad-cli` succeeds
|
||||
|
||||
After dev sync:
|
||||
|
||||
- [ ] dev branch has next preview version: `git show dev:package.json | grep version` shows next preview
|
||||
|
||||
---
|
||||
|
||||
## Post-Mortem Reference
|
||||
|
||||
This skill was created after the v0.8.22 release disaster. Full retrospective: `.squad/decisions/inbox/keaton-v0822-retrospective.md`
|
||||
|
||||
**Key learnings:**
|
||||
1. No release without a runbook = improvisation = disaster
|
||||
2. Semver validation is mandatory — 4-part versions break npm
|
||||
3. NPM_TOKEN type matters — User tokens with 2FA fail in CI
|
||||
4. Draft releases are a footgun — they don't trigger automation
|
||||
5. Retry logic is essential — npm propagation takes time
|
||||
|
||||
**Never again.**
|
||||
Reference in New Issue
Block a user