From 6d257691a39fb8c4191f4564ec6406d080dbf6b5 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 9 Aug 2026 18:41:49 +0900 Subject: feat(path-downloader): restrict a mirrored path package to its archivable files PathDownloader builds an ArchivableFilesFinder and hands it to Filesystem::mirror as the iterator that selects what to copy, so a path package installed by mirroring drops what .gitattributes marks export-ignore, what .gitignore excludes and the VCS directories. The port dropped the argument, and mirrored the source tree whole. Every method mirror() calls on the entries it walks is derived from the pathname, so the iterator is modeled as a list of pathnames rather than as a Traversable of SplFileInfo. Co-Authored-By: Claude Opus 5 (1M context) --- crates/shirabe-php-shim/src/fs.rs | 5 ---- .../shirabe-symfony-filesystem/src/filesystem.rs | 29 +++++++++++----------- crates/shirabe/src/downloader/path_downloader.rs | 15 +++++------ 3 files changed, 22 insertions(+), 27 deletions(-) (limited to 'crates') diff --git a/crates/shirabe-php-shim/src/fs.rs b/crates/shirabe-php-shim/src/fs.rs index 2142b0e7..c2b0dc41 100644 --- a/crates/shirabe-php-shim/src/fs.rs +++ b/crates/shirabe-php-shim/src/fs.rs @@ -190,11 +190,6 @@ impl RecursiveIteratorFileInfo { self.path.to_string_lossy().into_owned() } - // SplFileInfo::getLinkTarget(): readlink() on the entry. None is PHP's false-on-failure. - pub fn get_link_target(&self) -> Option { - readlink(&self.path) - } - pub fn get_size(&self) -> i64 { std::fs::metadata(&self.path) .map(|m| m.len() as i64) diff --git a/crates/shirabe-symfony-filesystem/src/filesystem.rs b/crates/shirabe-symfony-filesystem/src/filesystem.rs index 5d2fb1d2..737c3bea 100644 --- a/crates/shirabe-symfony-filesystem/src/filesystem.rs +++ b/crates/shirabe-symfony-filesystem/src/filesystem.rs @@ -280,7 +280,12 @@ impl Filesystem { Ok(()) } - pub fn mirror(&self, origin_dir: &str, target_dir: &str) -> anyhow::Result<()> { + pub fn mirror( + &self, + origin_dir: &str, + target_dir: &str, + iterator: Vec, + ) -> anyhow::Result<()> { let target_dir = shirabe_php_shim::rtrim(target_dir, Some("/\\")); let origin_dir = shirabe_php_shim::rtrim(origin_dir, Some("/\\")); let origin_dir_len = origin_dir.len(); @@ -298,21 +303,12 @@ impl Filesystem { .into()); } - let iterator = shirabe_php_shim::recursive_iterator_iterator( - shirabe_php_shim::recursive_directory_iterator( - &origin_dir, - shirabe_php_shim::SKIP_DOTS, - )?, - shirabe_php_shim::RecursiveIteratorIterator::SELF_FIRST, - ); - self.mkdir(&target_dir, 0o777)?; let mut files_created_while_mirroring: indexmap::IndexMap = indexmap::IndexMap::new(); - for file in &iterator { - let pathname = file.get_pathname(); + for pathname in iterator { // SplFileInfo::getRealPath(), which returns false for a path that cannot be resolved. let real_path = shirabe_php_shim::realpath(&pathname); if pathname == target_dir @@ -327,12 +323,15 @@ impl Filesystem { let target = format!("{}{}", target_dir, &pathname[origin_dir_len..]); files_created_while_mirroring.insert(target.clone(), true); - if file.is_link() { + if shirabe_php_shim::is_link(&pathname) { // PHP coerces the false getLinkTarget() returns on failure to the empty string. - self.symlink(&file.get_link_target().unwrap_or_default(), &target)?; - } else if file.is_dir() { + self.symlink( + &shirabe_php_shim::readlink(&pathname).unwrap_or_default(), + &target, + )?; + } else if shirabe_php_shim::is_dir(&pathname) { self.mkdir(&target, 0o777)?; - } else if file.is_file() { + } else if shirabe_php_shim::is_file(&pathname) { self.copy(&pathname, &target)?; } else { return Err(IOException::new( diff --git a/crates/shirabe/src/downloader/path_downloader.rs b/crates/shirabe/src/downloader/path_downloader.rs index 076f313d..5fa69ec7 100644 --- a/crates/shirabe/src/downloader/path_downloader.rs +++ b/crates/shirabe/src/downloader/path_downloader.rs @@ -443,13 +443,14 @@ impl DownloaderInterface for PathDownloader { io_interface::NORMAL, ); } - let _iterator = ArchivableFilesFinder::new(&real_url, vec![], false)?; - // PHP: $symfonyFilesystem->mirror($realUrl, $path, $iterator); - // TODO(phase-c): Symfony Filesystem::mirror takes a Traversable iterator as its third - // argument, but the external-package Filesystem stub does not model the iterator type - // that ArchivableFilesFinder (an IteratorAggregate) would be wrapped into, so None is - // passed and the mirrored file list is not restricted. - symfony_filesystem.mirror(&real_url, &path)?; + let iterator = ArchivableFilesFinder::new(&real_url, vec![], false)?; + symfony_filesystem.mirror( + &real_url, + &path, + iterator + .map(|entry| entry.to_string_lossy().into_owned()) + .collect(), + )?; } if output { -- cgit v1.3.1-4-g156e