| 315 | 315 | | let fetched = self.fetch_to(artifact, &file).and_then(|format| { |
| 316 | 316 | | if format == "tgz" { |
| 317 | 317 | | std::fs::create_dir_all(into).map_err(|e| e.to_string())?; |
| 318 | | − | let script = format!("tar -xzf {} -C {}", quote(&file.display().to_string()), quote(&into.display().to_string())); |
| 318 | + | let script = format!("tar{} -xzf {} -C {}", tar_local(), quote(&file.display().to_string()), quote(&into.display().to_string())); |
| 319 | 319 | | if self.shell(&script) { Ok(()) } else { Err("it could not be unpacked".into()) } |
| 320 | 320 | | } else { |
| 321 | 321 | | zip::extract(&file, into).map(|_| ()).map_err(|e| format!("it could not be unpacked: {e}")) |
| ⋯ |
| 558 | 558 | | let lookup_only = with.get("lookup-only").is_some_and(|v| v == "true"); |
| 559 | 559 | | let size = std::fs::metadata(&archive).map(|m| m.len()).unwrap_or(0); |
| 560 | 560 | | // tar finds out from the archive whether it is zstd or gzip. |
| 561 | | − | if !lookup_only && !self.shell(&format!("tar -xPf {}", quote(&archive.display().to_string()))) { |
| 561 | + | if !lookup_only && !self.shell(&format!("tar{} -xPf {}", tar_local(), quote(&archive.display().to_string()))) { |
| 562 | 562 | | self.log.line("##[warning]The cache was found but could not be unpacked."); |
| 563 | 563 | | } |
| 564 | 564 | | let _ = std::fs::remove_file(&archive); |
| ⋯ |
| 896 | 896 | | for f in \"${{found[@]}}\"; do if [ -e \"$f\" ]; then files+=(\"$f\"); fi; done; \ |
| 897 | 897 | | if [ ${{#files[@]}} -eq 0 ]; then exit 3; fi; \ |
| 898 | 898 | | if command -v zstd >/dev/null; then compress='zstd -T0 -3'; else compress=gzip; fi; \ |
| 899 | | − | tar -cPf {} -I \"$compress\" {} -- \"${{files[@]}}\"", |
| 899 | + | tar{} -cPf {} -I \"$compress\" {} -- \"${{files[@]}}\"", |
| 900 | 900 | | includes.join(" "), |
| 901 | + | tar_local(), |
| 901 | 902 | | quote(archive), |
| 902 | 903 | | excludes.join(" ") |
| 903 | 904 | | ) |
| 904 | 905 | | } |
| 905 | 906 | | |
| 907 | + | /// GNU tar on Windows reads `C:...` as a host and a file; `--force-local` |
| 908 | + | /// makes every path a local one. Elsewhere the flag is left off, since |
| 909 | + | /// bsdtar (macOS) doesn't know it. |
| 910 | + | pub(crate) fn tar_local() -> &'static str { |
| 911 | + | if cfg!(windows) { |
| 912 | + | " --force-local" |
| 913 | + | } else { |
| 914 | + | "" |
| 915 | + | } |
| 916 | + | } |
| 917 | + | |
| 906 | 918 | | fn urlencode(text: &str) -> String { |
| 907 | 919 | | let mut out = String::new(); |
| 908 | 920 | | for byte in text.bytes() { |
| ⋯ |
| 940 | 952 | | } |
| 941 | 953 | | |
| 942 | 954 | | #[test] |
| 955 | + | fn packing_keeps_windows_paths_local() { |
| 956 | + | let script = pack_script(&["C:/w/target".into()], "C:/t/c.tar"); |
| 957 | + | assert_eq!(script.contains("--force-local"), cfg!(windows), "{script}"); |
| 958 | + | } |
| 959 | + | |
| 960 | + | #[test] |
| 943 | 961 | | fn packing_expands_globs_and_leaves_exclusions_out() { |
| 944 | 962 | | let script = pack_script(&["/w/target/*/release".into(), "!/w/target/**/incremental".into()], "/t/c.tar"); |
| 945 | 963 | | assert!(script.contains("found=( /w/target/*/release )"), "{script}"); |