Skip to content

Commit 7b2c89f

Browse files
committed
Merge branch 'l-monninger/account-balances-redux' of https://github.com/movementlabsxyz/movement-to-maptos into l-monninger/expanded-docs
2 parents e0c13d8 + b3b921d commit 7b2c89f

File tree

5 files changed

+148
-16
lines changed

5 files changed

+148
-16
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ libp2p = { version = "0.55.0", features = ["tcp", "quic"] }
8484
chrono = "0.4.31"
8585
rand = "0.7.3"
8686
uuid = "1.10.0"
87+
glob = "0.3.2"
8788

8889
poem = { version = "=3.1.3", features = ["anyhow", "compression", "rustls"] }
8990
poem-openapi = { version = "=5.1.2", features = ["swagger-ui", "url"] }

checks/migrator/checks/sketchpad/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mtma-node-test-types = { workspace = true }
2929
[dev-dependencies]
3030
tracing-test = { workspace = true }
3131
tracing = { workspace = true }
32+
tempfile = { workspace = true }
3233

3334
[lints]
3435
workspace = true

migration/util/node-types/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ uuid = { workspace = true }
2828
walkdir = { workspace = true }
2929
mtma-types = { workspace = true }
3030

31+
[dev-dependencies]
32+
tempfile = { workspace = true }
33+
tracing-test = { workspace = true }
34+
3135
[lints]
3236
workspace = true

migration/util/node-types/src/executor/movement_executor.rs

Lines changed: 139 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,74 @@ pub struct MovementNode {
3434
opt_executor: MovementOptExecutor,
3535
}
3636

