import io
p='crates/xtask/src/bundle.rs'
s=io.open(p,encoding='utf-8',newline='').read()
nl='\r\n' if '\r\n' in s else '\n'
def rep(old,new):
    global s
    old=old.replace('\n',nl); new=new.replace('\n',nl)
    assert s.count(old)==1,(s.count(old),old[:70]); s=s.replace(old,new)
rep('''/// Assemble the bundle from `members` into `out` (a `.tar.gz`), returning its
/// bytes. Refuses an empty member list and duplicate names.''','''/// Where `tar` runs and what it is told, for creating `out` from a staging
/// tree beside it. Every path argument is a BARE name relative to the returned
/// working directory, so no drive letter ever reaches tar: GNU tar (Git Bash's
/// `/usr/bin/tar`) reads `C:` as a remote host and fails, while bsdtar
/// (Windows' System32 `tar.exe`) refuses GNU's `--force-local`. Which one runs
/// depends on the launching shell, so neither flag is safe; relative paths are
/// (measured by doyle on hfenduleam, H2 gate, 2026-09-24).
pub fn plan_tar_create(out: &Path) -> (PathBuf, String, Vec<String>) {
    let dir = match out.parent() {
        Some(p) if !p.as_os_str().is_empty() => p.to_path_buf(),
        _ => PathBuf::from("."),
    };
    let file = out.file_name().expect("bundle out has a file name").to_string_lossy().into_owned();
    let tree = format!("{file}.tree");
    let args = vec!["-czf".to_string(), file, "-C".to_string(), tree.clone(), ".".to_string()];
    (dir, tree, args)
}

/// Assemble the bundle from `members` into `out` (a `.tar.gz`), returning its
/// bytes. Refuses an empty member list and duplicate names. The bytes are NOT
/// reproducible across tar implementations (GNU and bsdtar write different
/// headers for identical input), so the signed sha256 is always taken from the
/// bytes actually uploaded, never recomputed on another box.''')
rep('''    let tree = out.with_extension("tree");
    let _ = std::fs::remove_dir_all(&tree);''','''    let (dir, tree_name, tar_args) = plan_tar_create(out);
    std::fs::create_dir_all(&dir).expect("bundle out dir");
    let tree = dir.join(&tree_name);
    let _ = std::fs::remove_dir_all(&tree);''')
rep('''    if let Some(parent) = out.parent() {
        std::fs::create_dir_all(parent).expect("bundle out dir");
    }
    crate::run(
        Command::new("tar").arg("-czf").arg(out).arg("-C").arg(&tree).arg("."),
        "tar -czf the adapter bundle",
    );''','''    crate::run(
        Command::new("tar").current_dir(&dir).args(&tar_args),
        "tar -czf the adapter bundle",
    );''')
rep('''        crate::run(Command::new("tar").arg("-xzf").arg(&out).arg("-C").arg(&x), "untar bundle");''','''        // Relative again: extract from inside `x`, naming the bundle as `../<file>`.
        crate::run(
            Command::new("tar").current_dir(&x).arg("-xzf").arg(format!("../{BUNDLE_ASSET_NAME}")),
            "untar bundle",
        );''')
rep('''    #[test]
    fn assembled_bundle_round_trips_members_and_index() {''','''    // [unit->REQ-BUNDLE-RELEASE-ASSET] no tar argument ever carries a drive
    // letter, even for an absolute Windows out path: GNU tar reads `C:` as a
    // remote host, and bsdtar refuses GNU's `--force-local`, so relative names
    // under a working directory are the only shape both accept.
    #[test]
    fn tar_plan_never_passes_a_drive_letter() {
        let out = Path::new("C:/Users/m/AppData/Local/Temp/spt-release-v9/spt-bundled-adapters.tar.gz");
        let (dir, tree, args) = plan_tar_create(out);
        assert!(args.iter().all(|a| !a.contains(':')), "{args:?}");
        assert_eq!(args, ["-czf", "spt-bundled-adapters.tar.gz", "-C", "spt-bundled-adapters.tar.gz.tree", "."]);
        assert_eq!(tree, "spt-bundled-adapters.tar.gz.tree");
        assert_eq!(dir, out.parent().unwrap());
        let (dir, _, args) = plan_tar_create(Path::new("bundle.tar.gz"));
        assert_eq!(dir, PathBuf::from("."));
        assert_eq!(args[1], "bundle.tar.gz");
    }

    #[test]
    fn assembled_bundle_round_trips_members_and_index() {''')
io.open(p,'w',encoding='utf-8',newline='').write(s)
print('ok')
