Skip to content

Commit 486252c

Browse files
authored
Merge branch 'main' into fix/install-package-options
2 parents 89c9e47 + 7952751 commit 486252c

119 files changed

Lines changed: 3605 additions & 535 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/release-manager/SKILL.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ Merging this PR will trigger the release workflow.
130130

131131
### Categorization rules
132132

133-
- Every PR from `generate-notes` appears exactly once, with one exception: omit bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. When you omit one, say so when reporting the validation counts so the mismatch reads as deliberate rather than missed. No PR is listed both in Highlights and a section below.
133+
- Every PR from `generate-notes` appears exactly once, except fully reverted changes described below and bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. Record each omitted PR and its reason when reporting validation counts. No PR is listed both in Highlights and a section below.
134134
- **Breaking Changes goes first, above Highlights, and only when the release has one.** A rename is breaking only when the old name stops working; if a deprecated alias is retained it is not breaking, so keep the two in different sections rather than merging them into one entry. Give each breaking entry an old -> new table when several names change, plus one line telling readers where to update (shell profile, CI job, Dockerfile). Do not editorialize about the version number.
135135
- When several breaking changes affect different workflows, group them under short `####` headings. Explain the changed behavior and required action before each table; keep automatic migration steps separate from changes users must make manually.
136136
- **Describe the net change between the two released versions, not intra-cycle churn.** When several PRs touch the same area within one release (one narrows a behavior, a later one broadens it back), the reader only sees the delta from `v<prev>` to `v<curr>`; describe that once, listing every PR number, and do not narrate a regression that was introduced and then fixed inside the cycle. Apply this to the intro/theme sentence too.
137+
- If a change and its complete revert are both unreleased, omit both when they leave no net change. Remove sections with no remaining entries. A revert of behavior in the previous release still needs an entry.
137138
- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.
138139
- `feat(docs)` goes in Docs when the user-facing surface is the docs site.
139140
- **Docs means the published docs site, not contributor files.** A `docs` commit that changes an RFC, `AGENTS.md`, the repo map, or a skill under `.claude/` belongs in Chore: a vite-plus user never reads those. Docs should hold only entries a reader could go and look at on the site or in the README.
@@ -176,15 +177,15 @@ gh pr edit <PR#> --repo voidzero-dev/vite-plus --title "release: vX.Y.Z: <theme>
176177

177178
```bash
178179
BODY=$(gh pr view <PR#> --repo voidzero-dev/vite-plus --json body -q '.body')
179-
# every generate-notes PR present (minus any deliberately omitted bot PR), none duplicated:
180+
# every generate-notes PR present (minus documented omissions), none duplicated:
180181
echo "$BODY" | grep -oE 'voidzero-dev/vite-plus/pull/[0-9]+' | sort -u | wc -l
181182
echo "$BODY" | grep -oE '(vite-plus|vite-task)/pull/[0-9]+' | sort | uniq -d # must be empty
182183
echo "$BODY" | grep -nE '[—–]' # must be empty
183184
echo "$BODY" | grep -c '\\`' # must be 0 (escaped backticks)
184185
echo "$BODY" | tail -1 # boilerplate closing line intact
185186
```
186187

187-
Diff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must be a bot PR you chose to omit.
188+
Diff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must have a documented reason for omission.
188189

189190
## 4. Preview build smoke test (before merging)
190191

@@ -241,6 +242,8 @@ Do this before both the local sweep and the fork PRs. If PRs were already opened
241242

242243
The workflow triggers only on the `labeled` event, not on new pushes. To rebuild after the head moves (e.g. after a step 5 merge from `main`), remove and re-add the label (this cancels an in-flight build for the branch). A stale build whose diff to the new head is test-only is still valid for smoke testing; ask before re-triggering.
243244

245+
Record each project's base commit, starting version, resolved preview commit, fork PR head, and CI run. After a new preview build, identify which projects ran again and which retain earlier evidence. Do not report a partial repeat as a full-catalog run on the new preview.
246+
244247
### Example (v0.2.2, PR #2016)
245248

246249
Changelog complete, CI green, release manager approved the smoke test. A build existed for head `06708538`; the head had since moved by a test-only merge from `main`, so that build was still valid and was not re-triggered.
@@ -355,12 +358,12 @@ Auto-merge being enabled is not a completed merge. Confirm `mergedAt` and the me
355358
-q '.[] | "\(.environment.name) can_approve=\(.current_user_can_approve) reviewers=\([.reviewers[]?.reviewer.login] | join(","))"'
356359
```
357360

