crate_seq_core/pipeline/
dry_run.rs1use std::path::{Path, PathBuf};
4use std::process::Command;
5
6use crate_seq_ledger::{detect_tag_pattern, load};
7
8use crate::pipeline::source::resolve_source;
9use crate::validate::{run_cargo_check, validate_no_path_deps};
10use crate::Error;
11
12#[derive(Debug)]
14pub enum DryRunOutcome {
15 Pass,
17 PackageFailed(String),
19 PathDepsFound(Vec<String>),
21 CargoCheckFailed(String),
23}
24
25#[derive(Debug)]
27pub struct DryRunVersionResult {
28 pub version: semver::Version,
30 pub tag_ref: String,
32 pub outcome: DryRunOutcome,
34}
35
36#[derive(Debug)]
38pub struct DryRunReport {
39 pub crate_name: String,
41 pub results: Vec<DryRunVersionResult>,
43}
44
45fn resolve_tag_pattern(settings: &crate_seq_ledger::LedgerSettings, crate_name: &str) -> String {
47 settings
48 .tag_pattern
49 .clone()
50 .unwrap_or_else(|| detect_tag_pattern(crate_name, false))
51}
52
53fn run_cargo_package(manifest_path: &Path) -> Result<DryRunOutcome, Error> {
57 let output = Command::new("cargo")
58 .args(["package", "--allow-dirty", "--manifest-path"])
59 .arg(manifest_path)
60 .output()
61 .map_err(|e| Error::Subprocess(e.to_string()))?;
62
63 if output.status.success() {
64 Ok(DryRunOutcome::Pass)
65 } else {
66 let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
67 Ok(DryRunOutcome::PackageFailed(stderr))
68 }
69}
70
71fn run_validations(manifest_path: &Path) -> Result<Option<DryRunOutcome>, Error> {
75 match validate_no_path_deps(manifest_path) {
76 Ok(()) => {}
77 Err(Error::PathDependencies { deps, .. }) => {
78 return Ok(Some(DryRunOutcome::PathDepsFound(deps)));
79 }
80 Err(e) => return Err(e),
81 }
82
83 match run_cargo_check(manifest_path) {
84 Ok(()) => {}
85 Err(Error::CargoCheck { stderr }) => {
86 return Ok(Some(DryRunOutcome::CargoCheckFailed(stderr)));
87 }
88 Err(e) => return Err(e),
89 }
90
91 Ok(None)
92}
93
94fn process_entry(
96 entry: &crate_seq_ledger::LedgerEntry,
97 repo_path: &Path,
98 crate_rel_path: &Path,
99 snapshot_store: &Path,
100) -> Result<DryRunVersionResult, Error> {
101 let tag_ref = entry.ref_.clone();
102 let checkout = resolve_source(entry, repo_path, snapshot_store)?;
103 let manifest_path = checkout.path().join(crate_rel_path).join("Cargo.toml");
104
105 if let Some(failed_outcome) = run_validations(&manifest_path)? {
106 return Ok(DryRunVersionResult {
107 version: entry.version.clone(),
108 tag_ref,
109 outcome: failed_outcome,
110 });
111 }
112
113 crate_seq_manifest::rewrite_version(&manifest_path, &entry.version)?;
114 let outcome = run_cargo_package(&manifest_path)?;
115
116 Ok(DryRunVersionResult {
117 version: entry.version.clone(),
118 tag_ref,
119 outcome,
120 })
121}
122
123fn default_snapshot_store(ledger_path: &Path) -> PathBuf {
125 ledger_path
126 .parent()
127 .unwrap_or_else(|| Path::new("."))
128 .join(".crate-seq-snapshots")
129}
130
131fn crate_rel_path(ledger_path: &Path, repo_path: &Path) -> PathBuf {
136 let crate_dir = ledger_path.parent().unwrap_or(repo_path);
137 crate_dir
138 .strip_prefix(repo_path)
139 .unwrap_or(std::path::Path::new(""))
140 .to_owned()
141}
142
143pub fn publish_dry_run(
156 ledger_path: &Path,
157 repo_path: &Path,
158 snapshot_store: Option<PathBuf>,
159) -> Result<DryRunReport, Error> {
160 let ledger = load(ledger_path)?;
161 let crate_name = ledger.crate_config.name.clone();
162 let _tag_pattern = resolve_tag_pattern(&ledger.settings, &crate_name);
163
164 let store = snapshot_store.unwrap_or_else(|| default_snapshot_store(ledger_path));
165 let rel_path = crate_rel_path(ledger_path, repo_path);
166
167 let pending: Vec<crate_seq_ledger::LedgerEntry> =
168 ledger.pending_versions().into_iter().cloned().collect();
169
170 let mut results = Vec::with_capacity(pending.len());
171 for entry in &pending {
172 results.push(process_entry(entry, repo_path, &rel_path, &store)?);
173 }
174
175 Ok(DryRunReport {
176 crate_name,
177 results,
178 })
179}