1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
use crate::plan::BumpKind;
use clap::{ArgAction, Parser};
use std::{
io::{stderr, stdout, IsTerminal},
path::PathBuf,
};
use termcolor::{ColorChoice, StandardStream};
fn color(s: &str) -> Result<ColorChoice, &'static str> {
match s {
"always" => Ok(ColorChoice::Always),
"never" => Ok(ColorChoice::Never),
"auto" if stdout().is_terminal() && stderr().is_terminal() => Ok(ColorChoice::Auto),
"auto" => Ok(ColorChoice::Never),
_ => Err("invalid value"),
}
}
#[derive(Parser, Debug)]
pub struct Args {
#[arg(long, short = 'C')]
pub chdir: Option<PathBuf>,
#[arg(long, value_parser = color, default_value = "auto")]
pub color: ColorChoice,
#[arg(long)]
pub debug: bool,
}
impl Args {
pub fn stdout(&self) -> StandardStream {
StandardStream::stdout(self.color)
}
pub fn stderr(&self) -> StandardStream {
StandardStream::stderr(self.color)
}
}
/// A tool to help with publishing crates
#[derive(Parser, Debug)]
#[command(author, version, about)]
pub struct Cli {
#[command(flatten)]
pub args: Args,
#[command(subcommand)]
pub comamnd: Command,
}
#[derive(Parser, Debug)]
pub enum Command {
/// Checkes the status of the crates in a given workspace path against crates.io
Status(Status),
/// Claim ownership of unpublished crates on crates.io
Claim(Claim),
/// Find crates that have major or minor semver changes
Semver(Semver),
/// Find crates marked as changed in prdoc
Prdoc(Prdoc),
/// Find what crates have changed since last crates.io release
Changed(Changed),
/// Plan a publish
Plan(Plan),
/// Execute a publish
Apply(Apply),
/// Check crates are okay to publish
Check(Check),
/// Manage Plan.config
Config(Config),
/// Query a workspace
Workspace(Workspace),
/// Restore clean manifests after publishing, keeping only version bumps
Restore(Restore),
}
#[derive(Parser, Debug)]
pub struct Status {
/// Filter to only crates that are not on crates.io
#[arg(long, short)]
pub missing: bool,
#[arg(long, short)]
/// Filter to only crates that are not owned by parity on crates.io
pub external: bool,
#[arg(long, short)]
/// Filter to only crates that do not match the version on crates.io
pub version: bool,
#[arg(long, short)]
/// Only print crate names
pub quiet: bool,
}
#[derive(Parser, Debug)]
pub struct Claim {
/// Don't actually claim crates
#[arg(long, short)]
pub dry_run: bool,
}
#[derive(Parser, Debug)]
pub struct Workspace {
/// Just print paths, pass twice to print manifests
#[arg(long, short, action = ArgAction::Count)]
pub paths: u8,
/// Only print crate names
#[arg(long, short)]
pub quiet: bool,
/// Print packages that own given files
#[arg(long, short)]
pub owns: bool,
/// targets to act on
#[arg(default_values_t = Vec::<String>::new())]
pub targets: Vec<String>,
}
#[derive(Parser, Debug)]
pub struct Semver {
/// Just print paths, pass twice to print manifests
#[arg(long, short, action = ArgAction::Count)]
pub paths: u8,
/// Only print crate names
#[arg(long, short)]
pub quiet: bool,
/// Only print breaking changes
#[arg(long, short)]
pub major: bool,
/// Verbose output
#[arg(long, short)]
pub verbose: bool,
/// Old version to compare against
#[arg(long)]
pub since: Option<String>,
/// Rust toolchain to use
#[arg(long, default_value = public_api::MINIMUM_NIGHTLY_RUST_VERSION)]
pub toolchain: String,
/// Cargo build target dir
#[arg(long, default_value = "target")]
pub target_dir: PathBuf,
/// Print the minimum nightly rust version needed for semver checks
#[arg(long)]
pub minimum_nightly_rust_version: bool,
/// Crates to check
#[arg(default_values_t = Vec::<String>::new())]
pub crates: Vec<String>,
}
#[derive(Parser, Debug)]
pub struct Prdoc {
/// Don't include packages that have has a dependency change
#[arg(long, short = 'd')]
pub no_deps: bool,
/// Just print paths, pass twice to print manifests
#[arg(long, short, action = ArgAction::Count)]
pub paths: u8,
/// Only print breaking changes
#[arg(long, short)]
pub major: bool,
/// Verbose output, also shows the output of rust doc builder
#[arg(long, short)]
pub verbose: bool,
/// Only print crate names
#[arg(long, short)]
pub quiet: bool,
/// Validate crate changes specified in prdocs
#[arg(long)]
pub since: Option<String>,
/// Validate crate changes specified in prdocs
#[arg(long)]
pub validate: bool,
/// Path to prdoc dir
pub prdoc_path: PathBuf,
/// Limit output to specified crates
/// Rust toolchain to use
#[arg(long, default_value = public_api::MINIMUM_NIGHTLY_RUST_VERSION)]
pub toolchain: String,
/// Cargo build target dir
#[arg(long, default_value = "target")]
pub target_dir: PathBuf,
#[arg(default_values_t = Vec::<String>::new())]
pub crates: Vec<String>,
/// The maximum bump that is allowed for any crate to happen. Only checked if `validate` is set.
#[arg(long, value_enum)]
pub max_bump: Option<BumpKind>,
}
#[derive(Parser, Debug)]
pub struct Changed {
/// Just print paths, pass twice to print manifests
#[arg(long, short, action = ArgAction::Count)]
pub paths: u8,
/// Only print crate names
#[arg(long, short)]
pub quiet: bool,
/// Verbose output
#[arg(long, short)]
pub verbose: bool,
/// Don't include packages that have has a dependency change
#[arg(long, short = 'd')]
pub no_deps: bool,
/// Only show packages where the manifest changed
#[arg(long, short)]
pub manifests: bool,
/// The git commit to look for changes from
pub from: String,
/// The git commit to look for changes to
#[arg(default_value = "HEAD")]
pub to: String,
}
#[derive(Parser, Debug)]
pub struct Plan {
/// Suffix crate descriptions with given string
#[arg(long, short)]
pub description: Option<String>,
/// add a pre release part to the published version
#[arg(long, short)]
pub pre: Option<String>,
/// publish all crates
#[arg(long, short)]
pub all: bool,
/// Publish crates that have changed since git ref
#[arg(long)]
pub since: Option<String>,
#[arg(long)]
/// Calculate changes from prdocs
pub prdoc: Option<PathBuf>,
/// don't verify before publishing
#[arg(long)]
pub no_verify: bool,
/// Create a new plan even if one exists
#[arg(long)]
pub new: bool,
/// Don't run check during plan
#[arg(long)]
pub skip_check: bool,
/// Patch bump the specified crates
#[arg(long)]
pub patch: bool,
/// Print expanded plan
#[arg(long)]
pub print_expanded: bool,
/// Don't bump versions when generating plan
#[arg(long)]
pub hold_version: bool,
pub crates: Vec<String>,
}
#[derive(Parser, Debug)]
pub struct Apply {
/// Don't actually publish crates
#[arg(long, short)]
pub dry_run: bool,
/// Publish the crates
#[arg(long, short)]
pub publish: bool,
/// Allow dirty working directories to be published
#[arg(long)]
pub allow_dirty: bool,
/// Don't verify packages before publish
#[arg(long)]
pub no_verify: bool,
/// Use registry for dependencies instead of local paths
#[arg(long)]
pub registry: bool,
/// Print packages that need publish
#[arg(long)]
pub print: bool,
/// Number of crates to publish in parallel
#[arg(long, short = 'j', default_value = "1")]
pub jobs: usize,
/// Publish to staging.crates.io instead of crates.io
#[arg(long)]
pub staging: bool,
/// Run `cargo clean` after every N published crates to free disk space (0 = disabled)
#[arg(long, default_value = "0")]
pub clean_every: usize,
}
#[derive(Parser, Debug)]
pub struct Restore {
/// Git ref to restore Cargo.toml files from (default: HEAD~1)
#[arg(long, default_value = "HEAD~1")]
pub from: String,
/// Don't restore, just print what would be done
#[arg(long)]
pub dry_run: bool,
}
#[derive(Parser, Debug)]
pub struct Check {
/// Dont exit 1 on errors that don't prevent publish
#[arg(long)]
pub allow_nonfatal: bool,
#[arg(long, short)]
/// Only print crate names
pub quiet: bool,
#[arg(long, short, action = ArgAction::Count)]
/// just print paths, pass twice to print manifests
pub paths: u8,
#[arg(long)]
/// Dont check ownership status
pub no_check_owner: bool,
#[arg(long)]
/// Dont exit 1 when crate is unpublished
pub allow_unpublished: bool,
#[arg(long, short)]
/// recursively find what crates depend on unpublished crates
pub recursive: bool,
}
#[derive(Parser, Debug)]
pub struct Config {
#[arg(long)]
/// Apply changes specified in Plan.config
pub apply: bool,
}