Skip to content

Commit 55918b2

Browse files
authored
Merge branch 'voidzero-dev:main' into feat/cli-completion
2 parents 641a3a6 + cc37ea4 commit 55918b2

23 files changed

Lines changed: 449 additions & 64 deletions

File tree

.claude/skills/add-ecosystem-ci/SKILL.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ gh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'
2525

2626
Fetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).
2727

28-
### 2.2 Auto-detect Commands from GitHub Workflows
28+
### 2.2 Check if Project Already Uses Vite-Plus
29+
30+
Check the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VITE_PLUS_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with "already using Vite+".
31+
32+
### 2.3 Auto-detect Commands from GitHub Workflows
2933

3034
Fetch the project's GitHub workflow files to detect available commands:
3135

@@ -43,7 +47,7 @@ Look for common patterns in workflow files:
4347
- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`
4448
- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.
4549

46-
### 2.3 Ask User to Confirm
50+
### 2.4 Ask User to Confirm
4751

4852
Present the auto-detected configuration and ask user to confirm or modify:
4953

@@ -62,7 +66,8 @@ Present the auto-detected configuration and ask user to confirm or modify:
6266
"repository": "https://github.com/owner/repo.git",
6367
"branch": "main",
6468
"hash": "full-commit-sha",
65-
"directory": "web" // only if subdirectory is needed
69+
"directory": "web", // only if subdirectory is needed
70+
"forceFreshMigration": true // only if project already uses vite-plus
6671
}
6772
}
6873
```
@@ -110,4 +115,5 @@ node ecosystem-ci/clone.ts project-name
110115
- The `directory` field is optional - only add it if the package.json is not in the project root
111116
- If `directory` is specified in repo.json, it must also be specified in the workflow matrix
112117
- `patch-project.ts` automatically handles running `vp migrate` in the correct directory
118+
- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VITE_PLUS_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping
113119
- OS exclusions are added to the existing `exclude` section in the workflow matrix

.github/workflows/claude.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
analyze:
9-
if: github.repository == 'voidzero-dev/vite-plus' && github.event.action == 'assigned' && github.event.assignee.login == 'boshen' && github.event.sender.login == 'boshen'
9+
if: github.repository == 'voidzero-dev/vite-plus' && github.event.action == 'assigned' && github.event.assignee.login == 'boshen'
1010
runs-on: ubuntu-slim
1111
permissions:
1212
contents: read

.github/workflows/e2e-test.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,13 @@ jobs:
257257
vp check
258258
vp pack
259259
vp test
260+
- name: vinext
261+
node-version: 24
262+
command: |
263+
vp run build
264+
vp check --fix
265+
vp run check
266+
vp run test
260267
exclude:
261268
# frm-stack uses Docker (testcontainers) which doesn't work the same way on Windows
262269
- os: windows-latest
@@ -266,6 +273,10 @@ jobs:
266273
- os: windows-latest
267274
project:
268275
name: dify
276+
# vinext uses workerd native deps that don't build on Windows
277+
- os: windows-latest
278+
project:
279+
name: vinext
269280

270281
steps:
271282
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
@@ -310,6 +321,11 @@ jobs:
310321
node $GITHUB_WORKSPACE/ecosystem-ci/patch-project.ts ${{ matrix.project.name }}
311322
vp install --no-frozen-lockfile
312323
324+
- name: Verify local tgz packages installed
325+
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}
326+
shell: bash
327+
run: node $GITHUB_WORKSPACE/ecosystem-ci/verify-install.ts
328+
313329
- name: Run vite-plus commands in ${{ matrix.project.name }}
314330
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}
315331
run: ${{ matrix.project.command }}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Issue Close Require
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * *'
6+
7+
jobs:
8+
close-issues:
9+
if: github.repository == 'voidzero-dev/vite-plus'
10+
runs-on: ubuntu-slim
11+
permissions:
12+
issues: write # for actions-cool/issues-helper to update issues
13+
pull-requests: write # for actions-cool/issues-helper to update PRs
14+
steps:
15+
- name: needs reproduction
16+
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
17+
with:
18+
actions: 'close-issues'
19+
token: ${{ secrets.GITHUB_TOKEN }}
20+
labels: 'needs reproduction'
21+
inactive-day: 3
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Issue Labeled
2+
3+
on:
4+
issues:
5+
types: [labeled]
6+
7+
jobs:
8+
reply-labeled:
9+
if: github.repository == 'voidzero-dev/vite-plus'
10+
runs-on: ubuntu-slim
11+
permissions:
12+
issues: write # for actions-cool/issues-helper to update issues
13+
pull-requests: write # for actions-cool/issues-helper to update PRs
14+
steps:
15+
- name: contribution welcome
16+
if: github.event.label.name == 'contribution welcome' || github.event.label.name == 'help wanted'
17+
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
18+
with:
19+
actions: 'remove-labels'
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
issue-number: ${{ github.event.issue.number }}
22+
labels: 'pending triage, needs reproduction'
23+
24+
- name: needs reproduction
25+
if: github.event.label.name == 'needs reproduction'
26+
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3
27+
with:
28+
actions: 'create-comment, remove-labels'
29+
token: ${{ secrets.GITHUB_TOKEN }}
30+
issue-number: ${{ github.event.issue.number }}
31+
labels: 'pending triage'
32+
body: |
33+
Hello @${{ github.event.issue.user.login }} 👋
34+
35+
Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository. This helps us understand and resolve your issue much faster.
36+
37+
**A good reproduction should be:**
38+
- **Minimal** – include only the code necessary to demonstrate the issue
39+
- **Complete** – contain everything needed to run and observe the problem
40+
- **Reproducible** – consistently show the issue with clear steps
41+
42+
If no reproduction is provided, issues labeled `needs reproduction` will be closed after 3 days of inactivity.
43+
44+
For more context on why this is required, please read: https://antfu.me/posts/why-reproductions-are-required

