xtask/cmd/semver_checks/
utils.rs1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4
5use anyhow::{Context, Result};
6use cargo_metadata::{Metadata, MetadataCommand};
7
8pub struct WorktreeCleanup {
9 path: PathBuf,
10}
11
12impl Drop for WorktreeCleanup {
13 fn drop(&mut self) {
14 println!("\n<details>");
16 println!("<summary> 🛬 Cleanup details 🛬 </summary>\n");
18 println!("Cleaning up git worktree at {:?}\n", self.path);
19 let status = Command::new("git")
20 .args(["worktree", "remove", "--force", self.path.to_str().unwrap()])
21 .status();
22
23 match status {
24 Ok(status) if status.success() => {
25 println!("Successfully removed git worktree");
26 }
27 Ok(status) => {
28 eprintln!("Failed to remove git worktree. Exit code: {status}");
29 }
30 Err(e) => {
31 eprintln!("Error removing git worktree: {e:?}");
32 }
33 }
34
35 println!("</details>");
36 }
37}
38
39pub fn metadata_from_dir(dir: impl AsRef<Path>) -> Result<Metadata> {
40 MetadataCommand::new()
41 .manifest_path(dir.as_ref().join("Cargo.toml"))
42 .exec()
43 .context("fetching cargo metadata from directory")
44}
45
46pub fn checkout_baseline(baseline_rev_or_hash: &str, target_dir: &PathBuf) -> Result<WorktreeCleanup> {
47 if target_dir.exists() {
48 std::fs::remove_dir_all(target_dir)?;
49 }
50
51 let rev_parse_output = Command::new("git")
53 .args(["rev-parse", "--verify", baseline_rev_or_hash])
54 .output()
55 .context("git rev-parse failed")?;
56
57 let commit_hash = if rev_parse_output.status.success() {
58 String::from_utf8(rev_parse_output.stdout)?.trim().to_string()
59 } else {
60 println!("Revision {baseline_rev_or_hash} not found locally. Fetching from origin...\n");
61
62 Command::new("git")
63 .args(["fetch", "--depth", "1", "origin", baseline_rev_or_hash])
64 .status()
65 .context("git fetch failed")?
66 .success()
67 .then_some(())
68 .context("git fetch unsuccessful")?;
69
70 let retry_output = Command::new("git")
72 .args(["rev-parse", "--verify", "FETCH_HEAD"])
73 .output()
74 .context("git rev-parse after fetch failed")?;
75
76 retry_output
77 .status
78 .success()
79 .then(|| String::from_utf8(retry_output.stdout).unwrap().trim().to_string())
80 .context(format!("Failed to resolve revision {baseline_rev_or_hash}"))?
81 };
82
83 println!("Checking out commit {commit_hash} into {target_dir:?}\n");
84
85 Command::new("git")
86 .args(["worktree", "add", "--detach", target_dir.to_str().unwrap(), &commit_hash])
87 .status()
88 .context("git worktree add failed")?
89 .success()
90 .then_some(())
91 .context("git worktree add unsuccessful")?;
92
93 Ok(WorktreeCleanup {
94 path: target_dir.clone(),
95 })
96}
97
98pub fn workspace_crates_in_folder(meta: &Metadata, folder: &str) -> HashSet<String> {
99 let folder_path = std::fs::canonicalize(folder).expect("folder should exist");
100
101 meta.packages
102 .iter()
103 .filter(|p| {
104 let manifest_path = p.manifest_path.parent().unwrap();
107 manifest_path.starts_with(&folder_path)
108 && p.publish.as_ref().map(|v| !v.is_empty()).unwrap_or(true)
109 && p.name != "scuffle-bootstrap-derive"
110 && p.name != "scuffle-metrics-derive"
111 })
112 .map(|p| p.name.clone())
113 .collect()
114}