Skip to content
Open
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
9 changes: 4 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1503,11 +1503,10 @@ jobs:
if: matrix.pipenv != '' || matrix.pip != ''
uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0
with:
# Pin uv: it is the PEP 751 oracle the pylock tests install with,
# and uv 0.13.2 started refusing Pipenv's `environments = []`
# pylock ("target environment is not supported by the lockfile").
# Bump deliberately, after checking the Pipenv pylock legs pass.
version: "0.13.1"
# Exact pin (scripts/check-tool-pins.py). uv 0.13.2+ refuses the
# `environments = []` pylock Pipenv writes; the pylock test checks
# that refusal matches Pipenv's own pylock, so keep this current.
version: "0.13.5"

- name: Install the Pythons the Pipenv / pip legs run on
if: matrix.pipenv != '' || matrix.pip != ''
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/pipenv-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,10 @@ jobs:
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
with:
# Pin uv: it is the PEP 751 oracle the pylock tests install with,
# and uv 0.13.2 started refusing Pipenv's `environments = []`
# pylock ("target environment is not supported by the lockfile").
# Bump deliberately, after checking the Pipenv pylock legs pass.
version: "0.13.1"
# Exact pin (scripts/check-tool-pins.py). uv 0.13.2+ refuses the
# `environments = []` pylock Pipenv writes; the pylock test checks
# that refusal matches Pipenv's own pylock, so keep this current.
version: "0.13.5"
- name: Install the Pythons the Pipenv releases run on
run: uv python install 3.8 3.12
- name: Build the CLI
Expand Down
120 changes: 95 additions & 25 deletions crates/socket-patch-cli/tests/e2e_vex_build/pipenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::vex_e2e_common;
use crate::vex_pipenv_pip_real;

use std::path::{Path, PathBuf};
use std::process::Output;

use serde_json::Value;
use vex_pipenv_pip_real::*;
Expand Down Expand Up @@ -450,32 +451,15 @@ fn pylock_flow(pipenv: &Pipenv, root: &Path) {
assert!(ok, "{what}: the fresh install must carry the PATCHED six");

// #1368: a PEP 751 installer reading the wired pylock gets the PATCHED
// six too, from the committed wheel.
// six too, from the committed wheel. pip must install it. uv must too,
// unless it refuses Pipenv's own untouched pylock the same way: uv
// 0.13.2+ reads the `environments = []` Pipenv writes as "no supported
// environment", which is upstream's call, not the wiring's.
let pep751 = root.join("pylock").join("pep751");
copy_tree(&proj, &pep751, &[".venv"]);
make_venv(
&pipenv.uv,
python.to_str().unwrap(),
&pep751.join(".venv"),
&[],
)
.unwrap_or_else(|e| panic!("{what}: pylock venv: {e}"));
let py = venv_bin(&pep751.join(".venv"), "python");
let out = tool(
&pipenv.uv,
&pep751,
&[
"pip",
"install",
"--python",
py.to_str().unwrap(),
"-r",
"pylock.toml",
],
&[],
);
assert_ok(&out, &format!("{what}: `uv pip install -r pylock.toml`"));
let (_, bytes, is_patched) = six_oracle(&py, &pep751)
let python = python.to_str().unwrap();
let (out, py) = pylock_install(&pipenv.uv, python, &proj, &pep751.join("pip"), true);
assert_ok(&out, &format!("{what}: `pip install -r pylock.toml`"));
let (_, bytes, is_patched) = six_oracle(&py, &pep751.join("pip"))
.unwrap_or_else(|| panic!("{what}: six not importable from the pylock install"));
let ok = is_patched && bytes == patched;
record(
Expand All @@ -486,6 +470,92 @@ fn pylock_flow(pipenv: &Pipenv, root: &Path) {
if ok { "pass" } else { "FAIL" },
);
assert!(ok, "{what}: the pylock install must carry the PATCHED six");

let (out, py) = pylock_install(&pipenv.uv, python, &proj, &pep751.join("uv"), false);
let verdict = if out.status.success() {
let (_, bytes, is_patched) = six_oracle(&py, &pep751.join("uv"))
.unwrap_or_else(|| panic!("{what}: six not importable from the uv pylock install"));
assert!(
is_patched && bytes == patched,
"{what}: the uv pylock install must carry the PATCHED six"
);
"pass"
} else {
let pristine_dir = pep751.join("uv-pristine");
copy_tree(&proj, &pristine_dir, &[".venv"]);
std::fs::write(pristine_dir.join("pylock.toml"), &pristine_pylock).unwrap();
let (pristine_out, _) = pylock_install(
&pipenv.uv,
python,
&pristine_dir,
&pep751.join("uv-p"),
false,
);
let wired_err = error_lines(&out);
assert!(
!pristine_out.status.success()
&& !wired_err.is_empty()
&& wired_err == error_lines(&pristine_out),
"{what}: uv refuses the wired pylock but not Pipenv's own: {}\n--- pristine\n{}",
out_text(&out),
out_text(&pristine_out)
);
"upstream-refused"
};
record(
"pipenv",
v,
"use_pylock/vendored",
"pylock-uv-install",
verdict,
);
}

/// Installs `src`'s `pylock.toml` into a fresh venv in a copy of `src` at
/// `dir`, with pip (`-r pylock.toml` needs pip 26.1+; pinned exactly like
/// every other oracle here) or uv. Returns the installer's output and the
/// venv's python.
fn pylock_install(
uv: &Path,
python: &str,
src: &Path,
dir: &Path,
with_pip: bool,
) -> (Output, PathBuf) {
copy_tree(src, dir, &[".venv"]);
let venv = dir.join(".venv");
let pins: &[&str] = if with_pip { &["pip==26.2.2"] } else { &[] };
make_venv(uv, python, &venv, pins).unwrap_or_else(|e| panic!("pylock venv: {e}"));
let py = venv_bin(&venv, "python");
let out = if with_pip {
tool(
&py,
dir,
&["-m", "pip", "install", "-r", "pylock.toml"],
&[],
)
} else {
let args = [
"pip",
"install",
"--python",
py.to_str().unwrap(),
"-r",
"pylock.toml",
];
tool(uv, dir, &args, &[])
};
(out, py)
}

/// The `error:` lines of an installer's stderr: what it refused, without
/// the paths it printed around it.
fn error_lines(out: &Output) -> Vec<String> {
String::from_utf8_lossy(&out.stderr)
.lines()
.filter(|l| l.starts_with("error:"))
.map(str::to_owned)
.collect()
}

#[test]
Expand Down
15 changes: 9 additions & 6 deletions crates/socket-patch-core/src/patch/redirect/yarnrc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,16 +586,19 @@ fn global_folder_values_read_like_yarn() {
Vec::<PathBuf>::new()
);
// An escaped expression stays verbatim (`ini` reads `\\` as `\`
// first).
// first). On Windows the leading `\` makes it root-relative: it keeps
// the cwd's drive and replaces the rest, as `path.resolve` does
// (`/w` has no drive, so nothing of it is kept).
let escaped = if cfg!(windows) {
"\\${HOME}/x"
} else {
"/w/\\${HOME}/x"
};
for text in [
"global-folder=\\${HOME}/x\n",
"global-folder=\\\\${HOME}/x\n",
] {
assert_eq!(
npmrc(Host::new(), text),
paths(&["/w/\\${HOME}/x"]),
"{text:?}"
);
assert_eq!(npmrc(Host::new(), text), paths(&[escaped]), "{text:?}");
}
// `ini` ends an unquoted value at a comment and unquotes a quoted one.
assert_eq!(
Expand Down
Loading