cargo_release/steps/
commit.rs1use crate::config;
2use crate::error::CliError;
3use crate::ops::git;
4use crate::ops::replace::{NOW, Template};
5use crate::steps::plan;
6
7#[derive(Debug, Clone, clap::Args)]
11pub struct CommitStep {
12 #[command(flatten)]
13 manifest: clap_cargo::Manifest,
14
15 #[arg(short, long = "config", value_name = "PATH")]
17 custom_config: Option<std::path::PathBuf>,
18
19 #[arg(long)]
21 isolated: bool,
22
23 #[arg(short = 'Z', value_name = "FEATURE")]
25 z: Vec<config::UnstableValues>,
26
27 #[arg(long, value_delimiter = ',')]
29 allow_branch: Option<Vec<String>>,
30
31 #[arg(short = 'x', long)]
33 execute: bool,
34
35 #[arg(short = 'n', long, conflicts_with = "execute", hide = true)]
36 dry_run: bool,
37
38 #[arg(long)]
40 no_confirm: bool,
41
42 #[command(flatten)]
43 commit: config::CommitArgs,
44}
45
46impl CommitStep {
47 pub fn run(&self) -> Result<(), CliError> {
48 git::git_version()?;
49
50 if self.dry_run {
51 let _ =
52 crate::ops::shell::warn("`--dry-run` is superfluous, dry-run is done by default");
53 }
54
55 let ws_meta = self
56 .manifest
57 .metadata()
58 .features(cargo_metadata::CargoOpt::AllFeatures)
60 .exec()?;
61 let config = self.to_config();
62 let ws_config = config::load_workspace_config(&config, &ws_meta)?;
63 let pkgs = plan::load(&config, &ws_meta)?;
64
65 let pkgs = plan::plan(pkgs)?;
66
67 let (selected_pkgs, excluded_pkgs): (Vec<_>, Vec<_>) = pkgs
68 .into_iter()
69 .map(|(_, pkg)| pkg)
70 .partition(|p| p.config.release());
71 if git::is_dirty(ws_meta.workspace_root.as_std_path())?.is_none() {
72 let _ = crate::ops::shell::error("nothing to commit");
73 return Err(2.into());
74 }
75
76 let dry_run = !self.execute;
77 let mut failed = false;
78
79 failed |= !super::verify_git_branch(
81 ws_meta.workspace_root.as_std_path(),
82 &ws_config,
83 dry_run,
84 log::Level::Warn,
85 )?;
86
87 failed |= !super::verify_if_behind(
88 ws_meta.workspace_root.as_std_path(),
89 &ws_config,
90 dry_run,
91 log::Level::Warn,
92 )?;
93
94 super::confirm("Commit", &selected_pkgs, self.no_confirm, dry_run)?;
96
97 if ws_config.is_workspace {
98 let consolidate_commits = super::consolidate_commits(&selected_pkgs, &excluded_pkgs)?;
99 if !consolidate_commits {
100 let _ = crate::ops::shell::warn(
101 "ignoring `consolidate-commits=false`; `cargo release commit` can effectively only do one commit",
102 );
103 }
104 workspace_commit(&ws_meta, &ws_config, &selected_pkgs, dry_run)?;
105 } else if !selected_pkgs.is_empty() {
106 let selected_pkg = selected_pkgs
107 .first()
108 .expect("non-workspace can have at most 1 package");
109 pkg_commit(selected_pkg, dry_run)?;
110 }
111
112 super::finish(failed, dry_run)
113 }
114
115 fn to_config(&self) -> config::ConfigArgs {
116 config::ConfigArgs {
117 custom_config: self.custom_config.clone(),
118 isolated: self.isolated,
119 z: self.z.clone(),
120 allow_branch: self.allow_branch.clone(),
121 commit: self.commit.clone(),
122 ..Default::default()
123 }
124 }
125}
126
127pub fn pkg_commit(pkg: &plan::PackageRelease, dry_run: bool) -> Result<(), CliError> {
128 let cwd = &pkg.package_root;
129 let crate_name = pkg.meta.name.as_str();
130 let version = pkg.planned_version.as_ref().unwrap_or(&pkg.initial_version);
131 let prev_version_var = pkg.initial_version.bare_version_string.as_str();
132 let prev_metadata_var = pkg.initial_version.full_version.build.as_str();
133 let version_var = version.bare_version_string.as_str();
134 let metadata_var = version.full_version.build.as_str();
135 let template = Template {
136 prev_version: Some(prev_version_var),
137 prev_metadata: Some(prev_metadata_var),
138 version: Some(version_var),
139 metadata: Some(metadata_var),
140 crate_name: Some(crate_name),
141 date: Some(NOW.as_str()),
142 ..Default::default()
143 };
144 let commit_msg = template.render(pkg.config.pre_release_commit_message());
145 let sign = pkg.config.sign_commit();
146 if !git::commit_all(cwd, &commit_msg, sign, dry_run)? {
147 return Err(101.into());
149 }
150
151 Ok(())
152}
153
154pub fn workspace_commit(
155 ws_meta: &cargo_metadata::Metadata,
156 ws_config: &config::Config,
157 pkgs: &[plan::PackageRelease],
158 dry_run: bool,
159) -> Result<(), CliError> {
160 let shared_version = super::find_shared_versions(pkgs)?;
161
162 let shared_commit_msg = {
163 let version_var = shared_version
164 .as_ref()
165 .map(|v| v.bare_version_string.as_str());
166 let metadata_var = shared_version
167 .as_ref()
168 .map(|v| v.full_version.build.as_str());
169 let template = Template {
170 version: version_var,
171 metadata: metadata_var,
172 date: Some(NOW.as_str()),
173 ..Default::default()
174 };
175 template.render(ws_config.pre_release_commit_message())
176 };
177 if !git::commit_all(
178 ws_meta.workspace_root.as_std_path(),
179 &shared_commit_msg,
180 ws_config.sign_commit(),
181 dry_run,
182 )? {
183 return Err(101.into());
185 }
186
187 Ok(())
188}