Skip to content

Commit 9adf4fd

Browse files
committed
fix: resolve Vite+ dist-tag versions
1 parent 6e1631f commit 9adf4fd

10 files changed

Lines changed: 216 additions & 59 deletions

File tree

dist/azure/index.mjs

Lines changed: 21 additions & 7 deletions
Large diffs are not rendered by default.

dist/index.mjs

Lines changed: 21 additions & 7 deletions
Large diffs are not rendered by default.

gitlab/bootstrap.sh

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ setup_vp_read_bin_dir() {
6262
' "$1"
6363
}
6464

65+
setup_vp_read_installed_version() {
66+
awk '
67+
$1 == "vp" && $2 ~ /^v?[0-9]/ {
68+
version = $2
69+
sub(/^v/, "", version)
70+
print version
71+
exit
72+
}
73+
{
74+
for (index = 1; index < NF; index++) {
75+
if ($index == "Global:" && $(index + 1) ~ /^v?[0-9]/) {
76+
version = $(index + 1)
77+
sub(/^v/, "", version)
78+
print version
79+
exit
80+
}
81+
}
82+
}
83+
' "$1"
84+
}
85+
6586
setup_vp_install_viteplus_from() {
6687
setup_vp_url="$1"
6788
setup_vp_download "$setup_vp_url" "$setup_vp_install_tmp" || return 1
@@ -79,8 +100,10 @@ setup_vp_install_viteplus_from() {
79100
# Source the official installer so its VpDirs-resolved shim remains
80101
# available long enough to ask the installed payload for its directories.
81102
. "$setup_vp_install_tmp" || return $?
82-
if [ -n "${SHIM_DIR:-}" ] && [ -x "$SHIM_DIR/vp" ]; then
83-
VP_DUMP_DIRS=1 "$SHIM_DIR/vp" > "$setup_vp_dirs_tmp"
103+
setup_vp_shim_dir="${SHIM_DIR:-${INSTALL_DIR:-${VP_HOME:-$HOME/.vite-plus}}/bin}"
104+
if [ -x "$setup_vp_shim_dir/vp" ]; then
105+
"$setup_vp_shim_dir/vp" --version > "$setup_vp_dirs_tmp"
106+
VP_DUMP_DIRS=1 "$setup_vp_shim_dir/vp" >> "$setup_vp_dirs_tmp"
84107
fi
85108
else
86109
bash "$setup_vp_install_tmp"
@@ -157,17 +180,20 @@ if [[ "$SETUP_VP_VERSION" =~ ^0\.0\.0-commit\.([0-9a-fA-F]{40})$ ]]; then
157180
setup_vp_pr_version="${BASH_REMATCH[1]}"
158181
fi
159182

160-
# VpDirs was added in Vite+ 0.3.0. Preview builds also contain it. Dist-tags
161-
# resolve during installation and track current releases, so keep detection on
162-
# when an exact version is not available.
183+
# VpDirs was added in Vite+ 0.3.0. Preview builds also contain it. For a
184+
# dist-tag, probe the installed version before a missing VpDirs result selects
185+
# the legacy layout.
163186
setup_vp_detect_dirs="true"
164-
if [ -z "$setup_vp_pr_version" ] &&
165-
[[ "$SETUP_VP_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]
166-
then
167-
setup_vp_major=$((10#${BASH_REMATCH[1]}))
168-
setup_vp_minor=$((10#${BASH_REMATCH[2]}))
169-
if [ "$setup_vp_major" -eq 0 ] && [ "$setup_vp_minor" -lt 3 ]; then
170-
setup_vp_detect_dirs="false"
187+
setup_vp_check_installed_version="false"
188+
if [ -z "$setup_vp_pr_version" ]; then
189+
if [[ "$SETUP_VP_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then
190+
setup_vp_major=$((10#${BASH_REMATCH[1]}))
191+
setup_vp_minor=$((10#${BASH_REMATCH[2]}))
192+
if [ "$setup_vp_major" -eq 0 ] && [ "$setup_vp_minor" -lt 3 ]; then
193+
setup_vp_detect_dirs="false"
194+
fi
195+
else
196+
setup_vp_check_installed_version="true"
171197
fi
172198
fi
173199

@@ -189,8 +215,21 @@ trap 'rm -f "$setup_vp_install_tmp" "$setup_vp_dirs_tmp" "$setup_vp_runtime_tmp"
189215
setup_vp_install_viteplus
190216
if [ "$setup_vp_detect_dirs" = "true" ]; then
191217
if ! setup_vp_bin_dir="$(setup_vp_read_bin_dir "$setup_vp_dirs_tmp")"; then
192-
echo "setup-vp: Vite+ was installed successfully, but setup-vp could not resolve its VpDirs." >&2
193-
return 1 2>/dev/null || exit 1
218+
setup_vp_bin_dir=""
219+
if [ "$setup_vp_check_installed_version" = "true" ]; then
220+
setup_vp_installed_version="$(setup_vp_read_installed_version "$setup_vp_dirs_tmp")"
221+
if [[ "$setup_vp_installed_version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9A-Za-z.-]+)?$ ]]; then
222+
setup_vp_major=$((10#${BASH_REMATCH[1]}))
223+
setup_vp_minor=$((10#${BASH_REMATCH[2]}))
224+
if [ "$setup_vp_major" -eq 0 ] && [ "$setup_vp_minor" -lt 3 ]; then
225+
setup_vp_bin_dir="$HOME/.vite-plus/bin"
226+
fi
227+
fi
228+
fi
229+
if [ -z "$setup_vp_bin_dir" ]; then
230+
echo "setup-vp: Vite+ was installed successfully, but setup-vp could not resolve its VpDirs." >&2
231+
return 1 2>/dev/null || exit 1
232+
fi
194233
fi
195234
else
196235
# Vite+ releases before VpDirs use the monolithic layout.

src/azure/install-viteplus.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,31 @@ describe("installVitePlus", () => {
102102
expect(installCalls[0]?.[1]?.SETUP_VP_DIRS_FILE).toMatch(/setup-vp-dirs-.*\.txt$/);
103103
});
104104

105+
it("uses the installed version to resolve the latest dist-tag", async () => {
106+
const prependPath = vi.fn();
107+
const env = { HOME: "/home/runner", PATH: "/usr/bin" };
108+
const runInstall = vi.fn((_url: string, installEnv: Record<string, string>) => {
109+
writeFileSync(installEnv.SETUP_VP_DIRS_FILE, "vp v0.2.9\n");
110+
return 0;
111+
});
112+
113+
await installVitePlus("latest", {
114+
platform: "linux",
115+
env,
116+
prependPath,
117+
sleep: async () => undefined,
118+
runInstall,
119+
logWarningFn: () => undefined,
120+
});
121+
122+
expect(prependPath).toHaveBeenCalledWith("/home/runner/.vite-plus/bin");
123+
expect(env.PATH).toBe("/home/runner/.vite-plus/bin:/usr/bin");
124+
});
125+
105126
it.each([
106-
{ version: "0.3.0", output: undefined },
127+
{ version: "0.3.0", output: "vp v0.2.9\n" },
107128
{ version: `0.0.0-commit.${"a".repeat(40)}`, output: "bin\t/test/data/bin\n" },
108-
{ version: "latest", output: "" },
129+
{ version: "latest", output: "vp v0.3.0\n" },
109130
])("fails when $version does not report valid VpDirs", async ({ version, output }) => {
110131
const prependPath = vi.fn();
111132
const runInstall = vi.fn((_url: string, env: Record<string, string>) => {

src/azure/install-viteplus.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import { logWarning } from "./commands.js";
1616
const INSTALL_MAX_ROUNDS = 2;
1717
const INSTALL_RETRY_DELAY_MS = 2000;
1818

19-
export function getVitePlusHome(platform: NodeJS.Platform = process.platform): string {
20-
const home =
21-
platform === "win32" ? process.env.USERPROFILE || homedir() : process.env.HOME || homedir();
19+
export function getVitePlusHome(
20+
platform: NodeJS.Platform = process.platform,
21+
env: NodeJS.ProcessEnv = process.env,
22+
): string {
23+
const home = platform === "win32" ? env.USERPROFILE || homedir() : env.HOME || homedir();
2224
return join(home, ".vite-plus");
2325
}
2426

@@ -120,7 +122,11 @@ export async function installVitePlus(
120122
};
121123

122124
const ensureBinInPath = (): void => {
123-
const binDir = resolveVitePlusBinDir(dirsFile, join(getVitePlusHome(platform), "bin"));
125+
const binDir = resolveVitePlusBinDir(
126+
version,
127+
dirsFile,
128+
join(getVitePlusHome(platform, targetEnv), "bin"),
129+
);
124130
const separator = platform === "win32" ? ";" : ":";
125131
if (!targetEnv.PATH?.split(separator).includes(binDir)) {
126132
targetEnv.PATH = `${binDir}${separator}${targetEnv.PATH || ""}`;

src/ci/vp-dirs.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ describe("Vite+ directory resolution", () => {
66
expect(
77
parseVitePlusDirs(
88
[
9+
"vp v0.3.0",
910
"data\t/home/runner/.local/share/vite-plus",
1011
"bin\t/home/runner/.local/share/vite-plus/bin",
1112
"cache\t/home/runner/.cache/vite-plus",
@@ -47,16 +48,18 @@ describe("Vite+ directory resolution", () => {
4748
expect(command.args[1]).toContain('-o "$installer_file"');
4849
expect(command.args[1]).toContain('source "$installer_file"');
4950
expect(command.args[1]).not.toContain("source /dev/stdin");
50-
expect(command.args[1]).toContain('VP_DUMP_DIRS=1 "$SHIM_DIR/vp"');
51-
expect(command.args[1]).toContain('> "$SETUP_VP_DIRS_FILE"');
51+
expect(command.args[1]).toContain('"$vp_dir/vp" --version');
52+
expect(command.args[1]).toContain('VP_DUMP_DIRS=1 "$vp_dir/vp"');
53+
expect(command.args[1]).toContain('>> "$SETUP_VP_DIRS_FILE"');
5254
});
5355

5456
it("dumps directories from the installer-resolved Windows shim", () => {
5557
const command = getInstallScriptCommand("https://example.com/install.ps1", "win32");
5658

5759
expect(command.command).toBe("pwsh");
5860
expect(command.args[1]).toContain(". ([scriptblock]::Create");
59-
expect(command.args[1]).toContain("Join-Path $script:ShimDir 'vp.exe'");
61+
expect(command.args[1]).toContain("Join-Path $vpDir 'vp.exe'");
62+
expect(command.args[1]).toContain("& $vpPath --version");
6063
expect(command.args[1]).toContain("$env:VP_DUMP_DIRS = '1'");
6164
});
6265

src/ci/vp-dirs.ts

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { readFileSync, rmSync } from "node:fs";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55
import { pkgPrNewCommitSha } from "./install-script-urls.js";
6+
import { parseInstalledVpVersion } from "./version.js";
67

78
// Keep installer network calls bounded so a hung source fails over quickly.
89
const CURL_TIMEOUT_FLAGS = "--connect-timeout 5 --max-time 15";
@@ -24,8 +25,8 @@ export function supportsVitePlusDirs(version: string): boolean {
2425
if (pkgPrNewCommitSha(version)) return true;
2526

2627
const match = version.match(EXACT_VERSION_RE);
27-
// Dist-tags resolve during installation. They track current releases, so
28-
// keep VpDirs detection enabled when an exact version is not available.
28+
// Dist-tags resolve during installation. Keep the probe enabled so the
29+
// installed version can decide whether missing VpDirs output is valid.
2930
if (!match) return true;
3031

3132
const major = Number(match[1]);
@@ -42,22 +43,43 @@ export function removeVitePlusDirsFile(filePath: string): void {
4243
}
4344

4445
export function readVitePlusDirs(filePath: string): VitePlusDirs | undefined {
46+
const output = readVitePlusProbe(filePath);
47+
return output === undefined ? undefined : parseVitePlusDirs(output);
48+
}
49+
50+
function readVitePlusProbe(filePath: string): string | undefined {
4551
try {
46-
return parseVitePlusDirs(readFileSync(filePath, "utf8"));
52+
return readFileSync(filePath, "utf8");
4753
} catch (error) {
4854
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
4955
throw error;
5056
}
5157
}
5258

53-
export function resolveVitePlusBinDir(dirsFile: string | undefined, legacyBinDir: string): string {
54-
if (!dirsFile) return legacyBinDir;
55-
56-
const dirs = readVitePlusDirs(dirsFile);
57-
if (!dirs) {
59+
export function resolveVitePlusBinDir(
60+
requestedVersion: string,
61+
dirsFile: string | undefined,
62+
legacyBinDir: string,
63+
): string {
64+
if (!dirsFile) {
65+
if (!supportsVitePlusDirs(requestedVersion)) return legacyBinDir;
5866
throw new Error("Vite+ was installed successfully, but setup-vp could not resolve its VpDirs.");
5967
}
60-
return dirs.bin;
68+
69+
const output = readVitePlusProbe(dirsFile);
70+
const dirs = output === undefined ? undefined : parseVitePlusDirs(output);
71+
if (dirs) return dirs.bin;
72+
73+
const hasKnownRequestedVersion =
74+
pkgPrNewCommitSha(requestedVersion) !== undefined || EXACT_VERSION_RE.test(requestedVersion);
75+
if (!hasKnownRequestedVersion && output !== undefined) {
76+
const installedVersion = parseInstalledVpVersion(output);
77+
if (installedVersion !== "unknown" && !supportsVitePlusDirs(installedVersion)) {
78+
return legacyBinDir;
79+
}
80+
}
81+
82+
throw new Error("Vite+ was installed successfully, but setup-vp could not resolve its VpDirs.");
6183
}
6284

6385
export function parseVitePlusDirs(output: string): VitePlusDirs | undefined {
@@ -101,10 +123,22 @@ export function getInstallScriptCommand(
101123
$dirsFile = $env:${VP_DIRS_FILE_ENV}
102124
Set-Content -LiteralPath $dirsFile -Value '' -NoNewline
103125
. ([scriptblock]::Create((irm -TimeoutSec ${PWSH_TIMEOUT_SEC} ${url})))
104-
$vpPath = if ($script:ShimDir) { Join-Path $script:ShimDir 'vp.exe' } else { $null }
105-
if ($vpPath -and (Test-Path -LiteralPath $vpPath)) {
126+
$vpDir = if ($script:ShimDir) {
127+
$script:ShimDir
128+
} elseif ($InstallDir) {
129+
Join-Path $InstallDir 'bin'
130+
} else {
131+
Join-Path $env:USERPROFILE '.vite-plus\\bin'
132+
}
133+
$vpPath = Join-Path $vpDir 'vp.exe'
134+
if (-not (Test-Path -LiteralPath $vpPath)) {
135+
$vpPath = Join-Path $vpDir 'vp.cmd'
136+
}
137+
if (Test-Path -LiteralPath $vpPath) {
138+
& $vpPath --version | Set-Content -LiteralPath $dirsFile
139+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
106140
$env:VP_DUMP_DIRS = '1'
107-
& $vpPath | Set-Content -LiteralPath $dirsFile
141+
& $vpPath | Add-Content -LiteralPath $dirsFile
108142
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
109143
}
110144
`.trim();
@@ -126,8 +160,10 @@ trap 'rm -f "$installer_file"' EXIT
126160
: > "$${VP_DIRS_FILE_ENV}"
127161
curl -fsSL ${CURL_TIMEOUT_FLAGS} ${url} -o "$installer_file"
128162
source "$installer_file"
129-
if [ -n "\${SHIM_DIR:-}" ] && [ -x "$SHIM_DIR/vp" ]; then
130-
VP_DUMP_DIRS=1 "$SHIM_DIR/vp" > "$${VP_DIRS_FILE_ENV}"
163+
vp_dir="\${SHIM_DIR:-\${INSTALL_DIR:-\${VP_HOME:-$HOME/.vite-plus}}/bin}"
164+
if [ -x "$vp_dir/vp" ]; then
165+
"$vp_dir/vp" --version > "$${VP_DIRS_FILE_ENV}"
166+
VP_DUMP_DIRS=1 "$vp_dir/vp" >> "$${VP_DIRS_FILE_ENV}"
131167
fi
132168
`.trim();
133169
return { command: "bash", args: ["-c", script] };

src/gitlab/bootstrap.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ describe("GitLab bootstrap", () => {
1616

1717
it("uses the bin directory reported by the installed payload", () => {
1818
expect(bootstrap).toContain('export VP_VPDIRS_AWARE="1"');
19-
expect(bootstrap).toContain('VP_DUMP_DIRS=1 "$SHIM_DIR/vp"');
19+
expect(bootstrap).toContain('"$setup_vp_shim_dir/vp" --version');
20+
expect(bootstrap).toContain('VP_DUMP_DIRS=1 "$setup_vp_shim_dir/vp"');
2021
expect(bootstrap).toContain('setup_vp_bin_dir="$(setup_vp_read_bin_dir "$setup_vp_dirs_tmp")"');
2122
expect(bootstrap).toContain('export PATH="$setup_vp_bin_dir:$PATH"');
2223
});
@@ -38,6 +39,14 @@ describe("GitLab bootstrap", () => {
3839
expect(bootstrap).toContain('bash "$setup_vp_install_tmp"');
3940
});
4041

42+
it("checks the installed version for dist-tags", () => {
43+
expect(bootstrap).toContain('setup_vp_check_installed_version="true"');
44+
expect(bootstrap).toContain(
45+
'setup_vp_installed_version="$(setup_vp_read_installed_version "$setup_vp_dirs_tmp")"',
46+
);
47+
expect(bootstrap).toContain('[ "$setup_vp_minor" -lt 3 ]');
48+
});
49+
4150
it("preserves a sourced installer's failure status", () => {
4251
expect(bootstrap).toContain('. "$setup_vp_install_tmp" || return $?');
4352
});

src/install-viteplus.test.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,24 @@ describe("installVitePlus", () => {
102102
expect((options as { env: Record<string, string> }).env.SETUP_VP_DIRS_FILE).toBeUndefined();
103103
});
104104

105+
it("should use the installed version to resolve the latest dist-tag", async () => {
106+
vi.stubEnv("HOME", "/home/runner");
107+
vi.stubEnv("PATH", "/usr/bin");
108+
vi.mocked(exec).mockImplementationOnce(async (_command, _args, options) => {
109+
const env = (options as { env: Record<string, string> }).env;
110+
writeFileSync(env.SETUP_VP_DIRS_FILE, "vp v0.2.9\n");
111+
return 0;
112+
});
113+
114+
await installVitePlus(baseInputs);
115+
116+
expect(addPath).toHaveBeenCalledWith("/home/runner/.vite-plus/bin");
117+
});
118+
105119
it.each([
106-
{ version: "0.3.0", output: undefined },
120+
{ version: "0.3.0", output: "vp v0.2.9\n" },
107121
{ version: `0.0.0-commit.${commitSha}`, output: "bin\t/test/data/bin\n" },
108-
{ version: "latest", output: "" },
122+
{ version: "latest", output: "vp v0.3.0\n" },
109123
])("should fail when $version does not report valid VpDirs", async ({ version, output }) => {
110124
vi.mocked(exec).mockImplementationOnce(async (_command, _args, options) => {
111125
if (output !== undefined) {
@@ -261,7 +275,8 @@ describe("installVitePlus", () => {
261275
expect(script).toContain('-o "$installer_file"');
262276
expect(script).toContain('source "$installer_file"');
263277
expect(script).not.toContain("source /dev/stdin");
264-
expect(script).toContain('VP_DUMP_DIRS=1 "$SHIM_DIR/vp"');
278+
expect(script).toContain('"$vp_dir/vp" --version');
279+
expect(script).toContain('VP_DUMP_DIRS=1 "$vp_dir/vp"');
265280
});
266281

267282
it("should declare VpDirs support to the installer", async () => {

src/install-viteplus.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export async function installVitePlus(inputs: Inputs): Promise<void> {
9393
try {
9494
if (pinned.length > 0) {
9595
if (await tryUrls(pinned)) {
96-
ensureVitePlusBinInPath(dirsFile);
96+
ensureVitePlusBinInPath(version, dirsFile);
9797
return;
9898
}
9999
warning(
@@ -102,7 +102,7 @@ export async function installVitePlus(inputs: Inputs): Promise<void> {
102102
}
103103

104104
if (await tryUrls(latest)) {
105-
ensureVitePlusBinInPath(dirsFile);
105+
ensureVitePlusBinInPath(version, dirsFile);
106106
return;
107107
}
108108

@@ -124,8 +124,8 @@ async function runInstallCommand(url: string, env: { [key: string]: string }): P
124124
return exec(command, args, options);
125125
}
126126

127-
function ensureVitePlusBinInPath(dirsFile: string | undefined): void {
128-
const binDir = resolveVitePlusBinDir(dirsFile, join(getVitePlusHome(), "bin"));
127+
function ensureVitePlusBinInPath(version: string, dirsFile: string | undefined): void {
128+
const binDir = resolveVitePlusBinDir(version, dirsFile, join(getVitePlusHome(), "bin"));
129129
if (!process.env.PATH?.split(delimiter).includes(binDir)) {
130130
addPath(binDir);
131131
}

0 commit comments

Comments
 (0)