.github/workflows/test-standalone-install.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,41 @@ jobs:
114114
vp upgrade --rollback
115115
vp --version
116116
117+
test-install-sh-readonly-config:
118+
name: Test install.sh (readonly shell config)
119+
runs-on: ubuntu-latest
120+
permissions:
121+
contents: read
122+
steps:
123+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
124+
125+
- name: Make shell config files read-only
126+
run: |
127+
# Simulate Nix-managed or read-only shell configs
128+
touch ~/.bashrc ~/.bash_profile ~/.profile
129+
chmod 444 ~/.bashrc ~/.bash_profile ~/.profile
130+
131+
- name: Run install.sh
132+
run: |
133+
output=$(cat packages/cli/install.sh | bash 2>&1) || {
134+
echo "$output"
135+
echo "Install script exited with non-zero status"
136+
exit 1
137+
}
138+
echo "$output"
139+
# Verify installation succeeds (not a fatal error)
140+
echo "$output" | grep -q "successfully installed"
141+
# Verify fallback message shows binary location
142+
echo "$output" | grep -q "vp was installed to:"
143+
# Verify fallback message shows manual instructions
144+
echo "$output" | grep -q "Or run vp directly:"
145+
# Verify the permission warning was shown
146+
echo "$output" | grep -qi "permission denied"
147+
148+
- name: Verify vp works via direct path
149+
run: |
150+
~/.vite-plus/bin/vp --version
151+
117152
test-install-sh-arm64:
118153
name: Test install.sh (Linux ARM64 glibc via QEMU)
119154
runs-on: ubuntu-latest