358-
4. `Release`: publishes the platform-native CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`) and then `@voidzero-dev/vite-plus-core` and `vite-plus` to npm (`--tag latest`), creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.
361+
4. `Release`: publishes the NAPI bindings (`@voidzero-dev/vite-plus-<platform>`) and standalone CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`), then `@voidzero-dev/vite-plus-core` and `vite-plus` to npm (`--tag latest`). Each dependency tier waits for npm propagation before publication advances. It then creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.
359362
5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).
360363
6. `deploy-docs`: deploys the production docs after a stable release is published.
361364
7. `discord-notify`: announces to Discord after Docker publishing and docs deployment succeed (docs are skipped for prereleases).
362365

363-
**A green `Release` job does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.
366+
**A successful publish command does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.
364367

365368
Check visibility directly, not through `npm view`, which caches:
366369

@@ -377,6 +380,8 @@ Both must report `True` before you trust the release. A stale `modified` timesta
377380
d=$(mktemp -d); cd "$d" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund
378381
```
379382

383+
The full package document can update before npm's separately cached installation metadata. The workflow's `.github/scripts/wait-for-npm-packages.ts` checks that installation metadata and the referenced tarball, then waits for settlement. Use that script to reproduce its availability check; local visibility does not prove the workflow runner sees the same state.
384+
380385
## 8. Post-release
381386

382387
1. **Polish the GitHub release notes** (ask first): the auto-created release body has only Published Packages and Installation. Build the polished notes from the final release PR body:
@@ -413,6 +418,8 @@ d=$(mktemp -d); cd "$d" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z
413418
docker run --rm ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp --version
414419
```
415420

421+
In a project installation, the CLI depends on core through the `vite` npm alias. Resolve `vite/package.json` from `vite-plus/package.json` when checking core's installed version. Check `@voidzero-dev/vite-plus-<platform>` for the installed NAPI binding; the standalone CLI package is separate.
422+
416423
`vp upgrade` requires a standalone installation; `vp update` is not a substitute because it updates project dependencies. Resolve the intended binary and query its roots with `VP_DUMP_DIRS=1`; installations can use split XDG/platform roots, an explicit `VP_HOME`, or the legacy `~/.vite-plus` directory. Remove temporary overrides left by preview/control runs, while preserving the intended installation's configuration.
417424

