Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1164,10 +1164,27 @@ jobs:
export VP_SNAP_SH_BIN="$(command -v sh)"
export VP_SNAP_BASH_BIN="$(command -v bash)"
export VP_SNAP_ZSH_BIN="$(command -v zsh)"
VP_SNAP_GLOBAL_VP="$HOME/.vite-plus/bin/vp" \
VP_SNAP_JS_RUNTIME_DIR="$HOME/.vite-plus/js_runtime" \
VP_SNAP_FISH_BIN="$(command -v fish)" \
VP_SNAP_NU_BIN="$(command -v nu)" \
export VP_SNAP_GLOBAL_VP="$HOME/.vite-plus/bin/vp"
export VP_SNAP_JS_RUNTIME_DIR="$HOME/.vite-plus/js_runtime"
# Missing optional shells must leave their overrides unset.
if VP_SNAP_FISH_BIN="$(command -v fish)"; then
export VP_SNAP_FISH_BIN
else
unset VP_SNAP_FISH_BIN
fi
if VP_SNAP_NU_BIN="$(command -v nu)"; then
export VP_SNAP_NU_BIN
else
unset VP_SNAP_NU_BIN
fi
# Fetch pinned package-manager archives before command timers start.
# Cases still download from the registry into their isolated homes.
VP_SNAP_PREWARM_MANIFEST="$RUNNER_TEMP/snapshot-prewarm.json" \
./target/snapshot-tests/cli_snapshots --list > "$RUNNER_TEMP/snapshot-test-list.txt"
node packages/tools/src/local-npm-registry.ts \
--snapshot-cache "$VP_SNAP_REGISTRY_CACHE_DIR" \
--request-log "$VP_SNAP_ARTIFACTS_DIR/prewarm/registry.jsonl" \
--prewarm-manifest "$RUNNER_TEMP/snapshot-prewarm.json"
./target/snapshot-tests/cli_snapshots --test-threads 8
env:
RUST_BACKTRACE: '1'
Expand All @@ -1177,6 +1194,7 @@ jobs:
CARGO_BIN_EXE_vpt: ${{ github.workspace }}/target/snapshot-tests/vpt
VP_SNAP_SHARD: ${{ matrix.shard }}/3
VP_SNAP_ARTIFACTS_DIR: ${{ runner.temp }}/snapshot-artifacts
VP_SNAP_REGISTRY_CACHE_DIR: ${{ runner.temp }}/snapshot-registry-cache

- name: Upload snapshot diagnostics and timings
if: ${{ !cancelled() }}
Expand Down Expand Up @@ -1318,7 +1336,7 @@ jobs:
VP_SNAP_PREWARM_MANIFEST="$RUNNER_TEMP/snapshot-prewarm.json" \
cargo-nextest nextest list --archive-file windows-snapshot-tests.tar.zst --workspace-remap . \
> "$RUNNER_TEMP/snapshot-test-list.txt"
# Derive Bun archives from the same fixtures as the test list, so
# Derive package-manager archives from the same fixtures as the test list, so
# version changes need no matching CI edit. Only response bytes are shared.
node packages/tools/src/local-npm-registry.ts \
--snapshot-cache "$VP_SNAP_REGISTRY_CACHE_DIR" \
Expand Down
24 changes: 17 additions & 7 deletions .github/workflows/upgrade-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
uses: anthropics/claude-code-action@1d6de8cb0c237e7c15e9e1bdf973826ebae490cc # v1.0.248
env:
ANTHROPIC_BASE_URL: https://api.kimi.com/coding/
BASH_MAX_TIMEOUT_MS: '1800000'
CLI_HELP_DIFF_CHANGED: ${{ steps.cli-help-diff.outputs.has-changes }}
CLI_HELP_DIFF_REPORT: ${{ env.UPGRADE_DEPS_META_DIR }}/cli-help-report.md
RELEASE_BUILD: 'true'
Expand Down Expand Up @@ -166,8 +167,11 @@ jobs:
upgrade operations: `pnpm dedupe`,
`node .github/scripts/upgrade-deps.ts --sync-bundled-deps`, then
`pnpm install --no-frozen-lockfile` before building or testing.
2. Re-run the steps in `./.github/actions/build-upstream/action.yml`; fix any
non-zero exits. If the Rust build fails inside a vendored `rolldown`
2. Reuse successful outputs from `./.github/actions/build-upstream/action.yml`.
If the action failed, fix the failure and complete its remaining build commands.
Rebuild affected packages after source or dependency changes. Preserve the
action's target, profile, compiler, and linker settings for native builds.
If the Rust build fails inside a vendored `rolldown`
crate with an oxc API mismatch (e.g. `oxc_ast::template_element` taking
a different number of arguments, or any `oxc_*` type/signature error),
the root `Cargo.toml` oxc pins are out of sync with the bumped rolldown.
Expand Down Expand Up @@ -195,7 +199,9 @@ jobs:
- `echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH`
8. If any Rust code or `Cargo.toml` was modified, run `cargo check
--all-targets --all-features` and `cargo shear`; fix anything they report.
9. Run `pnpm run lint` (requires a prior `just build`); fix any errors.
9. Run `pnpm run lint`; fix any errors. Reuse the completed build outputs
from step 2 and rebuild packages affected by later changes first.
Do not run `just build` only to prepare for lint.
10. Smoke-test the CLI: `vp -h`, `vp run -h`, `vp lint -h`, `vp test -h`,
`vp build -h`, `vp fmt -h`, `vp pack -h`.