crates/vite_global_cli/src/commands/version.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ use std::{
1010
use owo_colors::OwoColorize;
1111
use serde::Deserialize;
1212
use vite_install::get_package_manager_type_and_version;
13-
use vite_js_runtime::{VersionSource, resolve_node_version};
1413
use vite_path::AbsolutePathBuf;
1514
use vite_workspace::find_workspace_root;
1615

17-
use crate::{error::Error, help};
16+
use crate::{commands::env::config::resolve_version, error::Error, help};
1817

1918
#[derive(Debug, Deserialize)]
2019
#[serde(rename_all = "camelCase")]
@@ -133,14 +132,31 @@ fn format_version(version: Option<String>) -> String {
133132
}
134133

135134
async fn get_node_version_info(cwd: &AbsolutePathBuf) -> Option<(String, String)> {
136-
let resolution_opt = resolve_node_version(cwd, true).await.ok()?;
137-
let resolution = resolution_opt?;
138-
let source_label = match resolution.source {
139-
VersionSource::NodeVersionFile => ".node-version",
140-
VersionSource::EnginesNode => "engines.node",
141-
VersionSource::DevEnginesRuntime => "devEngines.runtime",
142-
};
143-
Some((resolution.version.to_string(), source_label.to_string()))
135+
// Try the full managed resolution chain
136+
if let Ok(resolution) = resolve_version(cwd).await {
137+
return Some((resolution.version, resolution.source));
138+
}
139+
140+
// Fallback: detect system Node version (with VITE_PLUS_BYPASS to avoid hitting the shim)
141+
let version = detect_system_node_version()?;
142+
Some((version, "system".to_string()))
143+
}
144+
145+
fn detect_system_node_version() -> Option<String> {
146+
let output = std::process::Command::new("node")
147+
.arg("--version")
148+
.env(vite_shared::env_vars::VITE_PLUS_BYPASS, "1")
149+
.output()
150+
.ok()?;
151+
if !output.status.success() {
152+
return None;
153+
}
154+
let version = String::from_utf8(output.stdout).ok()?;
155+
let version = version.trim().strip_prefix('v').unwrap_or(version.trim());
156+
if version.is_empty() {
157+
return None;
158+
}
159+
Some(version.to_string())
144160
}
145161

146162
/// Execute the `--version` command.
@@ -182,7 +198,10 @@ pub async fn execute(cwd: AbsolutePathBuf) -> Result<ExitStatus, Error> {
182198

183199
let node_info = get_node_version_info(&cwd)
184200
.await
185-
.map(|(v, s)| format!("v{v} ({s})"))
201+
.map(|(v, s)| match s.as_str() {
202+
"lts" | "default" | "system" => format!("v{v}"),
203+
_ => format!("v{v} ({s})"),
204+
})
186205
.unwrap_or(NOT_FOUND.to_string());
187206

188207
let env_rows = [("Package manager", package_manager_info), ("Node.js", node_info)];
@@ -197,9 +216,9 @@ mod tests {
197216
#[cfg(unix)]
198217
use std::{fs, path::Path};
199218

200-
use super::format_version;
201219
#[cfg(unix)]
202220
use super::{ToolSpec, find_local_vite_plus, resolve_tool_version};
221+
use super::{detect_system_node_version, format_version};
203222

204223
#[cfg(unix)]
205224
fn symlink_dir(src: &Path, dst: &Path) {
@@ -212,6 +231,15 @@ mod tests {
212231
assert_eq!(format_version(None), "Not found");
213232
}
214233

234+
#[test]
235+
fn detect_system_node_version_returns_version() {
236+
let version = detect_system_node_version();
237+
assert!(version.is_some(), "expected node to be installed");
238+
let version = version.unwrap();
239+
assert!(!version.starts_with('v'), "version should not have v prefix");
240+
assert!(version.contains('.'), "expected semver-like version, got: {version}");
241+
}
242+
215243
#[cfg(unix)]
216244
#[test]
217245
fn resolves_tool_versions_from_pnpm_symlink_layout() {

docs/guide/pack.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ See the [tsdown guide](https://tsdown.dev/guide/) and the [tsdown config file do
2525
Use it for:
2626

2727
- [declaration files (`dts`)](https://tsdown.dev/options/dts)
28-
- [output formats](https://tsdown.dev/options/format)
29-
- [watch mode](https://tsdown.dev/options/watch)
28+
- [output formats](https://tsdown.dev/options/output-format)
29+
- [watch mode](https://tsdown.dev/options/watch-mode)
3030
- [standalone executables](https://tsdown.dev/options/exe#executable)
3131

3232
```ts

ecosystem-ci/patch-project.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,6 @@ const cwd = directory ? join(repoRoot, directory) : repoRoot;
2121
// run vp migrate
2222
const cli = process.env.VITE_PLUS_CLI_BIN ?? 'vp';
2323

24-
// Projects that already have vite-plus need it removed before migration so
25-
// vp migrate treats them as fresh and applies tgz overrides. Without this,
26-
// vp migrate detects "already using Vite+" and skips override injection.
27-
const forceFreshMigration = 'forceFreshMigration' in repoConfig && repoConfig.forceFreshMigration;
28-
if (forceFreshMigration) {
29-
const pkgPath = join(cwd, 'package.json');
30-
const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
31-
delete pkg.devDependencies?.['vite-plus'];
32-
delete pkg.dependencies?.['vite-plus'];
33-
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
34-
}
35-
3624
if (project === 'rollipop') {
3725
const oxfmtrc = await readFile(join(repoRoot, '.oxfmtrc.json'), 'utf-8');
3826
await writeFile(
@@ -42,11 +30,16 @@ if (project === 'rollipop') {
4230
);
4331
}
4432

33+
// Projects that already use vite-plus need VITE_PLUS_FORCE_MIGRATE=1 so
34+
// vp migrate runs full dependency rewriting instead of skipping.
35+
const forceFreshMigration = 'forceFreshMigration' in repoConfig && repoConfig.forceFreshMigration;
36+
4537
execSync(`${cli} migrate --no-agent --no-interactive`, {
4638
cwd,
4739
stdio: 'inherit',
4840
env: {
4941
...process.env,
42+
...(forceFreshMigration ? { VITE_PLUS_FORCE_MIGRATE: '1' } : {}),
5043
VITE_PLUS_OVERRIDE_PACKAGES: JSON.stringify({
5144
vite: `file:${tgzDir}/voidzero-dev-vite-plus-core-0.0.0.tgz`,
5245
vitest: `file:${tgzDir}/voidzero-dev-vite-plus-test-0.0.0.tgz`,

ecosystem-ci/repo.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"vibe-dashboard": {
1414
"repository": "https://github.com/voidzero-dev/vibe-dashboard.git",
1515
"branch": "main",
16-
"hash": "fcb0518e75e0f05e09ac910dcc88090220dfd3ae"
16+
"hash": "158e4a0c3d8a1801e330300a5deba4506fd5dfb9",
17+
"forceFreshMigration": true
1718
},
1819
"rollipop": {
1920
"repository": "https://github.com/leegeunhyeok/rollipop.git",
@@ -64,6 +65,13 @@
6465
"vp-config": {
6566
"repository": "https://github.com/kazupon/vp-config.git",
6667
"branch": "main",
67-
"hash": "b58c48d71a17c25dec71a003535e6312791ce2aa"
68+
"hash": "b58c48d71a17c25dec71a003535e6312791ce2aa",
69+
"forceFreshMigration": true
70+
},
71+
"vinext": {
72+
"repository": "https://github.com/cloudflare/vinext.git",
73+
"branch": "main",
74+
"hash": "f78dd2b39f5b02242417e0a684b1f2f55d3dbdff",
75+
"forceFreshMigration": true
6876
}
6977
}

0 commit comments

Comments
 (0)