agb/tools/src/publish.rs

136 lines
3.8 KiB
Rust
Raw Normal View History

2022-08-05 06:11:44 +10:00
use std::fs;
2022-08-05 06:10:11 +10:00
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Duration;
use std::{env, thread};
use toml_edit::Document;
const CRATES_TO_PUBLISH: &[&str] = &[
"agb-macros",
"agb-fixnum",
"agb-image-converter",
"agb-sound-converter",
"agb",
];
#[derive(Debug)]
pub enum Error {
FindRootDirectory,
PublishCrate,
Poll,
CrateVersion,
}
pub fn publish() -> Result<(), Error> {
let root_directory = find_agb_root_directory()?;
for crate_to_publish in CRATES_TO_PUBLISH.iter() {
let crate_dir = root_directory.join(crate_to_publish);
let publish_result = Command::new("cargo")
.arg("publish")
.current_dir(&crate_dir)
.spawn();
if let Err(err) = publish_result {
println!("Error while publishing crate {crate_to_publish}: {err}");
return Err(Error::PublishCrate);
}
let expected_version = read_cargo_toml_version(&crate_dir)?;
wait_for_release(crate_to_publish, &expected_version)?;
}
Ok(())
}
fn find_agb_root_directory() -> Result<PathBuf, Error> {
let mut current_path = env::current_dir().map_err(|_| Error::FindRootDirectory)?;
while !current_path.clone().join("justfile").exists() {
current_path = current_path
.parent()
.ok_or(Error::FindRootDirectory)?
.to_owned();
}
Ok(current_path)
}
fn wait_for_release(crate_to_publish: &str, expected_version: &str) -> Result<(), Error> {
let url_to_poll = &get_url_to_poll(crate_to_publish);
for attempt in 0..15 {
println!(
"Polling crates.io with URL {url_to_poll} for {crate_to_publish} hoping for version {expected_version}. Attempt {attempt}"
);
let curl_result = Command::new("curl")
.arg(url_to_poll)
.output()
.map_err(|_| Error::Poll)?;
if String::from_utf8_lossy(&curl_result.stdout).contains(expected_version) {
return Ok(());
}
thread::sleep(Duration::from_secs(30));
}
Ok(())
}
fn get_url_to_poll(crate_name: &str) -> String {
let crate_name_with_underscores = crate_name.replace('-', "_");
let crate_folder = if crate_name_with_underscores.len() == 3 {
format!("3/{}", crate_name_with_underscores.chars().next().unwrap())
} else {
let first_two_characters = &crate_name_with_underscores[0..2];
let second_two_characters = &crate_name_with_underscores[2..4];
format!("{first_two_characters}/{second_two_characters}")
};
format!("https://raw.githubusercontent.com/rust-lang/crates.io-index/master/{crate_folder}/{crate_name_with_underscores}")
}
fn read_cargo_toml_version(folder: &Path) -> Result<String, Error> {
let cargo_toml_contents =
fs::read_to_string(folder.join("Cargo.toml")).map_err(|_| Error::CrateVersion)?;
let cargo_toml: Document = cargo_toml_contents
.parse()
.map_err(|_| Error::CrateVersion)?;
let version_value = cargo_toml["package"]["version"]
.as_value()
.ok_or(Error::CrateVersion)?
.as_str()
.ok_or(Error::CrateVersion)?;
Ok(version_value.to_owned())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn url_to_poll_should_return_correct_url() {
let test_cases = [
["agb", "3/a/agb"],
["agb-image-converter", "ag/b_/agb_image_converter"],
["agb-fixnum", "ag/b_/agb_fixnum"],
];
for [name, result] in test_cases {
let url = get_url_to_poll(name);
assert_eq!(
url,
format!(
"https://raw.githubusercontent.com/rust-lang/crates.io-index/master/{result}",
)
)
}
}
}