GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix gem restore of Bundler 4.1 override targets (#1989) (#1991) · SocketDev/socket-patch@318193c

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • crates/socket-patch-core/src/patch/redirect/upstream
    • gem.rs
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎crates/socket-patch-core/src/patch/redirect/upstream/gem.rs‎

Copy file name to clipboardExpand all lines: crates/socket-patch-core/src/patch/redirect/upstream/gem.rs
+131-2Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
//! rewriter could not have written in place (a blank line before it —
4848
//! the in-place match swallows every blank line before the declaration,
4949
//! and the append always writes one; it goes with the block) that
50-
//! another locked spec depends on. Otherwise the gem is kept as a
50+
//! another locked spec depends on (a spec sub-line, or a Bundler 4.1
51+
//! `OVERRIDES` `with:` target, #1989). Otherwise the gem is kept as a
5152
//! direct pin: a stray exact pin installs the same bytes, while dropping
5253
//! a real declaration would stop `Bundler.require` loading the gem.
5354
//! * a `CHECKSUMS` entry the rewriter ADDED is only recognizable next to
@@ -173,11 +174,34 @@ fn dependency_line<'l>(lines: &[&'l str], name: &str) -> Option<(usize, &'l str)
173174
})
174175
}
175176

176-
/// Whether another locked spec depends on `name` (a 6-space sub-line).
177+
/// Whether another locked spec depends on `name`: a 6-space sub-line, or
178+
/// the `with:` target of a Bundler 4.1 `OVERRIDES` entry (#1989). An
179+
/// `override "other", with: "name", in: "parent"` keeps `parent`'s
180+
/// original edge to `other` in the specs, so no sub-line names `name`.
177181
fn is_subdependency(lines: &[&str], name: &str) -> bool {
178182
lines
179183
.iter()
180184
.any(|l| indented(l, 6).is_some_and(|d| d.split([' ', '(']).next() == Some(name)))
185+
|| override_targets(lines).any(|t| t == name)
186+
}
187+
188+
/// The `with:` values of the lock's `OVERRIDES` entries
189+
/// (` "other", with: "name", in: "parent"`).
190+
fn override_targets<'l>(lines: &'l [&'l str]) -> impl Iterator<Item = &'l str> + 'l {
191+
let range = named_section(lines, "OVERRIDES").unwrap_or((0, 0));
192+
lines[range.0..range.1].iter().filter_map(|l| {
193+
let entry = indented(l, 2)?.trim_end();
194+
// Bundler writes `"<target>", with: "<gem>", in: "<parent>"`
195+
// (`Override#to_lock`: `inspect`ed values joined by `, `). A
196+
// `version:` value may itself hold `, `, so splitting on it can
197+
// break that value apart, but never the `with:` one: a gem name
198+
// holds no `"`, `,` or space.
199+
entry.split(", ").find_map(|arg| {
200+
arg.strip_prefix("with: \"")?
201+
.strip_suffix('"')
202+
.filter(|v| !v.contains('"'))
203+
})
204+
})
181205
}
182206

183207
/// How the lock wires patch `uuid`.
@@ -1870,6 +1894,46 @@ mod tests {
18701894
assert!(!is_subdependency(&[" rails (7.0.0)"], "rails"));
18711895
}
18721896

1897+
/// #1989: Bundler 4.1's `override "other", with: "rails", in: "puma"`
1898+
/// keeps `puma`'s original edge to `other` in the specs and records
1899+
/// the replacement only in `OVERRIDES`, so its `with:` target is a
1900+
/// dependency of another spec although no sub-line names it.
1901+
#[test]
1902+
fn an_overrides_with_target_is_a_subdependency() {
1903+
let lock = |overrides: &str| {
1904+
format!(
1905+
"GEM\n remote: https://rubygems.org/\n specs:\n puma (6.0.0)\n \
1906+
other (>= 1)\n rails (7.0.0)\n\nDEPENDENCIES\n puma\n\nOVERRIDES\n\
1907+
{overrides}\nBUNDLED WITH\n 4.1.0.beta3\n"
1908+
)
1909+
};
1910+
let has = |overrides: &str| {
1911+
let text = lock(overrides);
1912+
let lines: Vec<&str> = text.split('\n').collect();
1913+
is_subdependency(&lines, "rails")
1914+
};
1915+
assert!(has(" \"other\", with: \"rails\", in: \"puma\"\n"));
1916+
assert!(has(" \"other\", in: \"puma\", with: \"rails\"\n"));
1917+
// A replacement version requirement, as Bundler 4.1 locks it.
1918+
assert!(has(
1919+
" \"other\", with: \"rails\", in: \"puma\", version: \">= 7, < 8\"\n"
1920+
));
1921+
assert!(has(
1922+
" \"x\", with: \"y\", in: \"z\"\n \"other\", with: \"rails\", in: \"puma\"\n"
1923+
));
1924+
// `rails` as the overridden name, the parent, or a prefix is not
1925+
// a `with:` target.
1926+
assert!(!has(" \"rails\", with: \"other\", in: \"puma\"\n"));
1927+
assert!(!has(" \"other\", with: \"x\", in: \"rails\"\n"));
1928+
assert!(!has(" \"other\", with: \"rails-html\", in: \"puma\"\n"));
1929+
// `with: nil` drops the dependency: no target.
1930+
assert!(!has(" \"rails\", with: nil, in: \"puma\"\n"));
1931+
assert!(!has(""));
1932+
// A `with:` entry outside the OVERRIDES section is not read.
1933+
let lines = ["DEPENDENCIES", " \"other\", with: \"rails\", in: \"puma\""];
1934+
assert!(!is_subdependency(&lines, "rails"));
1935+
}
1936+
18731937
/// #457: what the hosted rewriter itself writes for a TRANSITIVE gem
18741938
/// (an appended block, converged CHECKSUMS lock) must be recognized as
18751939
/// its append, and undoing it must give the manifest back byte for
@@ -2062,6 +2126,71 @@ mod tests {
20622126
assert_eq!(next_lock.as_deref(), Some(lock.as_str()));
20632127
}
20642128

