diff --git a/CLAUDE.md b/CLAUDE.md index d76c12ca..35b32300 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -605,7 +605,7 @@ QA YAML is a single file per release cycle - During trunk-based development, edit `qa-test-cases-unreleased.yaml` — add TCs, update automated status, adjust preconditions. The version is deferred until `finalize-release` locks it in. + During trunk-based development, edit `qa-test-cases-unreleased.yaml` — add TCs, update automated status, adjust preconditions. The version is deferred until `release:lock` locks it in. When starting a new release cycle, use `pnpm generate:qa-test-plan:vscode-extension` to create a fresh `qa-test-cases-unreleased.yaml` carrying forward all TCs from the previous version's YAML Edit a YAML file from a past release (e.g., don't touch v1.0.0.yaml after it has shipped) Fixing typos or updating `automated` status (`true`/`assisted`/`false`) in the current file is always allowed diff --git a/docs/RELEASE-STRATEGY.md b/docs/RELEASE-STRATEGY.md index bc32cd1d..484178f9 100644 --- a/docs/RELEASE-STRATEGY.md +++ b/docs/RELEASE-STRATEGY.md @@ -129,19 +129,31 @@ Skip marking cosmetic renames or rewording of existing features — only mark ge ## Release Workflow +### Lifecycle + +```mermaid +flowchart TD + A[Unreleased (deferred)] -->|release:lock| B[Version locked] + B --> C[QA pass] + C -->|release:prepare| D[Docs ready for publish] + D --> E[publish] + E -->|release:start| A +``` + +Three verbs, three phases: **lock** (freeze the version for QA), **prepare** (make documentation publication-ready), **start** (open the next development cycle). + ### High-Level Process Releasing a package involves these phases: -1. **Prepare** - 1. `pnpm lock-version:vscode-extension X.Y.Z` — soft-lock the version for QA - 2. Run QA pass (manual + automated TCs) - 3. `pnpm finalize-release:vscode-extension` — hard-finalize: updates CHANGELOG, strips README markers, generates publishing instructions -2. **Build & Test** - Package and validate locally -3. **Publish** - Deploy to marketplace(s) and create GitHub release -4. **Tag** - Create annotated git tag following [tagging convention](#tagging-convention) -5. **Verify** - Confirm publication and test installation -6. **Next cycle** — `pnpm start-release:vscode-extension` to begin the next development cycle +1. **Lock** — `pnpm release:lock:vscode-extension X.Y.Z` — soft-lock the version for QA +2. **QA Pass** — manual + automated TCs against the versioned YAML +3. **Prepare** — `pnpm release:prepare:vscode-extension` — date-stamp CHANGELOG, strip `Unreleased` markers, remove `[!IMPORTANT]` banner, generate publishing instructions +4. **Build & Test** - Package and validate locally +5. **Publish** - Deploy to marketplace(s) and create GitHub release +6. **Tag** - Create annotated git tag following [tagging convention](#tagging-convention) +7. **Verify** - Confirm publication and test installation +8. **Next cycle** — `pnpm release:start:vscode-extension` to begin the next development cycle ### Package-Specific Instructions @@ -321,9 +333,9 @@ git push origin vscode-extension-v0.1.0 ```bash # 1. Prepare: lock version, run QA, then finalize -pnpm lock-version:vscode-extension 0.1.1 +pnpm release:lock:vscode-extension 0.1.1 # ... QA pass ... -pnpm finalize-release:vscode-extension +pnpm release:prepare:vscode-extension # 2. Build, test, package locally pnpm package:vscode-extension @@ -337,7 +349,7 @@ git tag -a vscode-extension-v0.1.1 -m "Release vscode-extension v0.1.1" git push origin vscode-extension-v0.1.1 # 5. Start next development cycle -pnpm start-release:vscode-extension +pnpm release:start:vscode-extension # 6. Create GitHub release with CHANGELOG content ``` @@ -346,15 +358,15 @@ pnpm start-release:vscode-extension ```bash # Same process as Example 2, with the new version number. -pnpm lock-version:vscode-extension 1.0.0 +pnpm release:lock:vscode-extension 1.0.0 # ... QA pass ... -pnpm finalize-release:vscode-extension +pnpm release:prepare:vscode-extension pnpm package:vscode-extension pnpm install-local:vscode-extension:both pnpm publish:vscode-extension:vsix git tag -a vscode-extension-v1.0.0 -m "Release vscode-extension v1.0.0" git push origin vscode-extension-v1.0.0 -pnpm start-release:vscode-extension +pnpm release:start:vscode-extension ``` ### Example 4: Multiple Package Release @@ -419,7 +431,7 @@ These items are planned but not yet implemented: - Better monorepo version coordination - [x] **Pre-commit hooks** — working tree cleanliness enforced by the release scripts; remaining hook work: prevent commits that introduce version/CHANGELOG mismatches. -- [x] **Release verification scripts** — `pnpm lock-version:vscode-extension`, `pnpm finalize-release:vscode-extension`, and `pnpm generate:publish-instructions:vscode-extension` validate the working tree, version numbers, CHANGELOG, and unreleased markers before allowing each phase to proceed. +- [x] **Release verification scripts** — `pnpm release:lock:vscode-extension`, `pnpm release:prepare:vscode-extension`, and `pnpm generate:publish-instructions:vscode-extension` validate the working tree, version numbers, CHANGELOG, and unreleased markers before allowing each phase to proceed. --- @@ -444,4 +456,4 @@ git push origin vscode-extension-v0.1.0 --- **Last Updated:** 2026-05-26 -**Status:** Active — automated via `pnpm lock-version:vscode-extension` → `pnpm finalize-release:vscode-extension` → `pnpm start-release:vscode-extension` +**Status:** Active — automated via `pnpm release:lock:vscode-extension` → `pnpm release:prepare:vscode-extension` → `pnpm release:start:vscode-extension` diff --git a/package.json b/package.json index a95688e0..057d958f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "clean:all": "pnpm -r clean:all", "compile": "pnpm clean && find . -name '*.tsbuildinfo' -delete && pnpm -r --workspace-concurrency=1 compile", "enable-pnpm": "corepack enable", - "finalize-release:vscode-extension": "pnpm --filter rangelink-vscode-extension run finalize-release", "fix": "pnpm lint:fix && pnpm format:fix", "format": "prettier --check .", "format:fix": "prettier --write .", @@ -26,12 +25,13 @@ "install-local:vscode-extension:vscode": "pnpm --filter rangelink-vscode-extension install-local:vscode", "lint": "eslint .", "lint:fix": "eslint . --fix", - "lock-version:vscode-extension": "pnpm --filter rangelink-vscode-extension run lock-version", "package:prepare": "pnpm clean:all && pnpm install", "package:vscode-extension": "pnpm --filter rangelink-vscode-extension clean && pnpm --filter rangelink-vscode-extension compile && pnpm --filter rangelink-vscode-extension test && pnpm --filter rangelink-vscode-extension package", "package:vscode-extension:withInstall:both": "pnpm package:vscode-extension && pnpm install-local:vscode-extension:both", "publish:vscode-extension:vsix": "pnpm --filter rangelink-vscode-extension publish:vsix", - "start-release:vscode-extension": "pnpm --filter rangelink-vscode-extension run start-release", + "release:lock:vscode-extension": "pnpm --filter rangelink-vscode-extension run release:lock", + "release:prepare:vscode-extension": "pnpm --filter rangelink-vscode-extension run release:prepare", + "release:start:vscode-extension": "pnpm --filter rangelink-vscode-extension run release:start", "test": "pnpm -r test", "test:bats": "bats tests/shell/", "test:release": "pnpm --filter rangelink-vscode-extension test:release", diff --git a/packages/rangelink-vscode-extension/TESTING.md b/packages/rangelink-vscode-extension/TESTING.md index 4cf8a15a..803ddd3d 100644 --- a/packages/rangelink-vscode-extension/TESTING.md +++ b/packages/rangelink-vscode-extension/TESTING.md @@ -36,22 +36,11 @@ All `test:release*` commands accept `--label ` (include TCs with QA YAML la | Script | When | Re-runnable? | What it does | | ------------------------------------------ | ---------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------- | -| `pnpm lock-version:vscode-extension X.Y.Z` | Ready to start QA | Yes (idempotent) | Renames QA YAML → versioned, bumps `.version`, regenerates instructions | -| `pnpm finalize-release:vscode-extension` | QA passed, ready to ship | No (one-way door) | Finalizes CHANGELOG, strips README markers/banner, generates publishing instructions | -| `pnpm start-release:vscode-extension` | After publish, starting next cycle | Yes (idempotent) | Copies versioned YAML → unreleased, adds `[Unreleased]` CHANGELOG header, re-adds README banner | +| `pnpm release:lock:vscode-extension X.Y.Z` | Ready to start QA | Yes (idempotent) | Renames QA YAML → versioned, bumps `.version`, regenerates instructions, generates QA issue | +| `pnpm release:prepare:vscode-extension` | QA passed, ready to ship | No (one-way door) | Date-stamps CHANGELOG, strips README markers/banner, generates publishing instructions | +| `pnpm release:start:vscode-extension` | After publish, starting next cycle | Yes (idempotent) | Copies versioned YAML → unreleased, adds `[Unreleased]` CHANGELOG header, re-adds README banner | -```mermaid -flowchart TD - A[Version: Unreleased (deferred)] --> B[lock-version.sh X.Y.Z] - B --> C[QA pass — manual + automated TCs] - C --> D{All TCs pass?} - D -- No --> E[Fix bugs] - E --> C - D -- Yes --> F[finalize-release.sh] - F --> G[build VSIX + publish] - G --> H[start-release.sh] - H --> A -``` +The release lifecycle is documented in [RELEASE-STRATEGY.md](../../docs/RELEASE-STRATEGY.md#release-workflow). --- @@ -277,9 +266,7 @@ The QA test plan is a version-scoped YAML file that tracks both automated and ma qa/qa-test-cases-unreleased.yaml ``` -During trunk-based development the file is `qa/qa-test-cases-unreleased.yaml`. At release time `pnpm lock-version:vscode-extension` renames it to `qa/qa-test-cases-v.yaml`. - -The filename is always `qa-test-cases-unreleased.yaml` during trunk-based development — the version is deferred until `pnpm lock-version:vscode-extension` locks it in at QA time. It is parsed automatically by the `generate-qa-issue` script — no extra flags needed. One file per release — Git tracks history across versions. +During trunk-based development the file is `qa/qa-test-cases-unreleased.yaml`. At release time `pnpm release:lock:vscode-extension` renames it to `qa/qa-test-cases-v.yaml` and generates the QA issue automatically. One file per release — Git tracks history across versions. New QA YAML files are created by `pnpm generate:qa-test-plan`. The script carries forward all TCs from the most recent YAML, resets `status:` fields to `pending`, and preserves `automated:` flags. diff --git a/packages/rangelink-vscode-extension/package.json b/packages/rangelink-vscode-extension/package.json index 6ef4d01a..b7b0e47b 100644 --- a/packages/rangelink-vscode-extension/package.json +++ b/packages/rangelink-vscode-extension/package.json @@ -36,7 +36,6 @@ "clean:all": "pnpm clean && rm -rf node_modules .eslintcache *.log .vscode-test", "compile": "pnpm compile:deps && pnpm generate-version:all && node esbuild.config.js", "compile:deps": "cd ../barebone-logger && pnpm clean && pnpm compile && cd ../barebone-logger-testing && pnpm clean && pnpm compile && cd ../rangelink-core-ts && pnpm clean && pnpm compile", - "finalize-release": "./scripts/finalize-release.sh", "generate-version": "node scripts/generate-version.js --copy-to out", "generate-version:all": "node scripts/generate-version.js --copy-to out,dist", "generate:publish-instructions": "./scripts/generate-publishing-instructions.sh", @@ -46,10 +45,11 @@ "install-local": "./scripts/install-local.sh", "install-local:cursor": "./scripts/install-local.sh cursor", "install-local:vscode": "./scripts/install-local.sh vscode", - "lock-version": "./scripts/lock-version.sh", "package": "rm -rf *.vsix && ../../scripts/sync-assets.sh && rm -f rangelink-vscode-extension-*.vsix && vsce package --no-dependencies", "publish:vsix": "./scripts/publish-from-vsix.sh", - "start-release": "./scripts/start-release.sh", + "release:lock": "./scripts/orchestrate-release-lock.sh", + "release:prepare": "./scripts/finalize-release.sh", + "release:start": "./scripts/start-release.sh", "test": "jest --coverage", "test:coverage": "jest --coverage --coverageReporters=text --coverageReporters=text-summary --coverageReporters=html", "test:fast": "jest --coverage --testPathIgnorePatterns '/src/__integration-tests__/' '\\.integration\\.test\\.ts$'", diff --git a/packages/rangelink-vscode-extension/scripts/finalize-release.sh b/packages/rangelink-vscode-extension/scripts/finalize-release.sh index 60bc4701..61a295c6 100755 --- a/packages/rangelink-vscode-extension/scripts/finalize-release.sh +++ b/packages/rangelink-vscode-extension/scripts/finalize-release.sh @@ -41,7 +41,7 @@ fi # --- Working tree must be clean --- -if ! git -C "$REPO_ROOT" diff-index --quiet HEAD --; then +if [[ -n "$(git -C "$REPO_ROOT" status --porcelain)" ]]; then echo -e "${RED}Error: working tree is dirty. Commit or stash changes first.${NC}" >&2 exit 1 fi diff --git a/packages/rangelink-vscode-extension/scripts/generate-publishing-instructions.sh b/packages/rangelink-vscode-extension/scripts/generate-publishing-instructions.sh index 12e77094..b48ff6a3 100755 --- a/packages/rangelink-vscode-extension/scripts/generate-publishing-instructions.sh +++ b/packages/rangelink-vscode-extension/scripts/generate-publishing-instructions.sh @@ -434,7 +434,45 @@ The following were validated when generating this file: --- -## Phase 1: Create Git Tag +## Phase 1: Smoke Test + +Install the extension locally and verify it works before publishing. This catches packaging +errors (missing files, broken builds) before they reach the marketplace. + +### Install Locally + +Run from **monorepo root**: + +\`\`\`bash +pnpm install-local:vscode-extension:both +\`\`\` + +This installs the VSIX in both VS Code and Cursor. Restart any running instances first. + +### Verify Version Number + +1. Open VS Code (\`code\`), then the Command Palette (\`Cmd+Shift+P\` / \`Ctrl+Shift+P\`) +2. Run: **RangeLink: Show Version Info** +3. Confirm the version shown is **${VERSION}** +4. Confirm \`isDirty\` is **false** — a dirty build should never be published + +### Spot-Check Core Commands and Content + +Verify these commands work and the content is OK: + +- [ ] **R-L** +- [ ] **R-F** +- [ ] **Status bar menu** +- [ ] **No README artifacts** — open the extension's README in the Extensions panel. No \`Unreleased\` markers, no \`> [!IMPORTANT]\` banner. + +### If Anything Fails + +- Fix the issue, rebuild (\`pnpm package:vscode-extension\`), and re-run the smoke test. +- Regenerate these instructions afterward: \`pnpm generate:publish-instructions:vscode-extension\` + +--- + +## Phase 2: Create Git Tag ### Tag the Release @@ -459,7 +497,7 @@ git show ${GIT_TAG} --stat --- -## Phase 2: Create GitHub Release +## Phase 3: Create GitHub Release ### Navigate to GitHub Releases @@ -479,7 +517,7 @@ Click "Publish release" --- -## Phase 3: Publish to VS Code Marketplace +## Phase 4: Publish to VS Code Marketplace ### Ensure Logged In @@ -506,7 +544,7 @@ Wait 5-10 minutes, then check: --- -## Phase 4: Publish to Open-VSX Registry +## Phase 5: Publish to Open-VSX Registry ### Login (First Time Setup) @@ -541,7 +579,7 @@ Check: --- -## Phase 5: Post-Publishing Verification +## Phase 6: Post-Publishing Verification ### Check All Locations diff --git a/packages/rangelink-vscode-extension/scripts/generate-qa-issue.sh b/packages/rangelink-vscode-extension/scripts/generate-qa-issue.sh index be85d9f0..254cc146 100755 --- a/packages/rangelink-vscode-extension/scripts/generate-qa-issue.sh +++ b/packages/rangelink-vscode-extension/scripts/generate-qa-issue.sh @@ -62,11 +62,15 @@ if [[ -z "$YAML_FILE" ]]; then fi YAML_FILE="$QA_DIR/$LATEST" - printf 'Use %s? [Y/n] ' "qa/$LATEST" - read -r REPLY - if [[ -n "$REPLY" && ! "$REPLY" =~ ^[Yy]$ ]]; then - echo "Aborted." >&2 - exit 0 + if [[ -t 0 ]]; then + printf 'Use %s? [Y/n] ' "qa/$LATEST" + read -r REPLY + if [[ -n "$REPLY" && ! "$REPLY" =~ ^[Yy]$ ]]; then + echo "Aborted." >&2 + exit 0 + fi + else + echo "Auto-selected latest QA YAML: qa/$LATEST" fi fi @@ -169,11 +173,12 @@ if [[ "$UBUNTU_COUNT" -gt 0 ]]; then GROUP_CHECKBOXES+="${UBUNTU_SECTION}"$'\n' fi -ISSUE_BODY="${ISSUE_BODY}${GROUP_CHECKBOXES}" - TOTAL_ASSISTED=$(echo "$GROUPS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync(0,'utf8')).total_assisted))") TOTAL_MANUAL=$(echo "$GROUPS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync(0,'utf8')).total_manual))") -ISSUE_BODY="${ISSUE_BODY}"$'\n'"**Total: ${TOTAL_ASSISTED} assisted, ${TOTAL_MANUAL} manual**" + +BODY_FOOTER="${GROUP_CHECKBOXES}"$'\n\n'"**Total: ${TOTAL_ASSISTED} assisted, ${TOTAL_MANUAL} manual**"$'\n\n'"---"$'\n\n'"Once all checkboxes above are checked, the release is ready to prepare for publication:"$'\n\n'"- [ ] **Release Ready** — Run \`pnpm release:prepare:vscode-extension\` to date-stamp the CHANGELOG, strip unreleased markers, and generate publishing instructions." + +ISSUE_BODY="${ISSUE_BODY}${BODY_FOOTER}" # --- Local mode: write to a markdown file --- if [[ "$LOCAL_MODE" == true ]]; then @@ -182,20 +187,12 @@ if [[ "$LOCAL_MODE" == true ]]; then TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S") LOCAL_FILE="$OUTPUT_DIR/qa-checklist-${VERSION}-${TIMESTAMP}.md" - TOTAL_ASSISTED=$(echo "$GROUPS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync(0,'utf8')).total_assisted))") - TOTAL_MANUAL=$(echo "$GROUPS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync(0,'utf8')).total_manual))") - { echo "# QA Checklist — ${VERSION}" echo "" echo "Generated from \`$(basename "$YAML_FILE")\` on $(date -u +"%Y-%m-%d %H:%M:%S UTC")" echo "" - echo "CI runs fully automated tests (\`test:release:automated\`). The checkboxes below cover assisted and manual tests only." - echo "" - echo "${GROUP_CHECKBOXES}" - echo "" - echo "**Total: ${TOTAL_ASSISTED} assisted, ${TOTAL_MANUAL} manual**" - + echo "${ISSUE_BODY}" } > "$LOCAL_FILE" REPO_ROOT="$(git rev-parse --show-toplevel)" diff --git a/packages/rangelink-vscode-extension/scripts/lock-version.sh b/packages/rangelink-vscode-extension/scripts/lock-version.sh index 0ab710c0..7ab5ca9e 100755 --- a/packages/rangelink-vscode-extension/scripts/lock-version.sh +++ b/packages/rangelink-vscode-extension/scripts/lock-version.sh @@ -41,7 +41,7 @@ QA_DIR="$PACKAGE_DIR/qa" # --- Validation --- -if ! git -C "$REPO_ROOT" diff-index --quiet HEAD --; then +if [[ -n "$(git -C "$REPO_ROOT" status --porcelain)" ]]; then echo -e "${RED}Error: working tree is dirty. Commit or stash changes first.${NC}" >&2 exit 1 fi diff --git a/packages/rangelink-vscode-extension/scripts/orchestrate-release-lock.sh b/packages/rangelink-vscode-extension/scripts/orchestrate-release-lock.sh new file mode 100755 index 00000000..16ab4255 --- /dev/null +++ b/packages/rangelink-vscode-extension/scripts/orchestrate-release-lock.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: ./scripts/orchestrate-release-lock.sh X.Y.Z +# +# Orchestrates the release lock workflow: +# 1. Create a release/vX.Y.Z branch from main +# 2. Run lock-version.sh (rename YAML, bump .version, regenerate instructions) +# 3. Run generate-qa-issue.sh (create GitHub QA issue tracker) +# 4. Generate a commit message file +# +# Delegates the actual work to the individual scripts; this script handles +# branch setup and sequencing only. + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +VERSION="${1:-}" +if [[ -z "$VERSION" ]]; then + echo -e "${RED}Usage: $0 ${NC}" >&2 + echo "Example: $0 2.0.0" >&2 + exit 1 +fi + +if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo -e "${RED}Error: version must be SemVer (X.Y.Z), got '$VERSION'${NC}" >&2 + exit 1 +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PACKAGE_DIR="$(dirname "$SCRIPT_DIR")" +REPO_ROOT="$(git -C "$PACKAGE_DIR" rev-parse --show-toplevel)" + +# --- Working tree must be clean --- + +if [[ -n "$(git -C "$REPO_ROOT" status --porcelain)" ]]; then + echo -e "${RED}Error: working tree is dirty. Commit or stash changes first.${NC}" >&2 + exit 1 +fi + +# --- Create release branch --- + +RELEASE_BRANCH="release/v${VERSION}" +if git -C "$REPO_ROOT" rev-parse --verify "$RELEASE_BRANCH" >/dev/null 2>&1; then + echo -e "${RED}Error: branch $RELEASE_BRANCH already exists.${NC}" >&2 + exit 1 +fi + +CURRENT_BRANCH=$(git -C "$REPO_ROOT" branch --show-current) +if [[ "$CURRENT_BRANCH" != "main" ]]; then + echo -e "${YELLOW}Not on main (current: $CURRENT_BRANCH). Creating $RELEASE_BRANCH from main.${NC}" +fi + +git -C "$REPO_ROOT" checkout -b "$RELEASE_BRANCH" main +echo -e "${GREEN}Created branch $RELEASE_BRANCH from main${NC}" + +# --- Step 1: Lock the version --- + +"$SCRIPT_DIR/lock-version.sh" "$VERSION" +echo "" + +# --- Step 2: Generate QA issue --- + +QA_OUTPUT=$("$SCRIPT_DIR/generate-qa-issue.sh") || { + echo -e "${RED}Error: generate-qa-issue.sh failed${NC}" >&2 + exit 1 +} +echo "$QA_OUTPUT" +echo "" + +QA_ISSUE_URL=$(echo "$QA_OUTPUT" | grep -o 'https://github\.com/[^ ]*issues/[0-9]*') +if [[ -z "$QA_ISSUE_URL" ]]; then + echo -e "${RED}Error: could not extract QA issue URL from generate-qa-issue.sh output${NC}" >&2 + exit 1 +fi + +# --- Step 3: Generate commit message --- + +COMMIT_MSGS_DIR="$REPO_ROOT/.commit-msgs" +mkdir -p "$COMMIT_MSGS_DIR" + +LAST=$(ls "$COMMIT_MSGS_DIR" | grep -o '^[0-9]\{4\}' | sort -n | tail -1 || true) +NEXT_NUM=$(printf "%04d" $((10#${LAST:-0} + 1))) +COMMIT_MSG_FILE="$COMMIT_MSGS_DIR/${NEXT_NUM}-lock-version-v${VERSION}.txt" + +cat > "$COMMIT_MSG_FILE" < "$FIXTURE_ROOT/package.json" <<'EOF' +{ + "version": "2.0.0", + "publisher": "test-publisher" +} +EOF + + cat > "$FIXTURE_ROOT/src/version.json" <<'EOF' +{"isDirty": false} +EOF + + # Files the script validates before generating instructions. + touch "$FIXTURE_ROOT/rangelink-vscode-extension-2.0.0.vsix" + cat > "$FIXTURE_ROOT/CHANGELOG.md" <<'EOF' +## [2.0.0] + +### Added +- Test entry +[2.0.0]: https://example.com +EOF + touch "$FIXTURE_ROOT/README.md" + + stub_dir + make_stub "node" <<'ENDOFSTUB' +#!/usr/bin/env bash +if [[ "$*" == *"isDirty"* ]]; then + echo "false" +elif [[ "$*" == *"publisher"* ]]; then + echo "test-publisher" +elif [[ "$*" == *".version"* ]]; then + echo "2.0.0" +else + exit 1 +fi +ENDOFSTUB + make_passive_stub "npx" + make_stub "git" <<'ENDOFSTUB' +case "$*" in + *rev-parse*) exit 1 ;; + *) exit 0 ;; +esac +ENDOFSTUB +} + +# ── Smoke-test command title ───────────────────────────────────────────────────── + +@test "publishing instructions smoke-test step uses Show Version Info" { + setup_fixture + run "$SCRIPT" --allow-dirty + [[ "$status" -eq 0 ]] + + output_file="$FIXTURE_ROOT/publishing-instructions/publish-vscode-extension-v2.0.0.md" + [[ -f "$output_file" ]] + + run cat "$output_file" + [[ "$output" =~ "Show Version Info" ]] + ! [[ "$output" =~ "Show Extension Info" ]] +} diff --git a/tests/shell/lock-version.bats b/tests/shell/lock-version.bats index 5e070e32..e5a71b8d 100644 --- a/tests/shell/lock-version.bats +++ b/tests/shell/lock-version.bats @@ -16,7 +16,7 @@ setup_fixture() { make_stub "git" <<'ENDOFSTUB' case "$*" in *--show-toplevel*) echo "${FIXTURE_ROOT_FOR_GIT:-$TEST_TEMP_DIR}" ;; - *diff-index*) exit 0 ;; + *status*) exit 0 ;; *"mv"*) shift 3; mv "$@" ;; *) exit 0 ;; esac diff --git a/tests/shell/orchestrate-release-lock.bats b/tests/shell/orchestrate-release-lock.bats new file mode 100644 index 00000000..147fc39c --- /dev/null +++ b/tests/shell/orchestrate-release-lock.bats @@ -0,0 +1,61 @@ +#!/usr/bin/env bats + +load test_helper + +REAL_SCRIPT="$PROJECT_ROOT/packages/rangelink-vscode-extension/scripts/orchestrate-release-lock.sh" + +setup_fixture() { + FIXTURE_ROOT="$TEST_TEMP_DIR" + mkdir -p "$FIXTURE_ROOT/scripts" + mkdir -p "$FIXTURE_ROOT/.commit-msgs" + + cp "$REAL_SCRIPT" "$FIXTURE_ROOT/scripts/orchestrate-release-lock.sh" + SCRIPT="$FIXTURE_ROOT/scripts/orchestrate-release-lock.sh" + + stub_dir + make_stub "git" <<'ENDOFSTUB' +case "$*" in + *--show-toplevel*) echo "$FIXTURE_ROOT_FOR_GIT" ;; + *status*) exit 0 ;; + *"checkout -b"*) exit 0 ;; + *"rev-parse --verify"*) exit 1 ;; + *"branch --show-current"*) echo "main" ;; + *) exit 0 ;; +esac +ENDOFSTUB + export FIXTURE_ROOT_FOR_GIT="$FIXTURE_ROOT" + + # Stub lock-version.sh to do nothing. + cat > "$FIXTURE_ROOT/scripts/lock-version.sh" <<'STUBEOF' +#!/usr/bin/env bash +echo "Locked version $1" +STUBEOF + chmod +x "$FIXTURE_ROOT/scripts/lock-version.sh" + + # Stub generate-qa-issue.sh to output a fake issue URL. + cat > "$FIXTURE_ROOT/scripts/generate-qa-issue.sh" <<'STUBEOF' +#!/usr/bin/env bash +echo "Created QA issue: https://github.com/couimet/rangeLink/issues/999" +STUBEOF + chmod +x "$FIXTURE_ROOT/scripts/generate-qa-issue.sh" +} + +# ── Commit message numbering ───────────────────────────────────────────────────── + +@test "first run with empty .commit-msgs creates 0001 file" { + setup_fixture + run "$SCRIPT" "1.0.0" + [[ "$status" -eq 0 ]] + [[ -f "$FIXTURE_ROOT/.commit-msgs/0001-lock-version-v1.0.0.txt" ]] +} + +@test "picks next number after existing files" { + setup_fixture + touch "$FIXTURE_ROOT/.commit-msgs/0001-old.txt" + touch "$FIXTURE_ROOT/.commit-msgs/0005-old.txt" + touch "$FIXTURE_ROOT/.commit-msgs/0010-old.txt" + + run "$SCRIPT" "2.0.0" + [[ "$status" -eq 0 ]] + [[ -f "$FIXTURE_ROOT/.commit-msgs/0011-lock-version-v2.0.0.txt" ]] +}