|
5 | 5 |
|
6 | 6 | use std::{ |
7 | 7 | env, |
8 | | - io::{Cursor, Read as _, Write as _}, |
| 8 | + io::{Cursor, Read as _}, |
9 | 9 | path::Path, |
10 | 10 | process::{self, Output}, |
11 | 11 | time::{SystemTime, UNIX_EPOCH}, |
@@ -136,92 +136,11 @@ pub async fn write_release_age_overrides(version_dir: &AbsolutePath) -> Result<( |
136 | 136 | Ok(()) |
137 | 137 | } |
138 | 138 |
|
139 | | -fn is_affirmative_response(input: &str) -> bool { |
140 | | - matches!(input.trim().to_ascii_lowercase().as_str(), "y" | "yes") |
141 | | -} |
142 | | - |
143 | | -fn should_prompt_release_age_override(silent: bool) -> bool { |
144 | | - !silent && vp_shared::is_stdin_terminal() && vp_shared::is_stderr_terminal() |
145 | | -} |
146 | | - |
147 | | -fn prompt_release_age_override(version: &str) -> bool { |
148 | | - eprintln!(); |
149 | | - eprintln!("warn: Your minimumReleaseAge setting prevented installing vite-plus@{version}."); |
150 | | - eprintln!("This setting helps protect against newly published compromised packages."); |
151 | | - eprintln!("Proceeding will disable this protection for this Vite+ install only."); |
152 | | - eprint!("Do you want to proceed? (y/N): "); |
153 | | - if std::io::stderr().flush().is_err() { |
154 | | - return false; |
155 | | - } |
156 | | - |
157 | | - let mut input = String::new(); |
158 | | - if std::io::stdin().read_line(&mut input).is_err() { |
159 | | - return false; |
160 | | - } |
161 | | - |
162 | | - is_affirmative_response(&input) |
163 | | -} |
164 | | - |
165 | | -fn is_release_age_error(stdout: &[u8], stderr: &[u8]) -> bool { |
166 | | - let output = |
167 | | - format!("{}\n{}", String::from_utf8_lossy(stdout), String::from_utf8_lossy(stderr)); |
168 | | - let lower = output.to_ascii_lowercase(); |
169 | | - |
170 | | - // This wrapper install path is pinned to pnpm via packageManager, so this |
171 | | - // detection follows pnpm's resolver/reporter output rather than npm/yarn. |
172 | | - // |
173 | | - // pnpm's PnpmError prefixes internal codes with ERR_PNPM_, so |
174 | | - // `NO_MATURE_MATCHING_VERSION` becomes `ERR_PNPM_NO_MATURE_MATCHING_VERSION` |
175 | | - // in CLI output. We still match the unprefixed code as a fallback in case |
176 | | - // future reporter/log output includes the raw internal code. |
177 | | - // https://github.com/pnpm/pnpm/blob/16cfde66ec71125d692ea828eba2a5f9b3cc54fc/core/error/src/index.ts#L18-L20 |
178 | | - // |
179 | | - // npm-resolver chooses NO_MATURE_MATCHING_VERSION when |
180 | | - // publishedBy/minimumReleaseAge rejects a matching version, and uses the |
181 | | - // "does not meet the minimumReleaseAge constraint" message. |
182 | | - // https://github.com/pnpm/pnpm/blob/16cfde66ec71125d692ea828eba2a5f9b3cc54fc/resolving/npm-resolver/src/index.ts#L76-L84 |
183 | | - // |
184 | | - // default-reporter handles both ERR_PNPM_NO_MATURE_MATCHING_VERSION and |
185 | | - // ERR_PNPM_NO_MATCHING_VERSION, and may append guidance mentioning |
186 | | - // minimumReleaseAgeExclude when the error has an immatureVersion. |
187 | | - // https://github.com/pnpm/pnpm/blob/16cfde66ec71125d692ea828eba2a5f9b3cc54fc/cli/default-reporter/src/reportError.ts#L163-L164 |
188 | | - // |
189 | | - // pnpm itself notes that NO_MATCHING_VERSION can also happen under |
190 | | - // minimumReleaseAge when all candidate versions are newer than the threshold. |
191 | | - // Because it is also used for real missing versions, we only treat it as |
192 | | - // release-age related when accompanied by the age-gate text below. |
193 | | - // https://github.com/pnpm/pnpm/blob/16cfde66ec71125d692ea828eba2a5f9b3cc54fc/deps/inspection/outdated/src/createManifestGetter.ts#L66-L76 |
194 | | - // |
195 | | - // minimum-release-age is the pnpm .npmrc key; npm's min-release-age is |
196 | | - // intentionally not treated as a pnpm signal here. |
197 | | - // https://github.com/pnpm/pnpm/blob/16cfde66ec71125d692ea828eba2a5f9b3cc54fc/config/reader/src/types.ts#L73-L74 |
198 | | - let has_release_age_text = output.contains("does not meet the minimumReleaseAge constraint") |
199 | | - || output.contains("minimumReleaseAge") |
200 | | - || output.contains("minimumReleaseAgeExclude") |
201 | | - || lower.contains("minimum release age") |
202 | | - || lower.contains("minimum-release-age"); |
203 | | - |
204 | | - output.contains("ERR_PNPM_NO_MATURE_MATCHING_VERSION") |
205 | | - || output.contains("NO_MATURE_MATCHING_VERSION") |
206 | | - || (output.contains("ERR_PNPM_NO_MATCHING_VERSION") && has_release_age_text) |
207 | | - || has_release_age_text |
208 | | -} |
209 | | - |
210 | | -fn format_install_failure_message( |
211 | | - exit_code: i32, |
212 | | - log_path: Option<&AbsolutePathBuf>, |
213 | | - release_age_blocked: bool, |
214 | | -) -> String { |
| 139 | +fn format_install_failure_message(exit_code: i32, log_path: Option<&AbsolutePathBuf>) -> String { |
215 | 140 | let log_msg = log_path |
216 | 141 | .map_or_else(String::new, |p| format!(". See log for details: {}", p.as_path().display())); |
217 | 142 |
|
218 | | - if release_age_blocked { |
219 | | - format!( |
220 | | - "Upgrade blocked by your minimumReleaseAge setting. Wait until the package is old enough or adjust your package manager configuration explicitly{log_msg}" |
221 | | - ) |
222 | | - } else { |
223 | | - format!("Failed to install production dependencies (exit code: {exit_code}){log_msg}") |
224 | | - } |
| 143 | + format!("Failed to install production dependencies (exit code: {exit_code}){log_msg}") |
225 | 144 | } |
226 | 145 |
|
227 | 146 | /// Write stdout and stderr from a failed install to `upgrade.log`. |
@@ -257,15 +176,12 @@ pub async fn write_upgrade_log( |
257 | 176 | pub async fn install_production_deps( |
258 | 177 | version_dir: &AbsolutePath, |
259 | 178 | registry: Option<&str>, |
260 | | - silent: bool, |
261 | | - new_version: &str, |
262 | 179 | ) -> Result<(), Error> { |
263 | 180 | tracing::debug!("Running pnpm install in {}", version_dir.as_path().display()); |
264 | 181 |
|
265 | | - // Do not pass `--silent` to the inner install: pnpm suppresses the |
266 | | - // release-age error body in silent mode, which would leave upgrade.log |
267 | | - // empty and make the release-age gate impossible to detect. This outer |
268 | | - // process captures the output and only surfaces it through the log. |
| 182 | + // Keep the bypass local to this Vite+ installation. |
| 183 | + write_release_age_overrides(version_dir).await?; |
| 184 | + |
269 | 185 | let mut args = vec!["install"]; |
270 | 186 | if let Some(registry_url) = registry { |
271 | 187 | args.push("--registry"); |
@@ -297,48 +213,13 @@ pub async fn install_production_deps( |
297 | 213 |
|
298 | 214 | if !output.status.success() { |
299 | 215 | let log_path = write_upgrade_log(version_dir, &output.stdout, &output.stderr).await; |
300 | | - let release_age_blocked = is_release_age_error(&output.stdout, &output.stderr); |
301 | | - |
302 | | - if !release_age_blocked { |
303 | | - return Err(Error::Setup( |
304 | | - format_install_failure_message( |
305 | | - vp_shared::exit_code_from_status(output.status), |
306 | | - log_path.as_ref(), |
307 | | - false, |
308 | | - ) |
309 | | - .into(), |
310 | | - )); |
311 | | - } |
312 | | - |
313 | | - if !should_prompt_release_age_override(silent) || !prompt_release_age_override(new_version) |
314 | | - { |
315 | | - return Err(Error::Setup( |
316 | | - format_install_failure_message( |
317 | | - vp_shared::exit_code_from_status(output.status), |
318 | | - log_path.as_ref(), |
319 | | - true, |
320 | | - ) |
321 | | - .into(), |
322 | | - )); |
323 | | - } |
324 | | - |
325 | | - // Only create the local override after explicit consent. This preserves |
326 | | - // minimumReleaseAge protection for the default and non-interactive paths. |
327 | | - write_release_age_overrides(version_dir).await?; |
328 | | - let retry_output = |
329 | | - run_pnpm_install(version_dir, &node_runtime, &pnpm_entry, &args, registry).await?; |
330 | | - if !retry_output.status.success() { |
331 | | - let retry_log_path = |
332 | | - write_upgrade_log(version_dir, &retry_output.stdout, &retry_output.stderr).await; |
333 | | - return Err(Error::Setup( |
334 | | - format_install_failure_message( |
335 | | - vp_shared::exit_code_from_status(retry_output.status), |
336 | | - retry_log_path.as_ref(), |
337 | | - false, |
338 | | - ) |
339 | | - .into(), |
340 | | - )); |
341 | | - } |
| 216 | + return Err(Error::Setup( |
| 217 | + format_install_failure_message( |
| 218 | + vp_shared::exit_code_from_status(output.status), |
| 219 | + log_path.as_ref(), |
| 220 | + ) |
| 221 | + .into(), |
| 222 | + )); |
342 | 223 | } |
343 | 224 |
|
344 | 225 | Ok(()) |
@@ -1021,41 +902,4 @@ mod tests { |
1021 | 902 | let registry = tokio::fs::read_to_string(version_dir.join("registry.txt")).await.unwrap(); |
1022 | 903 | assert_eq!(registry, registry_url); |
1023 | 904 | } |
1024 | | - |
1025 | | - #[test] |
1026 | | - fn test_is_release_age_error_detects_pnpm_no_mature_code() { |
1027 | | - assert!(is_release_age_error( |
1028 | | - b"", |
1029 | | - b"ERR_PNPM_NO_MATURE_MATCHING_VERSION Version 0.1.16 of vite-plus does not meet the minimumReleaseAge constraint", |
1030 | | - )); |
1031 | | - } |
1032 | | - |
1033 | | - #[test] |
1034 | | - fn test_is_release_age_error_detects_minimum_release_age_message() { |
1035 | | - assert!(is_release_age_error( |
1036 | | - b"", |
1037 | | - b"Version 0.1.16 (released just now) of vite-plus does not meet the minimumReleaseAge constraint", |
1038 | | - )); |
1039 | | - } |
1040 | | - |
1041 | | - #[test] |
1042 | | - fn test_is_release_age_error_detects_no_matching_with_release_age_context() { |
1043 | | - assert!(is_release_age_error( |
1044 | | - b"", |
1045 | | - b"ERR_PNPM_NO_MATCHING_VERSION No matching version found. Add the package name to minimumReleaseAgeExclude if you want to ignore the time it was published.", |
1046 | | - )); |
1047 | | - } |
1048 | | - |
1049 | | - #[test] |
1050 | | - fn test_is_release_age_error_ignores_plain_no_matching_version() { |
1051 | | - assert!(!is_release_age_error( |
1052 | | - b"", |
1053 | | - b"ERR_PNPM_NO_MATCHING_VERSION No matching version found for vite-plus@999.999.999", |
1054 | | - )); |
1055 | | - } |
1056 | | - |
1057 | | - #[test] |
1058 | | - fn test_is_release_age_error_ignores_npm_min_release_age() { |
1059 | | - assert!(!is_release_age_error(b"", b"min-release-age prevented installing vite-plus",)); |
1060 | | - } |
1061 | 905 | } |
0 commit comments