xtask/publish.rs
1//! `cargo xtask publish`: publish the workspace's crates in dependency order.
2//!
3//! A crate cannot be packaged until the crates it depends on are in the
4//! registry, so the order is not a preference — publishing `unlit3d` before
5//! `unlit_wgpu` fails with `no matching package named unlit_wgpu found`. The
6//! order is written down here rather than left to the caller, and each
7//! `cargo publish` waits for the crate it uploaded to appear in the index, so
8//! the next one's dependencies resolve by the time it is packaged.
9//!
10//! `cargo publish --workspace` orders the crates itself, but its `--dry-run`
11//! cannot verify them (rust-lang/cargo#16525), and a release is worth checking
12//! before it is uploaded; going crate by crate keeps that check.
13//!
14//! The test harness, the examples and the task runner are `publish = false`,
15//! so they are not in the list and are never uploaded.
16//!
17//! Arguments after `--` are forwarded to every `cargo publish`, so a flag cargo
18//! gains does not need adding here.
19
20use std::process::Command;
21
22use super::{PublishArgs, step};
23
24/// The publishable crates, in the order publishing requires.
25///
26/// Each crate depends on the ones before it and so goes after them. A crate
27/// added to the workspace belongs here if and only if it is meant to reach
28/// crates.io.
29const CRATES: [&str; 4] = ["unlit_ecs", "unlit_wgpu", "unlit3d", "unlit3dx"];
30
31/// Publish every crate, in order.
32pub fn run(args: &PublishArgs) -> Result<(), String> {
33 // The forwarded arguments are opaque here, so `--dry-run` is recognised by
34 // name: it makes the run repeatable, which is what decides whether a
35 // partial failure can simply be re-run.
36 let dry_run = args.cargo_args.iter().any(|arg| arg == "--dry-run");
37
38 for (index, krate) in CRATES.iter().enumerate() {
39 let mut cargo = Command::new("cargo");
40 cargo.args(["publish", "-p", krate]).args(&args.cargo_args);
41
42 let step_name = format!("publishing `{krate}`");
43 if let Err(error) = step::run(&mut cargo, &step_name) {
44 // A real publish is not repeatable: cargo refuses a version that is
45 // already on the registry, so a run that stopped partway has to be
46 // resumed at the crate that failed.
47 return Err(if index == 0 || dry_run {
48 error
49 } else {
50 format!(
51 "{error}\nthe crates before `{krate}` were uploaded; resume with \
52 `cargo publish -p {krate}`"
53 )
54 });
55 }
56 }
57
58 Ok(())
59}