Expand Down Expand Up @@ -234,8 +240,11 @@ jobs:
### Running long commands (IMPORTANT)
Run every long-running command (`just build`, `pnpm bootstrap-cli:ci`,
`pnpm test:unit`, `cargo check`, etc.) in the FOREGROUND — a single Bash tool call
that blocks until the command exits. The Bash tool already gives you a 10-minute
timeout per call, which is enough for these builds.
that blocks until the command exits. Set `timeout: 1800000` explicitly on
each long-running Bash call. `BASH_MAX_TIMEOUT_MS` permits 30 minutes per call;
it does not change the default timeout or a shorter timeout you request.
Stream command output. If saving a log, use `set -o pipefail` with `tee`
so progress remains visible and command failures keep a non-zero exit code.

Do NOT do any of the following:
- Spawn a background process (`&`, `nohup`, `disown`, `setsid`, `screen`, `tmux`).
Expand All @@ -246,8 +255,9 @@ jobs:
whether a build succeeded, just run it in the foreground and read its exit
code and stdout/stderr in the single tool result.

If a foreground command legitimately hits the 10-minute Bash timeout, report
that and stop — do not start polling.
If a foreground command reaches the 30-minute Bash timeout, stop any task
the tool moved to the background. Report the command and its last build output,
then stop — do not start polling.

### Commit rule
Do NOT run `git commit` or `git push`. A later workflow step commits every
Expand Down
25 changes: 14 additions & 11 deletions crates/vp_cli_snapshots/tests/cli_snapshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Environment overrides, mainly for CI:
| `VP_SNAP_PACKAGES_DIR` | Run-scoped directory for sharing packed packages across test processes |
| `VP_SNAP_ARTIFACTS_DIR` | Directory for phase timings and failure diagnostics; unset disables artifacts |
| `VP_SNAP_NEXTEST_CONFIG` | With `--list`, write nextest overrides for the discovered isolated cases |
| `VP_SNAP_PREWARM_MANIFEST` | With `--list`, write a JSON array of Bun archive URLs derived from runnable cases |
| `VP_SNAP_PREWARM_MANIFEST` | With `--list`, write a JSON array of package-manager archive URLs derived from runnable cases |
| `VP_NODE_DIST_MIRROR` | Node.js distribution mirror inherited by case commands; case and step overrides take precedence |

Windows CI generates its nextest configuration from the same case definitions.
Expand Down Expand Up @@ -142,16 +142,19 @@ including across nextest processes when `VP_SNAP_REGISTRY_CACHE_DIR` is set.
Use a fresh directory per run; this is not a persistent dependency cache.
Package-manager caches and installed packages remain isolated per case.

Windows CI generates a prewarm manifest while listing cases, then passes it to
CI generates a prewarm manifest while listing cases, then passes it to
`local-npm-registry.ts --prewarm-manifest <file>` before starting test commands.
The runner collects exact Bun pins from each case's package ancestry
(`packageManager` and `devEngines.packageManager`), case/step environment overrides,
`vpt json-edit` of package-manager declarations, `vpt write-file` of package files,
and `vp env default/use/exec` arguments. Versions are deduplicated and archives
match the runner platform, including the x64 baseline variant. Changing these
fixture pins automatically updates preparation; CI contains no separate version list.

Ignored and platform-excluded cases do not contribute pins. Set `prewarm-bun = false`
The runner collects exact Bun pins and exact pnpm pins below version 12 from each
case's package ancestry (`packageManager` and `devEngines.packageManager`), case
and step environment overrides, `vpt json-edit` of package-manager declarations,
`vpt write-file` of package files, and `vp env default/use/exec` arguments.
Changing these fixture pins automatically updates preparation; CI contains no
separate version list.

