Skip to content

Commit 7958efc

Browse files
committed
fix: align packaged cli integration cleanup
1 parent 160d38e commit 7958efc

3 files changed

Lines changed: 138 additions & 36 deletions

File tree

cli/integrate-tests/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ pub fn pack_cli_artifacts() -> Option<PackedArtifacts> {
575575

576576
eprintln!("[tnmsc-integrate-tests] running assemble-npm...");
577577
let assemble = run_tnmsc_with_env(
578-
&["assemble-npm"],
578+
&["assemble-npm", "--profile", "debug"],
579579
&workspace_root(),
580580
&[
581581
("TNMSC_NPM_PACKAGE_ROOT", package_root.as_str()),
@@ -629,7 +629,7 @@ pub fn pack_cli_artifacts() -> Option<PackedArtifacts> {
629629
);
630630

631631
let assemble_cross = run_tnmsc_with_env(
632-
&["assemble-npm"],
632+
&["assemble-npm", "--profile", "debug"],
633633
&workspace_root(),
634634
&[
635635
("TNMSC_NPM_PACKAGE_ROOT", package_root.as_str()),

sdk/src/infra/desk_paths.rs

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -215,38 +215,80 @@ pub fn delete_path_sync<P: AsRef<Path>>(path: P) -> io::Result<()> {
215215
delete_path(path).map(|_| ())
216216
}
217217

218+
#[cfg(windows)]
219+
fn strip_windows_extended_prefix(path: &Path) -> Option<PathBuf> {
220+
let raw = path.to_string_lossy();
221+
222+
if let Some(stripped) = raw.strip_prefix(r"\\?\UNC\") {
223+
return Some(PathBuf::from(format!(r"\\{}", stripped.replace('/', "\\"))));
224+
}
225+
226+
if let Some(stripped) = raw.strip_prefix(r"\\?\") {
227+
return Some(PathBuf::from(stripped.replace('/', "\\")));
228+
}
229+
230+
if let Some(stripped) = raw.strip_prefix("//?/UNC/") {
231+
return Some(PathBuf::from(format!(r"\\{}", stripped.replace('/', "\\"))));
232+
}
233+
234+
if let Some(stripped) = raw.strip_prefix("//?/") {
235+
return Some(PathBuf::from(stripped.replace('/', "\\")));
236+
}
237+
238+
None
239+
}
240+
241+
fn resolve_delete_path(path: &Path) -> io::Result<Option<(PathBuf, fs::Metadata)>> {
242+
match fs::symlink_metadata(path) {
243+
Ok(metadata) => Ok(Some((path.to_path_buf(), metadata))),
244+
Err(err) if err.kind() == io::ErrorKind::NotFound => {
245+
#[cfg(windows)]
246+
if let Some(fallback) = strip_windows_extended_prefix(path)
247+
&& fallback != path
248+
{
249+
return match fs::symlink_metadata(&fallback) {
250+
Ok(metadata) => Ok(Some((fallback, metadata))),
251+
Err(fallback_err) if fallback_err.kind() == io::ErrorKind::NotFound => Ok(None),
252+
Err(fallback_err) => Err(fallback_err),
253+
};
254+
}
255+
256+
Ok(None)
257+
}
258+
Err(err) => Err(err),
259+
}
260+
}
261+
218262
fn delete_path(path: impl AsRef<Path>) -> io::Result<bool> {
219-
let path = path.as_ref();
220-
let metadata = match fs::symlink_metadata(path) {
221-
Ok(metadata) => metadata,
222-
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(false),
223-
Err(err) => return Err(err),
263+
let (path, metadata) = match resolve_delete_path(path.as_ref())? {
264+
Some(resolved) => resolved,
265+
None => return Ok(false),
224266
};
225267

226268
if metadata.file_type().is_symlink() {
227269
#[cfg(windows)]
228270
{
229-
return fs::metadata(path)
271+
return fs::metadata(&path)
230272
.map(|resolved| resolved.is_dir())
231273
.unwrap_or(false)
232-
.then(|| fs::remove_dir(path).or_else(|_| fs::remove_file(path)))
233-
.unwrap_or_else(|| fs::remove_file(path).or_else(|_| fs::remove_dir(path)))
274+
.then(|| fs::remove_dir(&path).or_else(|_| fs::remove_file(&path)))
275+
.unwrap_or_else(|| fs::remove_file(&path).or_else(|_| fs::remove_dir(&path)))
234276
.map(|_| true);
235277
}
236278
#[cfg(not(windows))]
237279
{
238-
return fs::remove_file(path).map(|_| true);
280+
return fs::remove_file(&path).map(|_| true);
239281
}
240282
}
241283

242284
if metadata.is_dir() {
243-
match fs::remove_dir_all(path) {
285+
match fs::remove_dir_all(&path) {
244286
Ok(()) => Ok(true),
245287
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
246288
Err(err) => Err(err),
247289
}
248290
} else {
249-
match fs::remove_file(path) {
291+
match fs::remove_file(&path) {
250292
Ok(()) => Ok(true),
251293
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
252294
Err(err) => Err(err),
@@ -293,18 +335,16 @@ pub struct DeleteTargetsResult {
293335
}
294336

295337
fn delete_empty_directory(path: impl AsRef<Path>) -> io::Result<bool> {
296-
let path = path.as_ref();
297-
let metadata = match fs::symlink_metadata(path) {
298-
Ok(metadata) => metadata,
299-
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(false),
300-
Err(err) => return Err(err),
338+
let (path, metadata) = match resolve_delete_path(path.as_ref())? {
339+
Some(resolved) => resolved,
340+
None => return Ok(false),
301341
};
302342

303343
if metadata.file_type().is_symlink() || !metadata.is_dir() {
304344
return Ok(false);
305345
}
306346

307-
match fs::remove_dir(path) {
347+
match fs::remove_dir(&path) {
308348
Ok(()) => Ok(true),
309349
Err(err)
310350
if err.kind() == io::ErrorKind::NotFound
@@ -708,6 +748,36 @@ mod tests {
708748
assert!(target_dir.exists());
709749
}
710750

751+
#[cfg(windows)]
752+
#[test]
753+
fn delete_files_handles_windows_extended_prefix_paths() {
754+
let dir = tempdir().unwrap();
755+
let file = dir.path().join("artifact.txt");
756+
fs::write(&file, b"data").unwrap();
757+
758+
let extended = format!(r"\\?\{}", file.display());
759+
let result = delete_files(&[extended]);
760+
761+
assert_eq!(result.deleted, 1);
762+
assert!(result.errors.is_empty());
763+
assert!(!file.exists());
764+
}
765+
766+
#[cfg(windows)]
767+
#[test]
768+
fn delete_files_handles_windows_slash_extended_prefix_paths() {
769+
let dir = tempdir().unwrap();
770+
let file = dir.path().join("artifact.txt");
771+
fs::write(&file, b"data").unwrap();
772+
773+
let slash_extended = format!("//?/{}", file.display().to_string().replace('\\', "/"));
774+
let result = delete_files(&[slash_extended]);
775+
776+
assert_eq!(result.deleted, 1);
777+
assert!(result.errors.is_empty());
778+
assert!(!file.exists());
779+
}
780+
711781
#[test]
712782
fn compact_deletion_targets_removes_covered_paths() {
713783
let dir = tempdir().unwrap();

sdk/src/services/clean_service.rs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,58 @@ pub fn clean(options: MemorySyncCommandOptions) -> Result<MemorySyncCommandResul
7373
} else {
7474
let result =
7575
crate::policy::cleanup::perform_cleanup(snapshot).map_err(|e| CliError::ExecutionError(e))?;
76+
let blocked = !result.violations.is_empty() || !result.conflicts.is_empty();
77+
let success = !blocked && result.errors.is_empty();
78+
let mut warnings = result
79+
.violations
80+
.iter()
81+
.map(|v| {
82+
json!({
83+
"type": "violation",
84+
"target": v.target_path,
85+
"protected": v.protected_path,
86+
"reason": v.reason
87+
})
88+
})
89+
.collect::<Vec<_>>();
90+
let mut errors = result
91+
.conflicts
92+
.iter()
93+
.map(|c| {
94+
json!({
95+
"type": "conflict",
96+
"output": c.output_path,
97+
"protected": c.protected_path,
98+
"reason": c.reason
99+
})
100+
})
101+
.collect::<Vec<_>>();
102+
errors.extend(result.errors.iter().map(|e| {
103+
json!({
104+
"path": e.path,
105+
"kind": format!("{:?}", e.kind),
106+
"error": e.error
107+
})
108+
}));
109+
76110
Ok(MemorySyncCommandResult {
77-
success: result.errors.is_empty(),
111+
success,
78112
files_affected: result.deleted_files as i32,
79113
dirs_affected: result.deleted_dirs as i32,
80-
message: Some(format!(
81-
"Deleted {} files and {} directories",
82-
result.deleted_files, result.deleted_dirs
83-
)),
84-
warnings: Vec::new(),
85-
errors: result
86-
.errors
87-
.iter()
88-
.map(|e| {
89-
json!({
90-
"path": e.path,
91-
"kind": format!("{:?}", e.kind),
92-
"error": e.error
93-
})
94-
})
95-
.collect(),
114+
message: Some(if blocked {
115+
format!(
116+
"Cleanup blocked: {} conflicts, {} violations",
117+
result.conflicts.len(),
118+
result.violations.len()
119+
)
120+
} else {
121+
format!(
122+
"Deleted {} files and {} directories",
123+
result.deleted_files, result.deleted_dirs
124+
)
125+
}),
126+
warnings: std::mem::take(&mut warnings),
127+
errors,
96128
})
97129
}
98130
}

0 commit comments

Comments
 (0)