37-
/// Copies a directory recursively.
38-
fn copy_dir_recursive(src: &Path, dst: &Path) -> std::io::Result<()> {
39-
for entry in WalkDir::new(src) {
40-
let entry = entry?;
41-
let rel_path = entry.path().strip_prefix(src).unwrap();
42-
let dest_path = dst.join(rel_path);
43-
44-
if entry.file_type().is_dir() {
45-
fs::create_dir_all(&dest_path)?;
46-
} else {
47-
fs::copy(entry.path(), &dest_path)?;
48-
}
49-
}
50-
Ok(())
37+
/// Copies a directory recursively while ignoring specified paths
38+
fn copy_dir_recursive_with_ignore(src: &Path, ignore_paths: impl IntoIterator<Item = impl AsRef<Path>>, dst: &Path) -> Result<(), anyhow::Error> {
39+
let ignore_paths: Vec<_> = ignore_paths.into_iter().collect();
40+
let mut errors: Vec<Result<(), anyhow::Error>> = Vec::new();
41+
42+
// Configure WalkDir to be more resilient
43+
let walker = WalkDir::new(src)
44+
.follow_links(false)
45+
.same_file_system(true)
46+
.into_iter()
47+
.filter_entry(|entry| {
48+
// Early filter for ignored paths to avoid permission errors
49+
let path = entry.path();
50+
!ignore_paths.iter().any(|ignore| path.to_string_lossy().contains(ignore.as_ref().to_string_lossy().as_ref()))
51+
});
52+
53+
for entry in walker {
54+
let entry = match entry {
55+
Ok(e) => e,
56+
Err(e) => {
57+
// Check if this is a permission error on an ignored path
58+
if let Some(path) = e.path() {
59+
if ignore_paths.iter().any(|ignore| path.to_string_lossy().contains(ignore.as_ref().to_string_lossy().as_ref())) {
60+
debug!("Ignoring permission error on ignored path: {}", path.display());
61+
continue;
62+
}
63+
}
64+
// For other errors, log with info for now and fail.
65+
info!("Error accessing path: {:?}", e);
66+
errors.push(Err(anyhow::anyhow!("failed to get entry: {:?}", e)));
67+
continue;
68+
}
69+
};
70+
71+
debug!("Processing entry: {}", entry.path().display());
72+
73+
let path = entry.path();
74+
let dest_path = dst.join(path.strip_prefix(src).context("failed to strip prefix")?);
75+
76+
if entry.file_type().is_dir() {
77+
match fs::create_dir_all(&dest_path) {
78+
Ok(_) => (),
79+
Err(e) => errors.push(Err(e.into())),
80+
}
81+
} else {
82+
if let Some(parent) = dest_path.parent() {
83+
match fs::create_dir_all(parent) {
84+
Ok(_) => (),
85+
Err(e) => errors.push(Err(e.into())),
86+
}
87+
}
88+
match fs::copy(path, &dest_path) {
89+
Ok(_) => (),
90+
Err(e) => errors.push(Err(e.into())),
91+
}
92+
}
93+
}
94+
95+
// Combine all results into one
96+
if !errors.is_empty() {
97+
let mut total_error_message = String::from("failed to copy directory with the following errors: ");
98+
for error in errors {
99+
total_error_message = total_error_message + &format!("{:?}\n", error);
100+
}
101+
return Err(anyhow::anyhow!(total_error_message));
102+
}
103+
104+
Ok(())
51105
}
52106

53107
/// Sets all permission in a directory recursively.
@@ -74,13 +128,20 @@ impl MovementNode {
74128

75129
// Copy the entire .movement directory recursively
76130
let movement_dir = dir.join(".movement");
77-
copy_dir_recursive(&movement_dir, &debug_dir)?;
131+
132+
// set the permissions on the movement dir to 755
133+
// Note: this would mess up celestia node permissions, but we don't care about that here.
134+
// We really only care about maptos db permissions.
135+
fs::set_permissions(&movement_dir, Permissions::from_mode(0o755)).context(format!("failed to set permissions on the movement directory {}", movement_dir.display()))?;
136+
137+
// don't copy anything from the celestia directory
138+
copy_dir_recursive_with_ignore(&movement_dir, [PathBuf::from("celestia")], &debug_dir).context("failed to copy movement dir")?;
78139

79140
// Set all permissions in the debug directory recursively
80141
// Note: this would mess up celestia node permissions, but we don't care about that here.
81142
// We really only care about maptos db permissions.
82143
// TODO: tighten the copying accordingly.
83-
set_permissions_recursive(&debug_dir, Permissions::from_mode(0o755))?;
144+
set_permissions_recursive(&debug_dir, Permissions::from_mode(0o755)).context(format!("failed to set permissions on the debug directory {}", debug_dir.display()))?;
84145

85146
let movement_args = MovementArgs { movement_path: Some(debug_dir.display().to_string()) };
86147

@@ -415,3 +476,65 @@ impl<'a> Iterator for AccountAddressIterator<'a> {
415476
self.get_next_address()
416477
}
417478
}
479+
480+
#[cfg(test)]
481+
mod test {
482+
use super::*;
483+
use tempfile::TempDir;
484+
485+
#[test]
486+
#[tracing_test::traced_test]
487+
fn test_copy_dir_recursive_with_ignore() -> Result<(), anyhow::Error> {
488+
// put some file in a temp dir
489+
let temp_dir = TempDir::new()?;
490+
491+
// write a file that should be copied
492+
let file_path = temp_dir.path().join("maptos").join("test_file.txt");
493+
fs::create_dir_all(file_path.parent().context("failed to get parent directory for file that should be copied")?).context("failed to create directory")?;
494+
fs::write(file_path, "test").context("failed to write file that should be copied")?;
495+
496+
// write a file that should not be copied
497+
let file_path = temp_dir.path().join("celestia").join("test_file2.txt");
498+
fs::create_dir_all(file_path.parent().context("failed to get parent directory for file that should not be copied")?).context("failed to create directory")?;
499+
fs::write(file_path, "test").context("failed to write file that should not be copied")?;
500+
501+
// create the target temp dir
502+
let dst = TempDir::new()?;
503+
504+
// copy the file to a new dir, ignoring celestia directory
505+
copy_dir_recursive_with_ignore(&temp_dir.path(), [PathBuf::from("celestia")], &dst.path()).context("failed to copy directory")?;
506+
507+
// check that the file was copied
508+
assert!(dst.path().join("maptos").join("test_file.txt").exists());
509+
510+
// check that the celestia directory was not copied
511+
assert!(!dst.path().join("celestia").join("test_file2.txt").exists());
512+
513+
Ok(())
514+
}
515+
516+
// Somehow the following test is failing on CI: https://github.com/movementlabsxyz/movement-migration/actions/runs/15386989846/job/43287594343
517+
// This indicates that failure is not due to the inability to ignore copy, but rather some issue performing an oepration that requires permissions.
518+
#[test]
519+
fn test_are_you_kidding_me() -> Result<(), anyhow::Error> {
520+
521+
let source_dir = TempDir::new()?;
522+
let target_dir = TempDir::new()?;
523+
524+
let path_that_must_be_ignored = source_dir.path().join(".movement/celestia/c1860ae680eb2d91927b/.celestia-app/keyring-test");
525+
526+
fs::create_dir_all(path_that_must_be_ignored.parent().context("failed to get parent directory for path that must be ignored")?).context("failed to create directory")?;
527+
// write a file that must not be ignored
528+
fs::write(path_that_must_be_ignored.clone(), "test").context("failed to write file that must not be ignored")?;
529+
// set permissions to 000 on the file and then on the parent directory
530+
fs::set_permissions(path_that_must_be_ignored.clone(), Permissions::from_mode(0o000)).context("failed to set permissions on file that must not be ignored")?;
531+
fs::set_permissions(path_that_must_be_ignored.parent().context("failed to get parent directory for path that must be ignored")?, Permissions::from_mode(0o000)).context("failed to set permissions on parent directory that must not be ignored")?;
532+
533+
copy_dir_recursive_with_ignore(source_dir.path(), ["celestia"], target_dir.path()).context("failed to copy directory")?;
534+
535+
assert!(!target_dir.path().join("celestia").join("c1860ae680eb2d91927b").join(".celestia-app").join("keyring-test").exists());
536+
537+
Ok(())
538+
}
539+
540+
}

0 commit comments

Comments
 (0)