cargo_release/steps/
replace.rs1use crate::error::CliError;
2use crate::ops::git;
3use crate::ops::replace::{NOW, Template, do_file_replacements};
4use crate::steps::plan;
5
6#[derive(Debug, Clone, clap::Args)]
8pub struct ReplaceStep {
9 #[command(flatten)]
10 manifest: clap_cargo::Manifest,
11
12 #[command(flatten)]
13 workspace: clap_cargo::Workspace,
14
15 #[arg(long)]
17 unpublished: bool,
18
19 #[arg(short, long = "config")]
21 custom_config: Option<std::path::PathBuf>,
22
23 #[arg(long)]
25 isolated: bool,
26
27 #[arg(short = 'Z', value_name = "FEATURE")]
29 z: Vec<crate::config::UnstableValues>,
30
31 #[arg(long, value_delimiter = ',')]
33 allow_branch: Option<Vec<String>>,
34
35 #[arg(short = 'x', long)]
37 execute: bool,
38
39 #[arg(short = 'n', long, conflicts_with = "execute", hide = true)]
40 dry_run: bool,
41
42 #[arg(long)]
44 no_confirm: bool,
45}
46
47impl ReplaceStep {
48 pub fn run(&self) -> Result<(), CliError> {
49 git::git_version()?;
50 let mut index = crate::ops::index::CratesIoIndex::new();
51
52 if self.dry_run {
53 let _ =
54 crate::ops::shell::warn("`--dry-run` is superfluous, dry-run is done by default");
55 }
56
57 let ws_meta = self
58 .manifest
59 .metadata()
60 .features(cargo_metadata::CargoOpt::AllFeatures)
62 .exec()?;
63 let config = self.to_config();
64 let ws_config = crate::config::load_workspace_config(&config, &ws_meta)?;
65 let mut pkgs = plan::load(&config, &ws_meta)?;
66
67 let (_selected_pkgs, excluded_pkgs) =
68 if self.unpublished && self.workspace == clap_cargo::Workspace::default() {
69 ws_meta.packages.iter().partition(|_| false)
70 } else {
71 self.workspace.partition_packages(&ws_meta)
72 };
73 for excluded_pkg in excluded_pkgs {
74 let Some(pkg) = pkgs.get_mut(&excluded_pkg.id) else {
75 continue;
77 };
78 if !pkg.config.release() {
79 continue;
80 }
81
82 let crate_name = pkg.meta.name.as_str();
83 let explicitly_excluded = self.workspace.exclude.contains(&excluded_pkg.name);
84 if pkg.config.release()
87 && pkg.config.publish()
88 && self.unpublished
89 && !explicitly_excluded
90 {
91 let version = &pkg.initial_version;
92 if !crate::ops::cargo::is_published(
93 &mut index,
94 pkg.config.registry(),
95 crate_name,
96 &version.full_version_string,
97 pkg.config.certs_source(),
98 ) {
99 log::debug!(
100 "enabled {}, v{} is unpublished",
101 crate_name,
102 version.full_version_string
103 );
104 continue;
105 }
106 }
107
108 pkg.config.pre_release_replacements = Some(vec![]);
109 pkg.config.release = Some(false);
110 }
111
112 let pkgs = plan::plan(pkgs)?;
113
114 let (selected_pkgs, _excluded_pkgs): (Vec<_>, Vec<_>) = pkgs
115 .into_iter()
116 .map(|(_, pkg)| pkg)
117 .partition(|p| p.config.release());
118 if selected_pkgs.is_empty() {
119 let _ = crate::ops::shell::error("no packages selected");
120 return Err(2.into());
121 }
122
123 let dry_run = !self.execute;
124 let mut failed = false;
125
126 failed |= !super::verify_git_is_clean(
128 ws_meta.workspace_root.as_std_path(),
129 dry_run,
130 log::Level::Warn,
131 )?;
132
133 super::warn_changed(&ws_meta, &selected_pkgs)?;
134
135 failed |= !super::verify_git_branch(
136 ws_meta.workspace_root.as_std_path(),
137 &ws_config,
138 dry_run,
139 log::Level::Warn,
140 )?;
141
142 failed |= !super::verify_if_behind(
143 ws_meta.workspace_root.as_std_path(),
144 &ws_config,
145 dry_run,
146 log::Level::Warn,
147 )?;
148
149 super::confirm("Bump", &selected_pkgs, self.no_confirm, dry_run)?;
151
152 for pkg in &selected_pkgs {
154 replace(pkg, dry_run)?;
155 }
156
157 super::finish(failed, dry_run)
158 }
159
160 fn to_config(&self) -> crate::config::ConfigArgs {
161 crate::config::ConfigArgs {
162 custom_config: self.custom_config.clone(),
163 isolated: self.isolated,
164 z: self.z.clone(),
165 allow_branch: self.allow_branch.clone(),
166 ..Default::default()
167 }
168 }
169}
170
171pub fn replace(pkg: &plan::PackageRelease, dry_run: bool) -> Result<(), CliError> {
172 let version = pkg.planned_version.as_ref().unwrap_or(&pkg.initial_version);
173 if !pkg.config.pre_release_replacements().is_empty() {
174 let cwd = &pkg.package_root;
175 let crate_name = pkg.meta.name.as_str();
176 let prev_version_var = pkg.initial_version.bare_version_string.as_str();
177 let prev_metadata_var = pkg.initial_version.full_version.build.as_str();
178 let version_var = version.bare_version_string.as_str();
179 let metadata_var = version.full_version.build.as_str();
180 let template = Template {
182 prev_version: Some(prev_version_var),
183 prev_metadata: Some(prev_metadata_var),
184 version: Some(version_var),
185 metadata: Some(metadata_var),
186 crate_name: Some(crate_name),
187 date: Some(NOW.as_str()),
188 tag_name: pkg.planned_tag.as_deref(),
189 ..Default::default()
190 };
191 let prerelease = version.is_prerelease();
192 let noisy = true;
193 do_file_replacements(
194 pkg.config.pre_release_replacements(),
195 &template,
196 cwd,
197 prerelease,
198 noisy,
199 dry_run,
200 )?;
201 }
202
203 Ok(())
204}