Skip to content

Commit f56223b

Browse files
authored
Merge branch 'master' into shub/async-dst
2 parents 6da287d + 58660ad commit f56223b

4 files changed

Lines changed: 66 additions & 9 deletions

File tree

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@ jobs:
124124
- name: Run release (dry-run)
125125
if: ${{ inputs.dry_run }}
126126
# NOTE: This will print a warning that `cargo-release release crates` dry runs are not supported
127-
run: cargo-release release crates --dry-run
127+
run: cargo-release release crates ${{ github.event.inputs.release_tag }} --dry-run
128128

129129
- name: Run release
130130
if: ${{ !inputs.dry_run }}
131-
run: cargo-release release crates
131+
run: cargo-release release crates ${{ github.event.inputs.release_tag }}
132132

133133
release-csharp:
134134
needs: build-cargo-release

tools/release/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,17 @@ Release the following packages to crates.io:
4949
- sdk
5050

5151
```bash
52-
cargo release crates
52+
cargo release crates v1.2.0
5353
```
5454

5555
You can also perform a dry run to see what would be published without actually publishing:
5656

5757
```bash
58-
cargo release crates --dry-run
58+
cargo release crates v1.2.0 --dry-run
5959
```
6060

61+
After each crate is published, the release waits for that crate version to become visible in the crates.io index before publishing dependent crates.
62+
6163
### NPM Package
6264

6365
Release the TypeScript SDK to npm. This will:

tools/release/src/main.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct ReleaseArgs {
3333
enum Commands {
3434
/// Release crates.io packages
3535
Crates {
36+
release_version: String,
3637
#[arg(long)]
3738
dry_run: bool,
3839
},
@@ -82,8 +83,11 @@ fn main() {
8283
let CargoCli::Release(release_args) = cli.command;
8384

8485
let result = match &release_args.command {
85-
Commands::Crates { dry_run } => {
86-
let target = CratesRelease::new(*dry_run);
86+
Commands::Crates {
87+
release_version: version,
88+
dry_run,
89+
} => {
90+
let target = CratesRelease::new(version.clone(), *dry_run);
8791
target.release()
8892
}
8993
Commands::Csharp {
@@ -131,7 +135,7 @@ fn release_all(version: String, skip: Option<Vec<String>>, dry_run: bool) -> Res
131135
let skip_targets = skip.unwrap_or_default();
132136

133137
let targets: Vec<Box<dyn ReleaseTarget>> = vec![
134-
Box::new(CratesRelease::new(dry_run)),
138+
Box::new(CratesRelease::new(version.clone(), dry_run)),
135139
Box::new(NpmRelease::new(version.clone(), dry_run)),
136140
Box::new(CSharpRelease::new(version.clone(), dry_run)),
137141
Box::new(CppRelease::new(version.clone(), dry_run)),

tools/release/src/targets/crates.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@ use anyhow::Result;
33
use std::collections::HashMap;
44
use std::path::PathBuf;
55
use std::process::Command;
6+
use std::thread;
7+
use std::time::{Duration, Instant};
68

79
const CRATE_OWNERS: &[&str] = &["cloutiertyler", "jdetter", "bfops", "rekhoff", "spacetimedb-devops"];
10+
const CRATES_IO_POLL_INTERVAL: Duration = Duration::from_secs(15);
11+
const CRATES_IO_POLL_TIMEOUT: Duration = Duration::from_secs(15 * 60);
812

913
pub struct CratesRelease {
14+
pub version: String,
1015
pub dry_run: bool,
1116
}
1217

1318
impl CratesRelease {
14-
pub fn new(dry_run: bool) -> Self {
15-
Self { dry_run }
19+
pub fn new(version: String, dry_run: bool) -> Self {
20+
Self { version, dry_run }
1621
}
1722

1823
/// Publishes a single crate to crates.io
@@ -61,6 +66,50 @@ impl CratesRelease {
6166
))
6267
}
6368

69+
fn wait_for_crate_available(&self, crate_name: &str, version: &str) -> Result<(), String> {
70+
let spec = format!("{}@{}", crate_name, version);
71+
let deadline = Instant::now() + CRATES_IO_POLL_TIMEOUT;
72+
let mut attempt = 1;
73+
74+
println!(
75+
"Waiting for {} to be visible in the crates.io index before publishing dependent crates...",
76+
spec
77+
);
78+
79+
loop {
80+
let mut cmd = Command::new("cargo");
81+
cmd.args(["info", &spec]);
82+
util::print_command(&cmd);
83+
84+
let output = cmd
85+
.output()
86+
.map_err(|e| format!("Failed to execute cargo info {}: {}", spec, e))?;
87+
88+
if output.status.success() {
89+
println!("{} is visible in the crates.io index.", spec);
90+
return Ok(());
91+
}
92+
93+
if Instant::now() >= deadline {
94+
return Err(format!(
95+
"Timed out waiting for {} to become visible in the crates.io index\n--- stdout ---\n{}\n--- stderr ---\n{}",
96+
spec,
97+
String::from_utf8_lossy(&output.stdout),
98+
String::from_utf8_lossy(&output.stderr)
99+
));
100+
}
101+
102+
println!(
103+
"{} is not visible yet; retrying in {}s (attempt {}).",
104+
spec,
105+
CRATES_IO_POLL_INTERVAL.as_secs(),
106+
attempt
107+
);
108+
attempt += 1;
109+
thread::sleep(CRATES_IO_POLL_INTERVAL);
110+
}
111+
}
112+
64113
fn add_crate_owners(&self, crate_name: &str) -> Result<(), String> {
65114
println!("Adding owners for crate: {}", crate_name);
66115

@@ -126,8 +175,10 @@ impl ReleaseTarget for CratesRelease {
126175
}
127176

128177
println!("\nStarting publish process...");
178+
let crates_io_version = self.version.strip_prefix('v').unwrap_or(&self.version);
129179
for crate_name in &crates {
130180
self.publish_crate(crate_name, &manifest_map)?;
181+
self.wait_for_crate_available(crate_name, crates_io_version)?;
131182
self.add_crate_owners(crate_name)?;
132183
}
133184

0 commit comments

Comments
 (0)