|
1 | | -use std::collections::{BTreeMap, HashMap}; |
| 1 | +use std::collections::{BTreeMap, HashMap, HashSet}; |
2 | 2 | use std::fs; |
3 | 3 | use std::path::{Path, PathBuf}; |
4 | 4 | use std::process::Command as ProcessCommand; |
@@ -56,14 +56,15 @@ enum Command { |
56 | 56 | #[arg(value_name = "CRATE")] |
57 | 57 | crate_name: String, |
58 | 58 | }, |
59 | | - /// Prepare to release a crate and all dependents that needs updating |
| 59 | + /// Prepare to release crates and all dependents that needs updating |
60 | 60 | /// - Semver checks |
61 | 61 | /// - Bump versions and commit |
62 | 62 | /// - Create tag. |
63 | 63 | 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 |
65 | 66 | #[arg(value_name = "CRATE")] |
66 | | - crate_name: String, |
| 67 | + crate_names: Vec<String>, |
67 | 68 | }, |
68 | 69 | } |
69 | 70 |
|
@@ -345,117 +346,159 @@ fn main() -> Result<()> { |
345 | 346 | } |
346 | 347 | check_semver(ctx.root.clone(), c)?; |
347 | 348 | } |
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 | + } |
362 | 364 | } |
363 | 365 |
|
364 | 366 | let mut rgraph = ctx.graph.clone(); |
365 | 367 | rgraph.reverse(); |
366 | 368 |
|
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 | + } |
381 | 431 | } |
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)?; |
397 | 432 | } |
398 | | - |
399 | | - // Update changelog |
400 | | - update_changelog(&ctx.root, c)?; |
401 | 433 | } |
402 | 434 | } |
403 | 435 |
|
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)?; |
407 | 445 |
|
408 | | - println!("# Please inspect changes and run the following commands when happy:"); |
| 446 | + println!("# Please inspect changes and run the following commands when happy:"); |
409 | 447 |
|
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 | + } |
417 | 456 | } |
418 | | - } |
419 | 457 |
|
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 | + } |
427 | 480 |
|
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 | + } |
433 | 485 |
|
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()); |
439 | 489 |
|
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 | + } |
443 | 496 | } |
444 | 497 |
|
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"); |
454 | 501 | } |
455 | | - |
456 | | - println!(); |
457 | | - println!("# Run this command to push changes and tags:"); |
458 | | - println!("git push --tags"); |
459 | 502 | } |
460 | 503 | } |
461 | 504 | Ok(()) |
|
0 commit comments