Skip to content

Commit 28b926b

Browse files
committed
fix: normalize archive entry names and manifest paths to forward slashes
On Windows, PathBuf joins use backslashes, which leaked into tar archive entry names and the manifest import_paths field. This caused test failures on windows-latest CI and would break cross-platform package extraction. Reuse the existing normalize_path() helper for all archive entry names (app files, module files) and manifest import_path strings so they always use forward slashes regardless of the host OS.
1 parent 387f454 commit 28b926b

2 files changed

Lines changed: 20 additions & 27 deletions

File tree

crates/tower-package/src/lib.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,15 @@ impl Package {
272272
for (physical_path, logical_path) in file_paths {
273273
// All of the app code goes into the "app" directory.
274274
let logical_path = app_dir.join(logical_path);
275+
// Normalize to forward slashes so archive entry names are POSIX-compatible
276+
// on all platforms (Windows PathBuf uses backslashes).
277+
let archive_name = normalize_path(&logical_path)?;
275278

276279
let hash = compute_sha256_file(&physical_path).await?;
277-
path_hashes.insert(logical_path.clone(), hash);
280+
path_hashes.insert(PathBuf::from(&archive_name), hash);
278281

279282
builder
280-
.append_path_with_name(physical_path, logical_path)
283+
.append_path_with_name(physical_path, &archive_name)
281284
.await?;
282285
}
283286

@@ -301,7 +304,8 @@ impl Package {
301304
// The file_name should constitute the logical path
302305
let import_path = import_path.file_name().unwrap();
303306
let import_path = module_dir.join(import_path);
304-
let import_path_str = import_path.into_os_string().into_string().unwrap();
307+
// Normalize to forward slashes for the manifest (POSIX, cross-platform).
308+
let import_path_str = normalize_path(&import_path)?;
305309
import_paths.push(import_path_str);
306310

307311
// Now we write all of these paths to the modules directory.
@@ -310,13 +314,16 @@ impl Package {
310314
Ok(p) => module_dir.join(p),
311315
Err(_) => continue,
312316
};
317+
// Normalize to forward slashes so archive entry names are POSIX-compatible
318+
// on all platforms (Windows PathBuf uses backslashes).
319+
let archive_name = normalize_path(&logical_path)?;
313320

314321
let hash = compute_sha256_file(&physical_path).await?;
315-
path_hashes.insert(logical_path.clone(), hash);
322+
path_hashes.insert(PathBuf::from(&archive_name), hash);
316323

317324
debug!("adding file {}", logical_path.display());
318325
builder
319-
.append_path_with_name(physical_path, logical_path)
326+
.append_path_with_name(physical_path, &archive_name)
320327
.await?;
321328
}
322329
}

crates/tower-package/tests/package_test.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,7 @@ use tokio_tar::Archive;
1515
use tower_package::{Manifest, Package, PackageSpec, Parameter};
1616
use tower_telemetry::debug;
1717

18-
macro_rules! make_path {
19-
($($component:expr),+ $(,)?) => {
20-
{
21-
let mut path = PathBuf::new();
22-
$(
23-
path.push($component);
24-
)+
25-
&path.to_string_lossy().to_string()
26-
}
27-
};
28-
}
18+
2919

3020
#[tokio::test]
3121
async fn it_creates_package() {
@@ -289,12 +279,9 @@ async fn it_packages_import_paths() {
289279
.await
290280
.expect("Manifest was not valid JSON");
291281

292-
// NOTE: These paths are joined by the OS so we need to be more specific about the expected
293-
// path.
282+
// Archive paths are always normalized to forward slashes regardless of OS.
294283
assert!(
295-
manifest
296-
.import_paths
297-
.contains(make_path!("modules", "shared")),
284+
manifest.import_paths.contains(&"modules/shared".to_string()),
298285
"Import paths {:?} did not contain expected path",
299286
manifest.import_paths
300287
);
@@ -333,18 +320,19 @@ async fn it_packages_import_paths_nested_within_base_dir() {
333320
let files = read_package_files(package).await;
334321

335322
// Module files should be under modules/shared/..., NOT modules/libs/shared/...
323+
// Archive paths are always normalized to forward slashes regardless of OS.
336324
assert!(
337-
files.contains_key(make_path!("modules", "shared", "__init__.py")),
325+
files.contains_key("modules/shared/__init__.py"),
338326
"files {:?} was missing modules/shared/__init__.py",
339327
files
340328
);
341329
assert!(
342-
files.contains_key(make_path!("modules", "shared", "util.py")),
330+
files.contains_key("modules/shared/util.py"),
343331
"files {:?} was missing modules/shared/util.py",
344332
files
345333
);
346334
assert!(
347-
!files.contains_key(make_path!("modules", "libs", "shared", "__init__.py")),
335+
!files.contains_key("modules/libs/shared/__init__.py"),
348336
"files {:?} should NOT contain modules/libs/shared/__init__.py",
349337
files
350338
);
@@ -355,9 +343,7 @@ async fn it_packages_import_paths_nested_within_base_dir() {
355343
.expect("Manifest was not valid JSON");
356344

357345
assert!(
358-
manifest
359-
.import_paths
360-
.contains(make_path!("modules", "shared")),
346+
manifest.import_paths.contains(&"modules/shared".to_string()),
361347
"Import paths {:?} did not contain expected path modules/shared",
362348
manifest.import_paths
363349
);

0 commit comments

Comments
 (0)