diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71c67399..7475edf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,7 @@ jobs: -e 'patch::go_redirect' -e 'patch::bun_lock_text' \ -e 'utils::telemetry' -e 'utils::cleanup_blobs' \ -e 'utils::date' -e 'utils::fuzzy_match' \ + -e 'gem_setup::' -e 'composer_setup::' -e 'pth_hook::' \ crates; then echo '::error::use the canonical module paths (crate::vendor, patch::redirect::golang_local, crate::telemetry, manifest::cleanup_blobs, api::date, crawlers::fuzzy_match); the old-path aliases exist only for external consumers' exit 1 diff --git a/crates/socket-patch-cli/src/commands/setup.rs b/crates/socket-patch-cli/src/commands/setup.rs index 805bb8b9..166e44c7 100644 --- a/crates/socket-patch-cli/src/commands/setup.rs +++ b/crates/socket-patch-cli/src/commands/setup.rs @@ -1,7 +1,5 @@ use clap::Args; -use socket_patch_core::composer_setup::{self, ComposerSetupStatus}; use socket_patch_core::crawlers::python_crawler::is_python_project; -use socket_patch_core::gem_setup::{self, GemSetupStatus}; use socket_patch_core::manifest::operations::{read_manifest, write_manifest}; use socket_patch_core::manifest::schema::{PatchManifest, SetupConfig}; use socket_patch_core::package_json::detect::{is_setup_configured_str, PackageManager}; @@ -12,10 +10,12 @@ use socket_patch_core::package_json::update::{ remove_package_json, update_package_json, RemoveResult, RemoveStatus, UpdateResult, UpdateStatus, }; -use socket_patch_core::pth_hook::detect::{ +use socket_patch_core::setup::composer::{self, ComposerSetupStatus}; +use socket_patch_core::setup::gem::{self, GemSetupStatus}; +use socket_patch_core::setup::pypi::detect::{ deps_contain_hook, detect_python_pm, PythonPackageManager, }; -use socket_patch_core::pth_hook::edit::{ +use socket_patch_core::setup::pypi::edit::{ add_hook_dependency, pyproject_contains_hook, remove_hook_dependency, ManifestKind, PthEditResult, PthStatus, }; @@ -356,17 +356,17 @@ pub(crate) async fn configured_ecosystems( } // gem: the managed plugin directive is present in the Gemfile. - if let Some(project) = gem_setup::discover_bundler_project(&common.cwd).await { + if let Some(project) = gem::discover_bundler_project(&common.cwd).await { if let Ok(content) = tokio::fs::read_to_string(&project.gemfile).await { - if gem_setup::is_plugin_directive_present(&content) { + if gem::is_plugin_directive_present(&content) { set.insert(Ecosystem::Gem); } } } - if let Some(composer_json) = composer_setup::discover_composer_project(&common.cwd).await { + if let Some(composer_json) = composer::discover_composer_project(&common.cwd).await { if let Ok(content) = tokio::fs::read_to_string(&composer_json).await { - if composer_setup::is_hook_present(&content) { + if composer::is_hook_present(&content) { set.insert(Ecosystem::Composer); } } @@ -576,7 +576,7 @@ async fn build_gem_outcome(common: &GlobalArgs, remove: bool, dry_run: bool) -> if !eco_in_scope(common, ECO_GEM) { return SetupOutcome::default(); } - let project = match gem_setup::discover_bundler_project(&common.cwd).await { + let project = match gem::discover_bundler_project(&common.cwd).await { Some(p) => p, None => return SetupOutcome::default(), }; @@ -587,9 +587,9 @@ async fn build_gem_outcome(common: &GlobalArgs, remove: bool, dry_run: bool) -> }; let results = if remove { - gem_setup::remove_plugin_directive(&project, dry_run).await + gem::remove_plugin_directive(&project, dry_run).await } else { - gem_setup::add_plugin_directive(&project, dry_run).await + gem::add_plugin_directive(&project, dry_run).await }; let mut added_paths: Vec = Vec::new(); @@ -647,7 +647,7 @@ async fn build_composer_outcome(common: &GlobalArgs, remove: bool, dry_run: bool if !eco_in_scope(common, ECO_COMPOSER) { return SetupOutcome::default(); } - let composer_json = match composer_setup::discover_composer_project(&common.cwd).await { + let composer_json = match composer::discover_composer_project(&common.cwd).await { Some(p) => p, None => return SetupOutcome::default(), }; @@ -658,9 +658,9 @@ async fn build_composer_outcome(common: &GlobalArgs, remove: bool, dry_run: bool }; let r = if remove { - composer_setup::remove_hook(&composer_json, dry_run).await + composer::remove_hook(&composer_json, dry_run).await } else { - composer_setup::add_hook(&composer_json, dry_run).await + composer::add_hook(&composer_json, dry_run).await }; let mut added_paths: Vec = Vec::new(); @@ -716,13 +716,13 @@ async fn append_composer_check_entries( if !eco_in_scope(common, ECO_COMPOSER) { return false; } - let composer_json = match composer_setup::discover_composer_project(&common.cwd).await { + let composer_json = match composer::discover_composer_project(&common.cwd).await { Some(p) => p, None => return false, }; let (state, err) = match tokio::fs::read_to_string(&composer_json).await { Ok(content) => { - if composer_setup::is_hook_present(&content) { + if composer::is_hook_present(&content) { (CheckState::Configured, None) } else { (CheckState::NeedsConfiguration, None) @@ -798,13 +798,13 @@ async fn append_gem_check_entries( if !eco_in_scope(common, ECO_GEM) { return false; } - let project = match gem_setup::discover_bundler_project(&common.cwd).await { + let project = match gem::discover_bundler_project(&common.cwd).await { Some(p) => p, None => return false, }; let (state, err) = match tokio::fs::read_to_string(&project.gemfile).await { Ok(content) => { - if gem_setup::is_plugin_directive_present(&content) { + if gem::is_plugin_directive_present(&content) { (CheckState::Configured, None) } else { (CheckState::NeedsConfiguration, None) @@ -813,14 +813,14 @@ async fn append_gem_check_entries( Err(e) => (CheckState::Error, Some(e.to_string())), }; entries.push(("gemfile", project.gemfile.display().to_string(), state, err)); - let dir_state = if gem_setup::plugin_files_present(&project.root).await { + let dir_state = if gem::plugin_files_present(&project.root).await { CheckState::Configured } else { CheckState::NeedsConfiguration }; entries.push(( "gem_plugin", - gem_setup::plugin_dir(&project.root).display().to_string(), + gem::plugin_dir(&project.root).display().to_string(), dir_state, None, )); diff --git a/crates/socket-patch-core/src/crawlers/ruby_crawler.rs b/crates/socket-patch-core/src/crawlers/ruby_crawler.rs index 23a61a8d..8a6fed15 100644 --- a/crates/socket-patch-core/src/crawlers/ruby_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/ruby_crawler.rs @@ -132,7 +132,7 @@ impl RubyCrawler { /// `Gemfile`/`Gemfile.lock` and the alternate `gems.rb`/`gems.locked` /// (`Bundler::SharedHelpers.default_gemfile`). Both count: the project /// gate must recognize every project `setup` can wire, and - /// `gem_setup::discover_bundler_project` already walks up for `gems.rb`. + /// `setup::gem::discover_bundler_project` already walks up for `gems.rb`. /// Gating on `Gemfile` alone left a `gems.rb` project with a /// non-deployment `bundle install` undiscoverable — the bundler plugin /// `setup` installs would run `apply` on every `bundle install` and diff --git a/crates/socket-patch-core/src/lib.rs b/crates/socket-patch-core/src/lib.rs index 5358a20d..5337ab26 100644 --- a/crates/socket-patch-core/src/lib.rs +++ b/crates/socket-patch-core/src/lib.rs @@ -1,15 +1,21 @@ pub mod api; -pub mod composer_setup; pub mod constants; pub mod crawlers; -pub mod gem_setup; pub mod hash; pub mod manifest; pub mod package_json; pub mod patch; -pub mod pth_hook; +pub mod setup; pub mod telemetry; pub mod update; pub mod utils; pub mod vendor; pub mod vex; + +// Moved modules — these aliases keep the old top-level paths compiling for +// external consumers of the published crate. Internal code must import the +// canonical `setup::*` paths; CI greps reject new uses of the old ones. +// Drop these aliases at 4.0. +pub use setup::composer as composer_setup; +pub use setup::gem as gem_setup; +pub use setup::pypi as pth_hook; diff --git a/crates/socket-patch-core/src/composer_setup/mod.rs b/crates/socket-patch-core/src/setup/composer/mod.rs similarity index 99% rename from crates/socket-patch-core/src/composer_setup/mod.rs rename to crates/socket-patch-core/src/setup/composer/mod.rs index f97900eb..3e2f8d69 100644 --- a/crates/socket-patch-core/src/composer_setup/mod.rs +++ b/crates/socket-patch-core/src/setup/composer/mod.rs @@ -36,7 +36,7 @@ const HOOK_EVENTS: &[&str] = &["post-install-cmd", "post-update-cmd"]; /// a slightly different flag set still reads as configured. const HOOK_MARKER: &str = "socket-patch apply"; -/// Outcome of one setup edit. Mirrors `gem_setup::GemSetupStatus`. +/// Outcome of one setup edit. Mirrors `setup::gem::GemSetupStatus`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ComposerSetupStatus { Updated, diff --git a/crates/socket-patch-core/src/gem_setup/mod.rs b/crates/socket-patch-core/src/setup/gem/mod.rs similarity index 100% rename from crates/socket-patch-core/src/gem_setup/mod.rs rename to crates/socket-patch-core/src/setup/gem/mod.rs diff --git a/crates/socket-patch-core/src/gem_setup/templates/gemspec.tmpl b/crates/socket-patch-core/src/setup/gem/templates/gemspec.tmpl similarity index 100% rename from crates/socket-patch-core/src/gem_setup/templates/gemspec.tmpl rename to crates/socket-patch-core/src/setup/gem/templates/gemspec.tmpl diff --git a/crates/socket-patch-core/src/gem_setup/templates/plugins.rb.tmpl b/crates/socket-patch-core/src/setup/gem/templates/plugins.rb.tmpl similarity index 100% rename from crates/socket-patch-core/src/gem_setup/templates/plugins.rb.tmpl rename to crates/socket-patch-core/src/setup/gem/templates/plugins.rb.tmpl diff --git a/crates/socket-patch-core/src/gem_setup/update.rs b/crates/socket-patch-core/src/setup/gem/update.rs similarity index 100% rename from crates/socket-patch-core/src/gem_setup/update.rs rename to crates/socket-patch-core/src/setup/gem/update.rs diff --git a/crates/socket-patch-core/src/setup/mod.rs b/crates/socket-patch-core/src/setup/mod.rs new file mode 100644 index 00000000..c7ed6f1c --- /dev/null +++ b/crates/socket-patch-core/src/setup/mod.rs @@ -0,0 +1,22 @@ +//! Per-ecosystem `setup` backends: the code that wires (and unwires) each +//! ecosystem's auto-re-apply hook into a user's project, consumed by the +//! CLI's `setup` command. +//! +//! One concept, one home — these previously lived as four top-level modules +//! under four naming schemes (`gem_setup`, `composer_setup`, `pth_hook`, +//! plus `package_json`'s setup surface): +//! +//! * [`gem`] — Bundler plugin directive in the Gemfile + generated plugin +//! gem, re-applying gem patches on `bundle install`. +//! * [`composer`] — post-install hook in `composer.json`. +//! * [`pypi`] — the `socket-patch[hook]` dependency whose `.pth` wheel +//! re-applies pypi patches at interpreter startup. +//! * [`npm`] — a thin alias: the npm backend's real home is +//! [`crate::package_json`], which stays top-level because it doubles as +//! the crate-wide shared npm-manifest library (crawlers and vendor read +//! package.json through it too). + +pub mod composer; +pub mod gem; +pub mod npm; +pub mod pypi; diff --git a/crates/socket-patch-core/src/setup/npm.rs b/crates/socket-patch-core/src/setup/npm.rs new file mode 100644 index 00000000..7ba4dd16 --- /dev/null +++ b/crates/socket-patch-core/src/setup/npm.rs @@ -0,0 +1,10 @@ +//! The npm setup backend, by alias. +//! +//! npm's hook wiring lives in [`crate::package_json`] — that module stays +//! top-level because it is also the crate-wide shared npm-manifest library +//! (crawlers and vendor parse package.json through it). This alias exists so +//! `setup::*` enumerates every ecosystem backend in one place. + +pub use crate::package_json::detect::{is_setup_configured_str, PackageManager}; +pub use crate::package_json::find::{detect_package_manager, find_package_json_files}; +pub use crate::package_json::update::{remove_package_json, update_package_json}; diff --git a/crates/socket-patch-core/src/pth_hook/detect.rs b/crates/socket-patch-core/src/setup/pypi/detect.rs similarity index 100% rename from crates/socket-patch-core/src/pth_hook/detect.rs rename to crates/socket-patch-core/src/setup/pypi/detect.rs diff --git a/crates/socket-patch-core/src/pth_hook/edit.rs b/crates/socket-patch-core/src/setup/pypi/edit.rs similarity index 100% rename from crates/socket-patch-core/src/pth_hook/edit.rs rename to crates/socket-patch-core/src/setup/pypi/edit.rs diff --git a/crates/socket-patch-core/src/pth_hook/mod.rs b/crates/socket-patch-core/src/setup/pypi/mod.rs similarity index 100% rename from crates/socket-patch-core/src/pth_hook/mod.rs rename to crates/socket-patch-core/src/setup/pypi/mod.rs diff --git a/crates/socket-patch-core/src/utils/toml_edit_ext.rs b/crates/socket-patch-core/src/utils/toml_edit_ext.rs index dd05a900..5259a4e8 100644 --- a/crates/socket-patch-core/src/utils/toml_edit_ext.rs +++ b/crates/socket-patch-core/src/utils/toml_edit_ext.rs @@ -1,6 +1,6 @@ //! Small structured-TOML helpers shared by every module that edits or sniffs -//! TOML (`pth_hook`, `vendor::cargo_config`, `vendor::pypi`, -//! `vendor::pypi_uv`). Extracted from `pth_hook` so the pypi setup backend no +//! TOML (`setup::pypi`, `vendor::cargo_config`, `vendor::pypi`, +//! `vendor::pypi_uv`). Extracted from the pypi setup backend (now `setup::pypi`) so it no //! longer owns the crate's generic TOML seam. use toml_edit::{Item, Table}; diff --git a/crates/socket-patch-core/src/vendor/cargo_config.rs b/crates/socket-patch-core/src/vendor/cargo_config.rs index e5585b67..d2557ba1 100644 --- a/crates/socket-patch-core/src/vendor/cargo_config.rs +++ b/crates/socket-patch-core/src/vendor/cargo_config.rs @@ -1,7 +1,7 @@ //! Read / write `/.cargo/config.toml` for the cargo vendor //! backend's `[patch.crates-io]` wiring. //! -//! Mirrors the contract style of [`crate::pth_hook::edit`]: pure +//! Mirrors the contract style of [`crate::setup::pypi::edit`]: pure //! `fn(&str) -> Result, String>` transforms (`Some(new)` = //! changed, `None` = already in the desired state) wrapped by async //! read-or-create / write helpers that honour `dry_run` and preserve the diff --git a/crates/socket-patch-core/tests/crawler_ruby_e2e.rs b/crates/socket-patch-core/tests/crawler_ruby_e2e.rs index 2b0b4bbd..ac137863 100644 --- a/crates/socket-patch-core/tests/crawler_ruby_e2e.rs +++ b/crates/socket-patch-core/tests/crawler_ruby_e2e.rs @@ -385,7 +385,7 @@ async fn get_gem_paths_with_gemfile_lock_only_returns_gemdir() { /// Bundler accepts `gems.rb` as the alternate spelling of `Gemfile` /// (`Bundler::SharedHelpers.default_gemfile`), and -/// `gem_setup::discover_bundler_project` already walks up for it — so +/// `setup::gem::discover_bundler_project` already walks up for it — so /// `setup` will wire a `gems.rb` project with the bundler plugin that runs /// `apply` on every `bundle install`. The crawler's project gate must /// recognize the same spelling; otherwise that project's non-deployment