418425
If the user's installation points to `local-dev-*` or is managed by another tool, test an isolated copy of the previous published installation under an explicit `VP_HOME`. Repoint any absolute symlinks in the copy to the copied root before testing. Label the result as an isolated upgrade; preserve the development installation and the original control used for regression tests. Run the selected binary outside a project so a local CLI cannot take over:
@@ -439,6 +446,8 @@ d=$(mktemp -d); cd "$d" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z
439446
"https://ghcr.io/v2/voidzero-dev/vite-plus/manifests/X.Y.Z" | head -1 # HTTP/2 200
440447
```
441448
449+
Without Docker, package manifests can also be read from each architecture's published installer layer. Check the layer digest first. pnpm package files can be tar hardlinks into the package store; read their targets within the archive without extracting it.
450+
442451
3. **Announce on Discord** (concise format). Keep it tight: every line is a single short phrase, no heading-plus-explanation sentences, the whole message around 20 lines. No PR links, no tables, no per-entry credits, no em dashes. Make the theme and highlights self-contained by naming the affected capability rather than using vague benefit-only wording. Use verbs that match the actual behavior, especially distinguishing guidance or suggestions from automatic actions. One emoji per line by theme (`:lock:` security, `:zap:` performance, `:sparkles:` DX, `:seedling:` scaffolding, `:hammer_and_wrench:` tooling, `:package:` deps). Use **Upstream Upgrades** for dependency/tool version bumps, not Highlights, and list only tools whose version actually changed. Leave the full Bundled Versions table in the linked release notes rather than repeating it in the announcement. A security fix caused by a dependency bump can still have a Highlight focused on the vulnerability, and that line must link the CVE/GHSA/advisory when one exists. A breaking change gets its own `:warning:` Highlight naming the old and new names and what the reader must update. Include **Also in this release** only when there are meaningful secondary user-facing items, and omit the whole section for a narrow hotfix.
443452

444453
```markdown
@@ -483,7 +492,7 @@ After the release ships and announcements are approved or confirmed complete, re
483492
484493
- [ ] `prepare_release` run for the target version; release PR open
485494
- [ ] `binding/index.cjs` synced on the release branch (step 2 commit message shape)
486-
- [ ] PR description written from the head branch data; every PR exactly once except deliberately omitted bot-noise PRs; breaking changes in their own section above Highlights; no em/en dashes; closing boilerplate intact
495+
- [ ] PR description written from the head branch data; every PR exactly once except documented omissions; breaking changes in their own section above Highlights; no em/en dashes; closing boilerplate intact
487496
- [ ] Dependency-upgrade PRs consolidated; vite-task bump expanded with upstream credits; security advisories linked
488497
- [ ] Smoke test offered to the release manager at both levels (local sweep and fork-PR CI), with the commit count stated and a recommendation to run it when that count is above 10; if accepted, forks synced to upstream first, preview build published, and the full ecosystem-ci catalog verified via `test-pkg-pr-new-migrate` (following TESTING.md), with every failure triaged and regressions ruled out against the previous release
489498
- [ ] CI green; any fixes landed via separate PRs to main, merged back, and added to the changelog

.github/actions/build-upstream/action.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ inputs:
1111
skip-native:
1212
description: >-
1313
Skip the NAPI binding cache and all native cargo/napi build steps; the
14-
native binaries must already be in place (e.g. downloaded from the
15-
windows-cli-binaries artifact produced by the build-windows-cli job).
14+
native binaries and Cargo metadata must already be in place (e.g.
15+
downloaded from the windows-cli-binaries artifact produced by the
16+
build-windows-cli job).
1617
required: false
1718
default: 'false'
1819

@@ -86,7 +87,13 @@ runs:
8687
pnpm --filter rolldown build-node
8788
pnpm --filter vite build-types
8889
pnpm --filter "@voidzero-dev/*" build
89-
pnpm --filter vite-plus build-ts
90+
91+
# Native binding formatting imports the repo config, which needs the
92+
# built CLI. Prebuilt bindings do not need this preliminary build.
93+
- name: Build vite-plus TypeScript for native binding formatting
94+
if: steps.native.outputs.build == 'true'
95+
shell: bash
96+
run: pnpm --filter vite-plus build-ts
9097

9198
# Install zig + cargo-zigbuild for musl cross-compilation (napi-cross only supports gnu)
9299
- name: Add musl Rust target
@@ -212,6 +219,10 @@ runs:
212219
shell: bash
213220
run: |
214221
pnpm --filter=vite-plus build-ts
222+
env:
223+
# Reuse metadata from the native build so consumer jobs do not
224+
# download a Rust toolchain and dependency sources to generate versions.
225+
VP_BUILD_CARGO_METADATA: ${{ inputs.skip-native == 'true' && format('{0}/packages/cli/binding/vite-plus.cargo-metadata.json', github.workspace) || '' }}
215226

216227
- name: Print output after build
217228
shell: bash

