Skip to content

Commit f8c77a4

Browse files
authored
Merge branch 'main' into rss1102/issue-2603-private-registry-auth
2 parents 0ef0c52 + 31b0922 commit f8c77a4

76 files changed

Lines changed: 2979 additions & 171 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: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ git show origin/release/v<curr>:pnpm-workspace.yaml # vitest/oxl
9797
### Structure
9898

9999
```markdown
100-
Release vite-plus vX.Y.Z: <theme>.
101-
102-
<One or two sentences on the release theme. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>
100+
<One or two sentences on the release theme. Do not repeat the PR title as an opener line; GitHub renders the title directly above the body, and step 8 would only strip it again. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>
103101

104102
### Breaking Changes
105103

@@ -135,6 +133,9 @@ Merging this PR will trigger the release workflow.
135133
- **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.
136134
- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.
137135
- `feat(docs)` goes in Docs when the user-facing surface is the docs site.
136+
- **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.
137+
- **Describe behaviour, not resolution logic.** An entry states what a user now observes. Rules the implementation follows internally (target-selection signals, config precedence, detection order) belong in the RFC or the PR, not the changelog. If an entry needs a nested list to explain how a decision is reached, cut it down to the outcome.
138+
- **A breaking change needs its migration path.** State what existing installs or projects do by default, then how to move to the new behaviour deliberately, then what that costs. Link the guide rather than restating it, and say plainly when doing nothing is a valid choice.
138139
- Highlights: 3-5 changes a vite-plus user will notice (new capabilities, security, major fixes). Skip developer-tooling-only conveniences. Each highlight ends with `, by @<author>`, same as every other entry.
139140
- Entry format: `Description ([#N](https://github.com/voidzero-dev/vite-plus/pull/N)), by @author`. Describe the user-visible behavior, not the implementation. Group supporting implementation PRs under the user-visible change they enable instead of giving them separate entries. Never include defensive edge cases or internal mechanics unless users need them to use or understand the feature; use concrete behavior instead of internal UI taxonomy that needs extra context.
140141
- **Upstream dependency upgrade PRs** (`feat(deps): upgrade upstream dependencies`): consolidate all of them into one Features entry with net oldest-to-latest version changes (e.g. `vite 8.0.16 -> 8.1.2`), listing every PR number. Check the upgraded range for security fixes (search the upstream changelog for CVE/GHSA); if present, add a dedicated security entry quoting severity and linking the advisory. When oxfmt or oxlint changed version, add one clause telling users the new versions can flag code that passed before, so they should run `vp fmt` after upgrading if their CI runs `vp check`; in ecosystem testing this is reliably the largest single class of post-upgrade CI failures.
@@ -346,10 +347,27 @@ Merging the release PR is the release trigger. Before merging confirm: CI green,
346347
5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).
347348
6. `discord-notify`: announces to Discord with a link to the release.
348349

350+
**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.
351+
352+
Check visibility directly, not through `npm view`, which caches:
353+
354+
```bash
355+
for pkg in '@voidzero-dev%2Fvite-plus-core' 'vite-plus'; do
356+
curl -s -H 'Cache-Control: no-cache' "https://registry.npmjs.org/$pkg?t=$(date +%s)" |
357+
python3 -c "import json,sys;d=json.load(sys.stdin);print('$pkg', d['dist-tags'].get('latest'), 'X.Y.Z' in d['versions'])"
358+
done
359+
```
360+
361+
Both must report `True` before you trust the release. A stale `modified` timestamp on the packument is the giveaway that nothing landed. If `vite-plus` is visible and `core` is not, the release is broken **right now** for every new install: tell the release manager immediately and offer to move the tag back (`npm dist-tag add vite-plus@<prev> latest`) while the publish is sorted out. Confirm the fix with a real install in a temp directory, not just a registry read:
362+
363+
```bash
364+
d=$(mktemp -d); cd "$d" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund
365+
```
366+
349367
## 8. Post-release
350368

351369
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:
352-
- Drop the `Release vite-plus vX.Y.Z: ...` opener line (the release title carries it) and the closing `---` / `Merging this PR ...` boilerplate.
370+
- Drop the closing `---` / `Merging this PR ...` boilerplate.
353371
- Keep every changelog section through **Full Changelog** unchanged.
354372
- Append the generated Published Packages and Installation sections, and end Installation with a Docker usage block (keep the explanation to one short sentence):
355373

@@ -432,7 +450,7 @@ Merging the release PR is the release trigger. Before merging confirm: CI green,
432450
433451
The release-notes URL stays in `<angle brackets>` to suppress the embed; a blog post link (if any) goes bare so it unfurls. Lead the header with the server custom emoji `:viteplus:` (before the bold title, since it is a custom emoji). Link contributors as `[@user](https://github.com/user)` because Discord does not auto-link a bare GitHub handle. Keep the whole message user-facing: exclude vite-plus's own tooling/CI work.
434452

435-
Never post to Discord yourself. Save the draft to a file, update that file after every requested revision, and post the approved contents as a comment on the release PR wrapped in a fenced ` ```markdown ` block, so the `@mentions` do not ping anyone on GitHub, the emoji shortcodes stay literal, and any team member can copy-paste it into Discord. After the release manager approves the Discord draft, proceed directly to step 9; do not wait for another prompt or treat the skill update as optional.
453+
Never post to Discord yourself. Save the draft to a file, update that file after every requested revision, and hand the approved contents over in chat. Do **not** post it as a comment on the release PR: that PR is a code-review artifact, and an announcement draft there is noise for reviewers and a second copy that can drift from the approved wording. After the release manager approves the Discord draft, proceed directly to step 9; do not wait for another prompt or treat the skill update as optional.
436454

437455
## 9. Update this skill (post-release)
438456

@@ -453,5 +471,5 @@ After the release ships and the Discord announcement draft is approved, review t
453471
- [ ] Release PR merged; `release` environment approved by someone other than the merger; npm + GitHub release + Docker image all published
454472
- [ ] GitHub release notes polished (release manager approved before applying), retitled, and validated; Installation ends with the Docker usage block
455473
- [ ] Installs verified (npm versions + latest tag, `vp upgrade`, `vp --version` output inside the ghcr Docker image)
456-
- [ ] Discord announcement drafted (concise only) and shared as a fenced code block comment on the release PR
474+
- [ ] Discord announcement drafted (concise only) and handed over in chat, not posted to the release PR
457475
- [ ] Skill reviewed for durable learnings; any that generalize folded in and a `docs(skill)` PR proposed

.github/workflows/e2e-test.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ jobs:
327327
vp check --fix
328328
vp run check
329329
vp test run --project unit --shard=1/3
330+
- name: videojs-v10
331+
node-version: 24
332+
command: |
333+
node $GITHUB_WORKSPACE/ecosystem-ci/verify-videojs-v10.ts
334+
# The upstream preset keeps existing diagnostics at warning level.
335+
vp lint --quiet
336+
vp test run tools/oxlint/anti-slop/rules/tests
330337
- name: reactive-resume
331338
node-version: 24
332339
command: |

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Authored against the API vite-plus re-exports, with no `@oxlint/plugins`
2+
// dependency of its own: the point of the test is that this resolves and loads.
3+
import { definePlugin, defineRule } from 'vite-plus/lint/plugins';
4+
5+
const noFoo = defineRule({
6+
meta: { messages: { noFoo: 'Do not name things "foo".' } },
7+
create(context) {
8+
return {
9+
Identifier(node) {
10+
if (node.name === 'foo') {
11+
context.report({ node, messageId: 'noFoo' });
12+
}
13+
},
14+
};
15+
},
16+
});
17+
18+
export default definePlugin({
19+
meta: { name: 'local' },
20+
rules: { 'no-foo': noFoo },
21+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "lint-oxlint-plugin-api",
3+
"version": "0.0.0",
4+
"private": true
5+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[[case]]
2+
name = "lint_oxlint_plugin_api"
3+
vp = "local"
4+
skip-platforms = [{ os = "linux", libc = "musl" }]
5+
steps = [
6+
{ argv = [
7+
"vp",
8+
"lint",
9+
"src/uses-foo.ts",
10+
], comment = "the local JS plugin imports its API from vite-plus/lint/plugins. It declares no @oxlint/plugins dependency. A reported diagnostic therefore proves the export resolved and loaded", continue-on-failure = true },
11+
{ argv = [
12+
"vp",
13+
"lint",
14+
"src/legacy-imports.ts",
15+
], comment = "prefer-vite-plus-imports reports the three legacy authoring specifiers", continue-on-failure = true },
16+
{ argv = [
17+
"vp",
18+
"lint",
19+
"src/config-surface.ts",
20+
], comment = "oxlint still owns defineConfig and OxlintOverride, so these are clean", continue-on-failure = true },
21+
{ argv = [
22+
"vp",
23+
"lint",
24+
"--fix",
25+
"src/legacy-imports.ts",
26+
], comment = "the autofix matches what vp migrate rewrites", continue-on-failure = true },
27+
{ argv = [
28+
"vpt",
29+
"print-file",
30+
"src/legacy-imports.ts",
31+
], continue-on-failure = true },
32+
{ argv = [
33+
"vp",
34+
"lint",
35+
"src/legacy-imports.ts",
36+
], comment = "confirm the rewritten file is clean", continue-on-failure = true },
37+
]
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# lint_oxlint_plugin_api
2+
3+
## `vp lint src/uses-foo.ts`
4+
5+
the local JS plugin imports its API from vite-plus/lint/plugins. It declares no @oxlint/plugins dependency. A reported diagnostic therefore proves the export resolved and loaded
6+
7+
**Exit code:** 1
8+
9+
```
10+
11+
× local(no-foo): Do not name things "foo".
12+
╭─[src/uses-foo.ts:1:14]
13+
1 │ export const foo = 1;
14+
· ───
15+
2 │ export const bar = 2;
16+
╰────
17+
18+
Found 0 warnings and 1 error.
19+
Finished in <duration> on 1 file with <n> rules using <n> threads.
20+
```
21+
22+
## `vp lint src/legacy-imports.ts`
23+
24+
prefer-vite-plus-imports reports the three legacy authoring specifiers
25+
26+
**Exit code:** 1
27+
28+
```
29+
30+
× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of 'oxlint' in Vite+ projects.
31+
╭─[src/legacy-imports.ts:1:28]
32+
1 │ import { defineRule } from 'oxlint';
33+
· ────────
34+
2 │ import { definePlugin } from '@oxlint/plugins';
35+
╰────
36+
37+
× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of '@oxlint/plugins' in Vite+ projects.
38+
╭─[src/legacy-imports.ts:2:30]
39+
1 │ import { defineRule } from 'oxlint';
40+
2 │ import { definePlugin } from '@oxlint/plugins';
41+
· ─────────────────
42+
3 │ import { RuleTester } from 'oxlint/plugins-dev';
43+
╰────
44+
45+
× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins-dev' instead of 'oxlint/plugins-dev' in Vite+ projects.
46+
╭─[src/legacy-imports.ts:3:28]
47+
2 │ import { definePlugin } from '@oxlint/plugins';
48+
3 │ import { RuleTester } from 'oxlint/plugins-dev';
49+
· ────────────────────
50+
4 │
51+
╰────
52+
53+
× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of 'oxlint' in Vite+ projects.
54+
╭─[src/legacy-imports.ts:6:38]
55+
5 │ export { defineRule, definePlugin, RuleTester };
56+
6 │ export { 'defineRule' as rule } from 'oxlint';
57+
· ────────
58+
7 │ export type { 'Context' as RuleContext } from 'oxlint';
59+
╰────
60+
61+
× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of 'oxlint' in Vite+ projects.
62+
╭─[src/legacy-imports.ts:7:47]
63+
6 │ export { 'defineRule' as rule } from 'oxlint';
64+
7 │ export type { 'Context' as RuleContext } from 'oxlint';
65+
· ────────
66+
╰────
67+
68+
Found 0 warnings and 5 errors.
69+
Finished in <duration> on 1 file with <n> rules using <n> threads.
70+
```
71+
72+
## `vp lint src/config-surface.ts`
73+
74+
oxlint still owns defineConfig and OxlintOverride, so these are clean
75+
76+
```
77+
Found 0 warnings and 0 errors.
78+
Finished in <duration> on 1 file with <n> rules using <n> threads.
79+
```
80+
81+
## `vp lint --fix src/legacy-imports.ts`
82+
83+
the autofix matches what vp migrate rewrites
84+
85+
```
86+
Found 0 warnings and 0 errors.
87+
Finished in <duration> on 1 file with <n> rules using <n> threads.
88+
```
89+
90+
## `vpt print-file src/legacy-imports.ts`
91+
92+
```
93+
import { defineRule } from 'vite-plus/lint/plugins';
94+
import { definePlugin } from 'vite-plus/lint/plugins';
95+
import { RuleTester } from 'vite-plus/lint/plugins-dev';
96+
97+
export { defineRule, definePlugin, RuleTester };
98+
export { 'defineRule' as rule } from 'vite-plus/lint/plugins';
99+
export type { 'Context' as RuleContext } from 'vite-plus/lint/plugins';
100+
```
101+
102+
## `vp lint src/legacy-imports.ts`
103+
104+
confirm the rewritten file is clean
105+
106+
```
107+
Found 0 warnings and 0 errors.
108+
Finished in <duration> on 1 file with <n> rules using <n> threads.
109+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'oxlint';
2+
import type { OxlintOverride } from 'oxlint';
3+
4+
export const override: OxlintOverride = { files: ['**/*.ts'] };
5+
6+
export default defineConfig({ overrides: [override] });
7+
export { 'defineConfig' as config } from 'oxlint';
8+
export type { 'OxlintOverride' as Override } from 'oxlint';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineRule } from 'oxlint';
2+
import { definePlugin } from '@oxlint/plugins';
3+
import { RuleTester } from 'oxlint/plugins-dev';
4+
5+
export { defineRule, definePlugin, RuleTester };
6+
export { 'defineRule' as rule } from 'oxlint';
7+
export type { 'Context' as RuleContext } from 'oxlint';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const foo = 1;
2+
export const bar = 2;

0 commit comments

Comments
 (0)