2129+
/// #1989: a gem reached only through a Bundler 4.1 `override …, with:`
2130+
/// is transitive too. The lock keeps the parent's original edge, so no
2131+
/// spec sub-line names it; the restore must still drop the appended
2132+
/// block and its DEPENDENCIES entry, not write a new exact pin.
2133+
#[tokio::test]
2134+
async fn override_target_redirect_round_trips_through_restore() {
2135+
let client = super::super::UpstreamClient::new(true);
2136+
let upstream_sha = "2".repeat(64);
2137+
client
2138+
.seed_rubygems_sha256("rails", "7.0.0", &upstream_sha)
2139+
.await;
2140+
let ctx = ctx_with(&client);
2141+
let manifest = "source \"https://rubygems.org\"\n\ngem \"puma\"\n\n\
2142+
override \"other\", with: \"rails\", in: \"puma\"\n";
2143+
let lock = format!(
2144+
"GEM\n remote: https://rubygems.org/\n specs:\n puma (6.0.0)\n other\n \
2145+
rails (7.0.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n puma\n\nOVERRIDES\n \
2146+
\"other\", with: \"rails\", in: \"puma\"\n\nCHECKSUMS\n puma (6.0.0) sha256={}\n \
2147+
rails (7.0.0) sha256={upstream_sha}\n\nBUNDLED WITH\n 4.1.0.beta3\n",
2148+
"a".repeat(64)
2149+
);
2150+
let files = BTreeMap::from([
2151+
("Gemfile".to_string(), manifest.to_string()),
2152+
("Gemfile.lock".to_string(), lock.clone()),
2153+
]);
2154+
let r = crate::patch::redirect::rewrite_registry_redirect(
2155+
&files,
2156+
&[crate::patch::redirect::DepOverride {
2157+
ecosystem: "gem".into(),
2158+
name: "rails".into(),
2159+
namespace: None,
2160+
version: "7.0.0".into(),
2161+
token: "tok".into(),
2162+
patch_uuid: UUID.into(),
2163+
artifact_url: "https://patch.test/rails-7.0.0.gem".into(),
2164+
registry_override: Some(crate::patch::redirect::RegistryOverride {
2165+
kind: "rubygems-compact-index".into(),
2166+
index_url: IDX.into(),
2167+
identifiers: crate::patch::redirect::RegistryOverrideIdentifiers {
2168+
name: "rails".into(),
2169+
version: "7.0.0".into(),
2170+
gem_checksum_sha256: Some("f".repeat(64)),
2171+
..Default::default()
2172+
},
2173+
}),
2174+
integrity: Default::default(),
2175+
}],
2176+
);
2177+
let (hosted_manifest, hosted_lock) = (&r.files["Gemfile"], &r.files["Gemfile.lock"]);
2178+
assert!(hosted_lock.contains(" rails (= 7.0.0)!"), "{hosted_lock}");
2179+
let (next_lock, next_manifest, _) = restore_one(
2180+
&gem(),
2181+
Some(hosted_lock),
2182+
Some(hosted_manifest),
2183+
"Gemfile",
2184+
&global_sources(hosted_manifest),
2185+
&ctx,
2186+
)
2187+
.await
2188+
.unwrap()
2189+
.expect("the redirect is found");
2190+
assert_eq!(next_manifest.as_deref(), Some(manifest));
2191+
assert_eq!(next_lock.as_deref(), Some(lock.as_str()));
2192+
}
2193+
20652194
/// #1056: a gem the user declared inside a `source "…" do` block is
20662195
/// locked with a `!` source pin in `DEPENDENCIES` (bundler writes it
20672196
/// for any dependency with an explicit source, rubygems.org included).

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.