Skip to content

Commit 6538501

Browse files
committed
feat: support multiple starting points
1 parent 22c4cc9 commit 6538501

File tree

2 files changed

+139
-96
lines changed

2 files changed

+139
-96
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main.rs

Lines changed: 138 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{BTreeMap, HashMap};
1+
use std::collections::{BTreeMap, HashMap, HashSet};
22
use std::fs;
33
use std::path::{Path, PathBuf};
44
use std::process::Command as ProcessCommand;
@@ -56,14 +56,15 @@ enum Command {
5656
#[arg(value_name = "CRATE")]
5757
crate_name: String,
5858
},
59-
/// Prepare to release a crate and all dependents that needs updating
59+
/// Prepare to release crates and all dependents that needs updating
6060
/// - Semver checks
6161
/// - Bump versions and commit
6262
/// - Create tag.
6363
PrepareRelease {
64-
/// Crate to release. Will traverse that crate an it's dependents. If not specified checks all crates.
64+
/// Crates to release. Will traverse that crate an it's dependents. If not specified checks all crates.
65+
/// Crates specified in this list must be diseparate in the dependency tree
6566
#[arg(value_name = "CRATE")]
66-
crate_name: String,
67+
crate_names: Vec<String>,
6768
},
6869
}
6970

@@ -345,117 +346,159 @@ fn main() -> Result<()> {
345346
}
346347
check_semver(ctx.root.clone(), c)?;
347348
}
348-
Command::PrepareRelease { crate_name } => {
349-
let start = ctx
350-
.indices
351-
.get(&crate_name)
352-
.expect("unable to find crate in tree");
353-
354-
// Check if the target crate is publishable
355-
let start_weight = ctx.graph.node_weight(*start).unwrap();
356-
let start_crate = ctx.crates.get(start_weight).unwrap();
357-
if !start_crate.publish {
358-
bail!(
359-
"Cannot prepare release for non-publishable crate '{}'",
360-
crate_name
361-
);
349+
Command::PrepareRelease { crate_names } => {
350+
// Check if the target crates are publishable
351+
for crate_name in &crate_names {
352+
let start = ctx
353+
.indices
354+
.get(crate_name)
355+
.expect("unable to find crate in tree");
356+
let start_weight = ctx.graph.node_weight(*start).unwrap();
357+
let start_crate = ctx.crates.get(start_weight).unwrap();
358+
if !start_crate.publish {
359+
bail!(
360+
"Cannot prepare release for non-publishable crate '{}'",
361+
crate_name
362+
);
363+
}
362364
}
363365

364366
let mut rgraph = ctx.graph.clone();
365367
rgraph.reverse();
366368

367-
let mut bfs = Bfs::new(&rgraph, *start);
368-
369-
while let Some(node) = bfs.next(&rgraph) {
370-
let weight = rgraph.node_weight(node).unwrap();
371-
println!("Preparing {weight}");
372-
let c = ctx.crates.get_mut(weight).unwrap();
373-
if c.publish {
374-
let ver = semver::Version::parse(&c.version)?;
375-
let newver = match check_semver(ctx.root.clone(), c)? {
376-
ReleaseType::Major | ReleaseType::Minor => {
377-
semver::Version::new(ver.major, ver.minor + 1, 0)
378-
}
379-
ReleaseType::Patch => {
380-
semver::Version::new(ver.major, ver.minor, ver.patch + 1)
369+
let mut bumped = HashMap::new();
370+
for crate_name in &crate_names {
371+
if !bumped.contains_key(crate_name) {
372+
let start = ctx
373+
.indices
374+
.get(crate_name)
375+
.expect("unable to find crate in tree");
376+
377+
let mut bfs = Bfs::new(&rgraph, *start);
378+
while let Some(node) = bfs.next(&rgraph) {
379+
let weight = rgraph.node_weight(node).unwrap();
380+
println!("Preparing {weight}");
381+
let c = ctx.crates.get_mut(weight).unwrap();
382+
if c.publish {
383+
let ver = semver::Version::parse(&c.version)?;
384+
let (rtype, newver) = match check_semver(ctx.root.clone(), c)? {
385+
ReleaseType::Major | ReleaseType::Minor => (
386+
ReleaseType::Minor,
387+
semver::Version::new(ver.major, ver.minor + 1, 0),
388+
),
389+
ReleaseType::Patch => (
390+
ReleaseType::Patch,
391+
semver::Version::new(ver.major, ver.minor, ver.patch + 1),
392+
),
393+
_ => unreachable!(),
394+
};
395+
396+
let oldver = c.version.clone();
397+
println!("Updating {} from {} -> {}", weight, c.version, newver);
398+
let newver = newver.to_string();
399+
400+
match bumped.get(&c.name) {
401+
// We already bumped the minor version, don't do it again.
402+
Some(ReleaseType::Minor) => {
403+
println!("Minor version already bumped, skipping");
404+
}
405+
// No reason to bump patch twice
406+
Some(ReleaseType::Patch) if rtype == ReleaseType::Patch => {
407+
println!("Patch version already bumped, skipping");
408+
}
409+
// Do the bump as required
410+
_ => {
411+
bumped.insert(c.name.clone(), rtype);
412+
update_version(c, &newver)?;
413+
let c = ctx.crates.get(weight).unwrap();
414+
415+
// Update all nodes further down the tree
416+
let mut bfs = Bfs::new(&rgraph, node);
417+
while let Some(dep_node) = bfs.next(&rgraph) {
418+
let dep_weight = rgraph.node_weight(dep_node).unwrap();
419+
println!(
420+
"Updating {}-{} -> {} for {}",
421+
c.name, oldver, newver, dep_weight
422+
);
423+
let dep = ctx.crates.get(dep_weight).unwrap();
424+
update_versions(dep, &c.name, &newver)?;
425+
}
426+
427+
// Update changelog
428+
update_changelog(&ctx.root, c)?;
429+
}
430+
}
381431
}
382-
_ => unreachable!(),
383-
};
384-
385-
println!("Updating {} from {} -> {}", weight, c.version, newver);
386-
let newver = newver.to_string();
387-
388-
update_version(c, &newver)?;
389-
let c = ctx.crates.get(weight).unwrap();
390-
391-
// Update all nodes further down the tree
392-
let mut bfs = Bfs::new(&rgraph, node);
393-
while let Some(dep_node) = bfs.next(&rgraph) {
394-
let dep_weight = rgraph.node_weight(dep_node).unwrap();
395-
let dep = ctx.crates.get(dep_weight).unwrap();
396-
update_versions(dep, &c.name, &newver)?;
397432
}
398-
399-
// Update changelog
400-
update_changelog(&ctx.root, c)?;
401433
}
402434
}
403435

404-
let weight = rgraph.node_weight(*start).unwrap();
405-
let c = ctx.crates.get(weight).unwrap();
406-
publish_release(&ctx.root, c, false)?;
436+
let mut processed = HashSet::new();
437+
for crate_name in &crate_names {
438+
let start = ctx
439+
.indices
440+
.get(crate_name)
441+
.expect("unable to find crate in tree");
442+
let weight = rgraph.node_weight(*start).unwrap();
443+
let c = ctx.crates.get(weight).unwrap();
444+
publish_release(&ctx.root, c, false)?;
407445

408-
println!("# Please inspect changes and run the following commands when happy:");
446+
println!("# Please inspect changes and run the following commands when happy:");
409447

410-
println!("git commit -a -m 'chore: prepare crate releases'");
411-
let mut bfs = Bfs::new(&rgraph, *start);
412-
while let Some(node) = bfs.next(&rgraph) {
413-
let weight = rgraph.node_weight(node).unwrap();
414-
let c = ctx.crates.get(weight).unwrap();
415-
if c.publish {
416-
println!("git tag {}-v{}", weight, c.version);
448+
println!("git commit -a -m 'chore: prepare crate releases'");
449+
let mut bfs = Bfs::new(&rgraph, *start);
450+
while let Some(node) = bfs.next(&rgraph) {
451+
let weight = rgraph.node_weight(node).unwrap();
452+
let c = ctx.crates.get(weight).unwrap();
453+
if c.publish && !processed.contains(weight) {
454+
println!("git tag {}-v{}", weight, c.version);
455+
}
417456
}
418-
}
419457

420-
println!();
421-
println!("# Run these commands to publish the crate and dependents:");
422-
423-
let mut bfs = Bfs::new(&rgraph, *start);
424-
while let Some(node) = bfs.next(&rgraph) {
425-
let weight = rgraph.node_weight(node).unwrap();
426-
let c = ctx.crates.get(weight).unwrap();
458+
println!();
459+
println!("# Run these commands to publish the crate and dependents:");
460+
461+
let mut bfs = Bfs::new(&rgraph, *start);
462+
while let Some(node) = bfs.next(&rgraph) {
463+
let weight = rgraph.node_weight(node).unwrap();
464+
465+
if !processed.contains(weight) {
466+
processed.insert(weight.clone());
467+
let c = ctx.crates.get(weight).unwrap();
468+
469+
let mut args: Vec<String> = vec![
470+
"publish".to_string(),
471+
"--manifest-path".to_string(),
472+
c.path.join("Cargo.toml").display().to_string(),
473+
];
474+
475+
let config = c.configs.first().unwrap(); // TODO
476+
if !config.features.is_empty() {
477+
args.push("--features".into());
478+
args.push(config.features.join(","));
479+
}
427480

428-
let mut args: Vec<String> = vec![
429-
"publish".to_string(),
430-
"--manifest-path".to_string(),
431-
c.path.join("Cargo.toml").display().to_string(),
432-
];
481+
if let Some(target) = &config.target {
482+
args.push("--target".into());
483+
args.push(target.clone());
484+
}
433485

434-
let config = c.configs.first().unwrap(); // TODO
435-
if !config.features.is_empty() {
436-
args.push("--features".into());
437-
args.push(config.features.join(","));
438-
}
486+
/*
487+
let mut dry_run = args.clone();
488+
dry_run.push("--dry-run".to_string());
439489
440-
if let Some(target) = &config.target {
441-
args.push("--target".into());
442-
args.push(target.clone());
490+
println!("cargo {}", dry_run.join(" "));
491+
*/
492+
if c.publish {
493+
println!("cargo {}", args.join(" "));
494+
}
495+
}
443496
}
444497

445-
/*
446-
let mut dry_run = args.clone();
447-
dry_run.push("--dry-run".to_string());
448-
449-
println!("cargo {}", dry_run.join(" "));
450-
*/
451-
if c.publish {
452-
println!("cargo {}", args.join(" "));
453-
}
498+
println!();
499+
println!("# Run this command to push changes and tags:");
500+
println!("git push --tags");
454501
}
455-
456-
println!();
457-
println!("# Run this command to push changes and tags:");
458-
println!("git push --tags");
459502
}
460503
}
461504
Ok(())

0 commit comments

Comments
 (0)