.github/actions/build-windows-cli/action.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ runs:
3131
id: cache-key
3232
shell: bash
3333
run: |
34-
echo "key=windows-cli-xwin-v1-${{ steps.native-cache-inputs.outputs.hash }}" >> "$GITHUB_OUTPUT"
34+
echo "key=windows-cli-xwin-v2-${{ steps.native-cache-inputs.outputs.hash }}" >> "$GITHUB_OUTPUT"
3535
3636
- name: Define binary paths
3737
id: binary-paths
@@ -41,6 +41,7 @@ runs:
4141
echo 'paths<<EOF'
4242
echo 'packages/cli/binding/*.node'
4343
echo 'packages/cli/binding/vite-plus.build-time'
44+
echo 'packages/cli/binding/vite-plus.cargo-metadata.json'
4445
echo 'target/x86_64-pc-windows-msvc/release/vp.exe'
4546
echo 'target/x86_64-pc-windows-msvc/release/vp-shim.exe'
4647
echo 'target/x86_64-pc-windows-msvc/release/vp-setup.exe'
@@ -60,7 +61,7 @@ runs:
6061
save-cache: ${{ inputs.save-cache }}
6162
cache-key: windows-cli-cross
6263

63-
- uses: oxc-project/setup-node@f46a72f95efdc55273fcd042d61c84e723b2892c # v1.4.1
64+
- uses: oxc-project/setup-node@1f1a5b4450c8905bd1830c3a908d22b960c4559a # main (Node before pnpm; unreleased)
6465
if: steps.binaries-cache.outputs.cache-hit != 'true'
6566

6667
# --skip-format: formatting the generated index.cjs/index.d.cts needs the
@@ -94,6 +95,16 @@ runs:
9495
env:
9596
XWIN_ACCEPT_LICENSE: '1'
9697

98+
# TypeScript builds need only these Cargo package fields for toolchain
99+
# versions. Export them where the Rust dependencies are already present.
100+
- name: Export Cargo metadata
101+
if: steps.binaries-cache.outputs.cache-hit != 'true'
102+
shell: bash
103+
run: |
104+
cargo metadata --locked --format-version 1 \
105+
| jq '{packages: [.packages[] | {name, version, source}]}' \
106+
> packages/cli/binding/vite-plus.cargo-metadata.json
107+
97108
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
98109
with:
99110
name: ${{ inputs.artifact-name }}

.github/scripts/test-install-bootstrap.ps1

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,24 @@ Assert ($LASTEXITCODE -eq 0) 'Could not create fixture'
5050
$env:TEMP = "$testRoot/tmp"
5151

5252
function Invoke-RestMethod {
53-
param($Uri)
53+
param($Uri, $Headers)
5454
$script:Requests.Add("GET $Uri")
55-
return @{ version = '0.2.9' }
55+
if ($Uri -eq 'https://custom.example/vite-plus/latest') {
56+
return @{ version = '0.2.9' }
57+
}
58+
if ([System.Uri]::UnescapeDataString($Uri) -like 'https://custom.example/@voidzero-dev/vite-plus-cli-*/0.2.9') {
59+
# Release payloads must pass the real provenance gate before handoff.
60+
return @{
61+
version = '0.2.9'
62+
dist = @{
63+
tarball = 'https://custom.example/platform.tgz'
64+
attestations = @{
65+
provenance = @{ predicateType = 'https://slsa.dev/provenance/v1' }
66+
}
67+
}
68+
}
69+
}
70+
throw "Unexpected metadata request: $Uri"
5671
}
5772
function Invoke-WebRequest {
5873
param($Uri, $Method, $OutFile, [switch]$UseBasicParsing, $ErrorAction)

.github/scripts/test-install-bootstrap.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ curl() {
5959
*file://*) command curl "$@" ;;
6060
*-fsSIL*) printf 'x-commit-key: voidzero-dev:vite-plus:%s\r\n' "$fixture_sha" ;;
6161
*'https://custom.example/vite-plus/'*) printf '{"version":"0.2.9"}\n' ;;
62+
*'https://custom.example/@voidzero-dev%2Fvite-plus-cli-'*)
63+
# Release payloads must pass the real provenance gate before handoff.
64+
printf '{"version":"0.2.9","dist":{"tarball":"https://custom.example/platform.tgz","attestations":{"provenance":{"predicateType":"https://slsa.dev/provenance/v1"}}}}\n' ;;
6265
*) cp "$test_root/payload.tgz" "${@: -1}" ;;
6366
esac
6467
}

0 commit comments

Comments
 (0)