Versions are deduplicated. Bun archives match the runner platform, including the
x64 baseline variant. Native pnpm 12+ archives still use normal registry resolution.

Ignored and platform-excluded cases do not contribute pins. Set `prewarm-package-managers = false`
for fake installations and configuration-only cases. Script-generated pins must use
declarative setup or environment overrides to participate; discovery does not execute
scripts or scan assertions. Floating tags and version ranges retain normal registry
Expand Down Expand Up @@ -239,7 +242,7 @@ requires = ["bash"] # "sh" | "bash" | "zsh" | "cmd" | "fish" | "nu" |
ignore = false # true: only runs with `-- --ignored`
serial = false # true: run alone across cargo test/nextest workers
seed-runtime = true # false: start from an empty VP_HOME
prewarm-bun = true # false: fake install or configuration-only case
prewarm-package-managers = true # false: fake install or configuration-only case
link-node-modules = false # true: expose the run-root node_modules as
# the workspace's parent-dir node_modules,
# for `../node_modules/vite-plus/...` paths
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[case]]
name = "command_env_package_manager_modes"
prewarm-bun = false # Inspect configuration without downloading Bun.
prewarm-package-managers = false # Inspect configuration without downloading package managers.
vp = "global"
seed-runtime = false
steps = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[case]]
name = "pm_ci_package_managers"
prewarm-bun = false # This case installs fake package managers.
prewarm-package-managers = false # This case installs fake package managers.
vp = "global"
comment = """
Covers `vp pm ci` command delegation for each supported package manager.
Expand Down
20 changes: 10 additions & 10 deletions crates/vp_cli_snapshots/tests/cli_snapshots/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ struct Case {
#[serde(default, rename = "isolated-registry")]
isolated_registry: bool,
/// Disable archive preparation for fake installations and configuration-only cases.
#[serde(default = "default_true", rename = "prewarm-bun")]
prewarm_bun: bool,
#[serde(default = "default_true", rename = "prewarm-package-managers")]
prewarm_package_managers: bool,
/// Seed the case's `VP_HOME` with an already-provisioned managed JS
/// runtime (see `flavor::js_runtime_seed_dir`). Default true; runtime
/// provisioning tests set false to start from a genuinely empty home.
Expand Down Expand Up @@ -1983,7 +1983,7 @@ fn main() {
let mut isolated_trials = BTreeSet::new();
let prewarm_manifest =
args.list.then(|| std::env::var_os("VP_SNAP_PREWARM_MANIFEST")).flatten();
let mut bun_prewarm = prewarm::BunPrewarm::default();
let mut archives = prewarm::PackageManagerPrewarm::default();
for fixture_path in fixture_paths {
let fixture_path: Arc<Path> = Arc::from(fixture_path.as_path());
let fixture_name: Arc<str> = Arc::from(fixture_path.file_name().unwrap().to_str().unwrap());
Expand Down Expand Up @@ -2019,22 +2019,22 @@ fn main() {
let ignored = case.ignore
|| required_tool_missing
|| (case.local_registry && !local_build_present);
if prewarm_manifest.is_some() && !ignored && case.prewarm_bun {
bun_prewarm
if prewarm_manifest.is_some() && !ignored && case.prewarm_package_managers {
archives
.env(case.env.iter().map(|(key, value)| (key.as_str(), value.as_str())));
bun_prewarm.workspace(&fixture_path, &case.cwd).unwrap_or_else(|error| {
archives.workspace(&fixture_path, &case.cwd).unwrap_or_else(|error| {
panic!("failed to read prewarm inputs for {trial_name}: {error}")
});
for step in case.steps.iter().chain(&case.after) {
bun_prewarm
archives
.workspace(&fixture_path, step.cwd.as_deref().unwrap_or(&case.cwd))
.unwrap_or_else(|error| {
panic!("failed to read prewarm inputs for {trial_name}: {error}")
});
bun_prewarm.env(
archives.env(
step.envs.iter().map(|(key, value)| (key.as_str(), value.as_str())),
);
bun_prewarm.step(&step.argv);
archives.step(&step.argv);
}
}
let isolated = case_needs_isolation(&case);
Expand Down Expand Up @@ -2117,7 +2117,7 @@ fn main() {
}

if let Some(path) = prewarm_manifest {
let urls = bun_prewarm.urls(&prewarm::platform_package());
let urls = archives.urls(&prewarm::platform_package());
std::fs::write(&path, serde_json::to_vec_pretty(&urls).unwrap()).unwrap_or_else(|error| {
panic!("failed to write prewarm manifest {}: {error}", Path::new(&path).display())
});
Expand Down
64 changes: 45 additions & 19 deletions crates/vp_cli_snapshots/tests/cli_snapshots/prewarm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,34 @@ static EXACT_VERSION: LazyLock<regex::Regex> = LazyLock::new(|| {
});

#[derive(Default)]
pub struct BunPrewarm {
versions: BTreeSet<String>,
pub struct PackageManagerPrewarm {
versions: BTreeSet<(String, String)>,
packages: BTreeSet<PathBuf>,
}

impl BunPrewarm {
fn version(&mut self, spec: &str) {
if let Some(version) = EXACT_VERSION.captures(spec).and_then(|captures| captures.get(1)) {
self.versions.insert(version.as_str().to_owned());
impl PackageManagerPrewarm {
fn version(&mut self, manager: &str, spec: &str) {
if !matches!(manager, "bun" | "pnpm") {
return;
}
let Some(captures) = EXACT_VERSION.captures(spec) else {
return;
};
let version = &captures[1];
// pnpm 12+ selects a native platform package through registry metadata.
// Its main tarball is not the archive installed by the CLI.
if manager == "pnpm" {
let major = version.split('.').next().unwrap().parse::<u64>().unwrap_or(u64::MAX);
if major >= 12 {
return;
}
}
self.versions.insert((manager.to_owned(), version.to_owned()));
}

fn package_manager(&mut self, value: &Value) {
if let Some(spec) = value.as_str().and_then(|value| value.strip_prefix("bun@")) {
self.version(spec);
fn package_manager(&mut self, spec: &str) {
if let Some((manager, version)) = spec.split_once('@') {
self.version(manager, version);
}
}

Expand All @@ -35,15 +48,17 @@ impl BunPrewarm {
for entry in entries {
self.dev_engine(entry);
}
} else if value["name"] == "bun"
} else if let Some(manager) = value["name"].as_str()
&& let Some(version) = value["version"].as_str()
{
self.version(version);
self.version(manager, version);
}
}

pub fn package_json(&mut self, value: &Value) {
self.package_manager(&value["packageManager"]);
if let Some(spec) = value["packageManager"].as_str() {
self.package_manager(spec);
}
self.dev_engine(&value["devEngines"]["packageManager"]);
}

Expand Down Expand Up @@ -72,8 +87,9 @@ impl BunPrewarm {
pub fn env<'a>(&mut self, entries: impl IntoIterator<Item = (&'a str, &'a str)>) {
for (key, value) in entries {
match key {
"VP_BUN_VERSION" => self.version(value),
"VP_PACKAGE_MANAGER" => self.package_manager(&Value::String(value.to_owned())),
"VP_BUN_VERSION" => self.version("bun", value),
"VP_PNPM_VERSION" => self.version("pnpm", value),
"VP_PACKAGE_MANAGER" => self.package_manager(value),
_ => {}
}
}
Expand All @@ -89,7 +105,11 @@ impl BunPrewarm {
let value =
serde_json::from_str(raw).unwrap_or_else(|_| Value::String((*raw).into()));
match *key {
"packageManager" => self.package_manager(&value),
"packageManager" => {
if let Some(spec) = value.as_str() {
self.package_manager(spec);
}
}
"devEngines" => self.dev_engine(&value["packageManager"]),
"devEngines.packageManager" => self.dev_engine(&value),
_ => {}
Expand All @@ -104,18 +124,24 @@ impl BunPrewarm {
}
["vp", "env", "default" | "use" | "exec", specs @ ..] => {
for spec in specs {
self.package_manager(&Value::String((*spec).into()));
self.package_manager(spec);
}
}
_ => {}
}
}

pub fn urls(&self, package: &str) -> Vec<String> {
pub fn urls(&self, bun_package: &str) -> Vec<String> {
self.versions
.iter()
.map(|version| {
format!("https://registry.npmjs.org/@oven/{package}/-/{package}-{version}.tgz")
.map(|(manager, version)| {
if manager == "bun" {
format!(
"https://registry.npmjs.org/@oven/{bun_package}/-/{bun_package}-{version}.tgz"
)
} else {
format!("https://registry.npmjs.org/pnpm/-/pnpm-{version}.tgz")
}
})
.collect()
}
Expand Down
Loading
Loading