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
2 changes: 1 addition & 1 deletion crates/socket-patch-cli/CLI_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2231,7 +2231,7 @@ with `reason: <reason>: `:
|---|---|---|
| `vendor_jvm_shape_unsupported` | refusal, nothing written | `gradle_below_6_8`, `android_or_kmp` (also `available-at` module redirects), `gradle_exclusive_content_conflict` (a user rule claiming the module, in any script), `gradle_range_excludes_vendored` (no declared selector admits the vendored version), `gradle_verification_unparseable`, `gradle_index_unreadable`, `build_file_unreadable` (including a non-UTF-8 settings file), `build_file_outside_root`, `not_build_root` (run from a directory an ancestor settings file includes or may include, from a project with no settings file of its own below one, or below an ancestor settings file that is not UTF-8; `repair` refuses there too), `no_build_file` (no `pom.xml`, Gradle, sbt or scala-cli build at the root), `legacy_maven_root` (the root's ledger still holds a pre-v5 single-POM `maven_pom_repository` entry; the whole root is refused until `vendor --revert`, then vendor again) |
| `vendor_jvm_upstream_unavailable` | refusal | `classifier_unavailable` (a declared classifier jar no cache or registry has), `module_unavailable` (the pom declares Gradle module metadata that cannot be sourced), `pom_unavailable`, `verification_metadata_unavailable` |
| `vendor_jvm_degraded` | applied, VEX withheld | `gradle_unscanned_build_logic`, `unwired_build_logic` (a nonliteral included build), `settings_plugins_unwired`, `classifier_unpatched_copy` (a classifier jar carries an unpatched copy of a patched member), `verification_parent_chain_unhandled` |
| `vendor_jvm_degraded` | applied, VEX withheld | `gradle_unscanned_build_logic`, `unwired_build_logic` (a nonliteral included build), `settings_plugins_unwired`, `classifier_unpatched_copy` (a classifier jar carries an unpatched copy of a patched member), `verification_parent_chain_unhandled`, `conflicting_managed_version` / `management_unresolved` (Maven reactor: an imported BOM or an out-of-checkout parent manages the artifact at another version, or its POM is unavailable; that local root is not pinned) |
| `vendor_jvm_note` | applied, informational | `range_declared` (a range, prefix or rich selector lists versions from the derived `maven-metadata.xml`), `ide_sources_unavailable` |
| `vendor_jvm_upstream_unverified` | warning | Upstream metadata was taken offline and not authenticated against registry checksums. |

Expand Down
53 changes: 49 additions & 4 deletions crates/socket-patch-core/src/vendor/jvm/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ use super::super::state::{VendorEntry, WiringAction, WiringRecord};
use super::super::{RevertOpts, RevertOutcome, VendorWarning};
use super::{
coursier_tree, gradle, layout, maven_reactor, op_of, op_str, sbt, scala_cli, sha256_hex,
Coords, JvmPlan, JvmUnplan, Shape, CONFIG_LINE_KIND, COURSIER_INDEX_KIND, CREATED_DIR_KIND,
DERIVED_METADATA_KIND, KINDS, OWNED_FILE_KIND, POM_FRAGMENT_KIND, SBT_FRAGMENT_KIND,
SETTINGS_FRAGMENT_KIND, TREE_KIND, VERIFICATION_FRAGMENT_KIND,
Coords, JvmPlan, JvmUnplan, ReadFn, Shape, CONFIG_LINE_KIND, COURSIER_INDEX_KIND,
CREATED_DIR_KIND, DERIVED_METADATA_KIND, KINDS, OWNED_FILE_KIND, POM_FRAGMENT_KIND,
SBT_FRAGMENT_KIND, SETTINGS_FRAGMENT_KIND, TREE_KIND, VERIFICATION_FRAGMENT_KIND,
};

/// Whether `entry` was written by this backend: it has wiring and every
Expand Down Expand Up @@ -865,6 +865,33 @@ pub fn entry_wired_checked(root: &Path, entry: &VendorEntry) -> Result<bool, Str
wired
}

/// [`maven_reactor::ExternalPoms`] for `patch` from the Maven local
/// repository `local_repo` alone (`None` entries: not there).
fn local_external_poms(
read: ReadFn<'_>,
patch: &super::JvmPatch<'_>,
local_repo: Option<&Path>,
) -> maven_reactor::ExternalPoms {
let mut known = maven_reactor::ExternalPoms::new();
loop {
let need = maven_reactor::external_poms_needed(read, patch, &known);
if need.is_empty() {
return known;
}
for (g, a, v) in need {
let bytes = local_repo.and_then(|repo| {
let path = repo
.join(g.replace('.', "/"))
.join(&a)
.join(&v)
.join(format!("{a}-{v}.pom"));
read_regular_to_bytes_sync(&path).ok()
});
known.insert((g, a, v), bytes);
}
}
}

/// Verify every recorded file plus the effective wiring, without writes or network I/O.
pub fn check_entry(
root: &Path,
Expand Down Expand Up @@ -944,8 +971,26 @@ pub fn check_entry(
patched_members: &patched,
};
let config = !entry.wiring.iter().any(|w| op_of(w) == "config_none");
let plan = super::plan_with_config(shape_of(&entry.wiring), &read, &list, &patch, config)
// The management a Maven pin would override from outside the checkout
// (#488), read from the local repository only (no network). When some
// of it is not there, either decision `vendor` could have made is in
// sync: the pin it wrote when it could read it, or none.
let shape = shape_of(&entry.wiring);
let external = if matches!(shape, Shape::MavenReactor | Shape::Mixed) {
Some(local_external_poms(&read, &patch, local_repo))
} else {
None
};
let plan = super::plan_with_external(shape, &read, &list, &patch, config, external.as_ref())
.map_err(|e| e.detail)?;
let incomplete = external
.as_ref()
.is_some_and(|known| known.values().any(Option::is_none));
let plan = if incomplete && !plan.writes.is_empty() {
super::plan_with_config(shape, &read, &list, &patch, config).map_err(|e| e.detail)?
} else {
plan
};
if let Some(w) = plan.writes.first() {
return Err(format!("vendored wiring or metadata drifted: {}", w.rel));
}
Expand Down
Loading
Loading