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