From bf3021c6d9a1de672ae82810d49917574c716b7a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 20:28:14 +0100 Subject: [PATCH 01/13] Basic publish command that just prints 'publishing' --- tools/Cargo.lock | 238 ++++++++++++++++++++++++++++++++++++++++++++++ tools/Cargo.toml | 9 ++ tools/src/main.rs | 22 +++++ 3 files changed, 269 insertions(+) create mode 100644 tools/Cargo.lock create mode 100644 tools/Cargo.toml create mode 100644 tools/src/main.rs diff --git a/tools/Cargo.lock b/tools/Cargo.lock new file mode 100644 index 00000000..117ad0a6 --- /dev/null +++ b/tools/Cargo.lock @@ -0,0 +1,238 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "3.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "os_str_bytes" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "tools" +version = "0.1.0" +dependencies = [ + "clap", +] + +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/tools/Cargo.toml b/tools/Cargo.toml new file mode 100644 index 00000000..69994b58 --- /dev/null +++ b/tools/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tools" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "3.2.16", features = ["derive", "cargo"] } diff --git a/tools/src/main.rs b/tools/src/main.rs new file mode 100644 index 00000000..26490abe --- /dev/null +++ b/tools/src/main.rs @@ -0,0 +1,22 @@ +use clap::{command, Parser}; + +#[derive(Parser, Debug)] +struct PublishAllCratesArgs {} + +fn main() { + let matches = command!() + .propagate_version(true) + .subcommand_required(true) + .arg_required_else_help(true) + .subcommand(clap::Command::new("publish").about("Publishes agb and all subcrates")) + .get_matches(); + + match matches.subcommand() { + Some(("publish", _)) => publish(), + _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), + } +} + +fn publish() { + println!("publishing!"); +} From 9149cc046d48fa5b025d40bc5640cf7a3c21a07a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 20:29:01 +0100 Subject: [PATCH 02/13] Extract publish function to a new module --- tools/src/main.rs | 8 +++----- tools/src/publish.rs | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 tools/src/publish.rs diff --git a/tools/src/main.rs b/tools/src/main.rs index 26490abe..50d4ed88 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -1,5 +1,7 @@ use clap::{command, Parser}; +mod publish; + #[derive(Parser, Debug)] struct PublishAllCratesArgs {} @@ -12,11 +14,7 @@ fn main() { .get_matches(); match matches.subcommand() { - Some(("publish", _)) => publish(), + Some(("publish", _)) => publish::publish(), _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), } } - -fn publish() { - println!("publishing!"); -} diff --git a/tools/src/publish.rs b/tools/src/publish.rs new file mode 100644 index 00000000..d3fb88a2 --- /dev/null +++ b/tools/src/publish.rs @@ -0,0 +1,3 @@ +pub fn publish() { + println!("Publish!"); +} From e7d27b55f0b3e7067e45ea4714a41c9b79015d88 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:10:11 +0100 Subject: [PATCH 03/13] Get the publish script fully working --- tools/Cargo.lock | 49 ++++++++++++++++ tools/Cargo.toml | 1 + tools/src/main.rs | 6 +- tools/src/publish.rs | 136 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 189 insertions(+), 3 deletions(-) diff --git a/tools/Cargo.lock b/tools/Cargo.lock index 117ad0a6..4e201aef 100644 --- a/tools/Cargo.lock +++ b/tools/Cargo.lock @@ -25,6 +25,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + [[package]] name = "clap" version = "3.2.16" @@ -64,6 +70,22 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "combine" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + [[package]] name = "hashbrown" version = "0.12.3" @@ -95,12 +117,27 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "once_cell" version = "1.13.0" @@ -187,11 +224,23 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +[[package]] +name = "toml_edit" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +dependencies = [ + "combine", + "indexmap", + "itertools", +] + [[package]] name = "tools" version = "0.1.0" dependencies = [ "clap", + "toml_edit", ] [[package]] diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 69994b58..2cf6755b 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] clap = { version = "3.2.16", features = ["derive", "cargo"] } +toml_edit = "0.14.4" diff --git a/tools/src/main.rs b/tools/src/main.rs index 50d4ed88..5c1ed249 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -13,8 +13,12 @@ fn main() { .subcommand(clap::Command::new("publish").about("Publishes agb and all subcrates")) .get_matches(); - match matches.subcommand() { + let result = match matches.subcommand() { Some(("publish", _)) => publish::publish(), _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), + }; + + if let Err(e) = result { + eprintln!("Error: {:?}", e); } } diff --git a/tools/src/publish.rs b/tools/src/publish.rs index d3fb88a2..06e99802 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -1,3 +1,135 @@ -pub fn publish() { - println!("Publish!"); +use std::fs::{self, File}; +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 { + 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 { + 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}", + ) + ) + } + } } From ab4bcad10e394f773916142a00adf25efea28dca Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:11:44 +0100 Subject: [PATCH 04/13] Remove unused import --- tools/src/publish.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/publish.rs b/tools/src/publish.rs index 06e99802..41c85ffc 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -1,4 +1,4 @@ -use std::fs::{self, File}; +use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use std::time::Duration; From bd6089f7c9f1f02a6f7f8fca506954f0db1dbfa3 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:14:47 +0100 Subject: [PATCH 05/13] Add some tests for tools --- tools/src/publish.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/src/publish.rs b/tools/src/publish.rs index 41c85ffc..6911ad75 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -132,4 +132,20 @@ mod test { ) } } + + #[test] + fn should_find_root_directory() -> Result<(), Error> { + assert_ne!(find_agb_root_directory()?.to_string_lossy(), ""); + + Ok(()) + } + + #[test] + fn should_read_version() -> Result<(), Error> { + let root_directory = find_agb_root_directory()?; + let my_version = read_cargo_toml_version(&root_directory.join("tools"))?; + + assert_eq!(my_version, "0.1.0"); + Ok(()) + } } From 0fea113fa231a10f1848eabe25cff2afc2ce43c7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:30:02 +0100 Subject: [PATCH 06/13] Remove the need for clap derive and the cargo feature --- tools/Cargo.lock | 92 ----------------------------------------------- tools/Cargo.toml | 2 +- tools/src/main.rs | 8 ++--- 3 files changed, 3 insertions(+), 99 deletions(-) diff --git a/tools/Cargo.lock b/tools/Cargo.lock index 4e201aef..6d500719 100644 --- a/tools/Cargo.lock +++ b/tools/Cargo.lock @@ -39,28 +39,13 @@ checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9" dependencies = [ "atty", "bitflags", - "clap_derive", "clap_lex", "indexmap", - "once_cell", "strsim", "termcolor", "textwrap", ] -[[package]] -name = "clap_derive" -version = "3.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -92,12 +77,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -138,77 +117,18 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - [[package]] name = "os_str_bytes" version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "syn" -version = "1.0.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "termcolor" version = "1.1.3" @@ -243,18 +163,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "unicode-ident" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "winapi" version = "0.3.9" diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 2cf6755b..24206450 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.2.16", features = ["derive", "cargo"] } +clap = "3.2.16" toml_edit = "0.14.4" diff --git a/tools/src/main.rs b/tools/src/main.rs index 5c1ed249..0ed92e17 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -1,13 +1,9 @@ -use clap::{command, Parser}; +use clap::Command; mod publish; -#[derive(Parser, Debug)] -struct PublishAllCratesArgs {} - fn main() { - let matches = command!() - .propagate_version(true) + let matches = Command::new("Agb tools") .subcommand_required(true) .arg_required_else_help(true) .subcommand(clap::Command::new("publish").about("Publishes agb and all subcrates")) From 375c878849a5ebd2e9c91bb3ec30ce80e62add13 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:30:52 +0100 Subject: [PATCH 07/13] Use the new publish crate tool --- .github/scripts/publish-crate.sh | 44 ------------------------------- .github/workflows/publish-agb.yml | 5 ++-- justfile | 5 ++++ 3 files changed, 8 insertions(+), 46 deletions(-) delete mode 100644 .github/scripts/publish-crate.sh diff --git a/.github/scripts/publish-crate.sh b/.github/scripts/publish-crate.sh deleted file mode 100644 index a1103506..00000000 --- a/.github/scripts/publish-crate.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -set -e # Fail if any command fails - -function wait_for_release() { - local package="$1" - local package_with_underscores="${package/-/_}" - - local first_two_characters="${package_with_underscores:0:2}" - local second_two_characters="${package_with_underscores:2:2}" - - local path="$first_two_characters/$second_two_characters" - - if [ "$package" == "agb" ]; then - path="3/a" - fi - - local url_to_poll="https://raw.githubusercontent.com/rust-lang/crates.io-index/master/$path/$package_with_underscores" - - local expected_version - expected_version=$(grep -E '^version' Cargo.toml | grep -oE '[0-9.]+') - - local attempts=1 - - while [ $attempts -le 15 ]; do - echo "Polling crates.io with URL $url_to_poll to see if the version has updated (attempt $attempts)" - if curl "$url_to_poll" | grep "$expected_version"; then - return - fi - - sleep 30s - attempts=$((attempts + 1)) - done -} - -PROJECTS_TO_RELEASE_IN_ORDER="agb-macros agb-fixnum agb-image-converter agb-sound-converter agb" - -for PROJECT in $PROJECTS_TO_RELEASE_IN_ORDER; do - pushd "$PROJECT" - echo "Publishing $PROJECT" - cargo publish - wait_for_release "$PROJECT" - popd -done diff --git a/.github/workflows/publish-agb.yml b/.github/workflows/publish-agb.yml index f9941c5a..fdf8254d 100644 --- a/.github/workflows/publish-agb.yml +++ b/.github/workflows/publish-agb.yml @@ -19,8 +19,10 @@ jobs: - name: Login to crates.io run: cargo login ${{ secrets.CRATE_API }} + - uses: extractions/setup-just@v1 + - name: Publish crates - run: bash .github/scripts/publish-crate.sh + run: just publish - name: Update template repo env: @@ -30,7 +32,6 @@ jobs: - name: Install gbafix run: cargo install gbafix - - uses: extractions/setup-just@v1 - name: Build the examples run: just build-roms - name: Upload examples to the release diff --git a/justfile b/justfile index baaa31ef..a95a4831 100644 --- a/justfile +++ b/justfile @@ -64,6 +64,11 @@ update-linker-scripts: find -type f -name gba.ld | grep -v ./agb/gba.ld | xargs -n1 cp -v -- agb/gba.ld find -type f -name gba_mb.ld | grep -v ./agb/gba_mb.ld | xargs -n1 cp -v -- agb/gba_mb.ld +publish: (_run-tool "publish") + +_run-tool +tool: + cargo run --manifest-path "{{justfile_directory() + "/tools/Cargo.toml"}}" -- {{tool}} + _build-rom folder name: #!/usr/bin/env bash set -euxo pipefail From 4277f07ec0e5e5d1fc8dbefb884a71fe92289597 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:36:24 +0100 Subject: [PATCH 08/13] Allow dry run of publish --- tools/src/main.rs | 4 ++-- tools/src/publish.rs | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/tools/src/main.rs b/tools/src/main.rs index 0ed92e17..aba2046c 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -6,11 +6,11 @@ fn main() { let matches = Command::new("Agb tools") .subcommand_required(true) .arg_required_else_help(true) - .subcommand(clap::Command::new("publish").about("Publishes agb and all subcrates")) + .subcommand(publish::command()) .get_matches(); let result = match matches.subcommand() { - Some(("publish", _)) => publish::publish(), + Some(("publish", arg_matches)) => publish::publish(arg_matches), _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), }; diff --git a/tools/src/publish.rs b/tools/src/publish.rs index 6911ad75..ada644d5 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -1,3 +1,4 @@ +use clap::{Arg, ArgAction, ArgMatches}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -21,19 +22,40 @@ pub enum Error { CrateVersion, } -pub fn publish() -> Result<(), Error> { +pub fn command() -> clap::Command<'static> { + clap::Command::new("publish") + .about("Publishes agb and all subcrates") + .arg( + Arg::new("Dry run") + .long("dry-run") + .help("Don't actually publish") + .action(ArgAction::SetTrue), + ) +} + +pub fn publish(matches: &ArgMatches) -> Result<(), Error> { + let dry_run = matches.get_one::("Dry run").expect("defined by clap"); + 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); + if *dry_run { + println!( + "Would run `cargo publish` in {}", + crate_dir.to_string_lossy() + ); + } else { + 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)?; From c3f803206abe0564c92fd9d36dd5f252d7a6042c Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 21:53:18 +0100 Subject: [PATCH 09/13] Add the ability to read the dependencies of a crate --- tools/src/publish.rs | 52 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/tools/src/publish.rs b/tools/src/publish.rs index ada644d5..c0f9e016 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -20,6 +20,8 @@ pub enum Error { PublishCrate, Poll, CrateVersion, + ReadingDependencies, + CargoToml, } pub fn command() -> clap::Command<'static> { @@ -117,11 +119,7 @@ fn get_url_to_poll(crate_name: &str) -> String { } fn read_cargo_toml_version(folder: &Path) -> Result { - 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 cargo_toml = read_cargo_toml(folder)?; let version_value = cargo_toml["package"]["version"] .as_value() @@ -132,6 +130,33 @@ fn read_cargo_toml_version(folder: &Path) -> Result { Ok(version_value.to_owned()) } +fn get_agb_dependencies(folder: &Path) -> Result, Error> { + let cargo_toml = read_cargo_toml(folder)?; + + let dependencies = cargo_toml["dependencies"] + .as_table() + .ok_or(Error::ReadingDependencies)? + .get_values(); + + let mut result = vec![]; + + for (key, _) in dependencies { + let dep = key[0].get(); + if dep.starts_with("agb") { + result.push(dep.replace('_', "-")) + } + } + + Ok(result) +} + +fn read_cargo_toml(folder: &Path) -> Result { + let cargo_toml_contents = + fs::read_to_string(folder.join("Cargo.toml")).map_err(|_| Error::CargoToml)?; + let cargo_toml: Document = cargo_toml_contents.parse().map_err(|_| Error::CargoToml)?; + Ok(cargo_toml) +} + #[cfg(test)] mod test { use super::*; @@ -170,4 +195,21 @@ mod test { assert_eq!(my_version, "0.1.0"); Ok(()) } + + #[test] + fn should_detect_dependencies() -> Result<(), Error> { + let root_directory = find_agb_root_directory()?; + let deps = get_agb_dependencies(&root_directory.join("agb"))?; + + assert_eq!( + deps, + &[ + "agb-image-converter", + "agb-sound-converter", + "agb-macros", + "agb-fixnum" + ] + ); + Ok(()) + } } From 604395ec26c4287d461af02d3c3f2dec96a814b7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 22:17:54 +0100 Subject: [PATCH 10/13] Release as much at once as possible --- tools/src/publish.rs | 100 ++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/tools/src/publish.rs b/tools/src/publish.rs index c0f9e016..edb64e35 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -1,4 +1,5 @@ use clap::{Arg, ArgAction, ArgMatches}; +use std::collections::{HashMap, HashSet}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; @@ -40,28 +41,53 @@ pub fn publish(matches: &ArgMatches) -> 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 mut fully_published_crates: HashSet = HashSet::new(); + let mut published_crates: HashSet = HashSet::new(); - if *dry_run { - println!( - "Would run `cargo publish` in {}", - crate_dir.to_string_lossy() - ); - } else { - let publish_result = Command::new("cargo") - .arg("publish") - .current_dir(&crate_dir) - .spawn(); + let dependencies = build_dependency_graph(&root_directory, CRATES_TO_PUBLISH)?; - if let Err(err) = publish_result { - println!("Error while publishing crate {crate_to_publish}: {err}"); - return Err(Error::PublishCrate); + while published_crates.len() != CRATES_TO_PUBLISH.len() { + // find all crates which can be published now but haven't + let publishable_crates: Vec<_> = CRATES_TO_PUBLISH + .iter() + .filter(|&&crate_to_publish| !published_crates.contains(crate_to_publish)) + .filter(|&&crate_to_publish| { + let dependencies_of_crate = &dependencies[crate_to_publish]; + for dependency_of_crate in dependencies_of_crate { + if !fully_published_crates.contains(dependency_of_crate) { + return false; + } + } + + true + }) + .collect(); + + for publishable_crate in publishable_crates { + if *dry_run { + println!("Would execute cargo publish for {publishable_crate}"); + } else { + Command::new("cargo") + .arg("publish") + .current_dir(&root_directory.join(publishable_crate)) + .spawn() + .map_err(|_| Error::PublishCrate)?; + } + + published_crates.insert(publishable_crate.to_string()); + } + + for published_crate in published_crates.iter() { + if !fully_published_crates.contains(published_crate) { + let expected_version = + read_cargo_toml_version(&root_directory.join(published_crate))?; + if check_if_released(published_crate, &expected_version)? { + fully_published_crates.insert(published_crate.clone()); + } } } - let expected_version = read_cargo_toml_version(&crate_dir)?; - wait_for_release(crate_to_publish, &expected_version)?; + thread::sleep(Duration::from_secs(10)); } Ok(()) @@ -80,27 +106,17 @@ fn find_agb_root_directory() -> Result { Ok(current_path) } -fn wait_for_release(crate_to_publish: &str, expected_version: &str) -> Result<(), Error> { +fn check_if_released(crate_to_publish: &str, expected_version: &str) -> Result { 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}" - ); + println!("Polling crates.io with URL {url_to_poll} for {crate_to_publish} hoping for version {expected_version}."); - let curl_result = Command::new("curl") - .arg(url_to_poll) - .output() - .map_err(|_| Error::Poll)?; + 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(()) + Ok(String::from_utf8_lossy(&curl_result.stdout).contains(expected_version)) } fn get_url_to_poll(crate_name: &str) -> String { @@ -130,6 +146,22 @@ fn read_cargo_toml_version(folder: &Path) -> Result { Ok(version_value.to_owned()) } +fn build_dependency_graph( + root: &Path, + agb_crates: &[&str], +) -> Result>, Error> { + let mut result = HashMap::new(); + + for agb_crate in agb_crates { + result.insert( + agb_crate.to_string(), + get_agb_dependencies(&root.join(agb_crate))?, + ); + } + + Ok(result) +} + fn get_agb_dependencies(folder: &Path) -> Result, Error> { let cargo_toml = read_cargo_toml(folder)?; From 4bc5df458a6a515ba112dcf18fe7b6e5f4a71f83 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 22:23:49 +0100 Subject: [PATCH 11/13] Lint and test the tools in CI --- justfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/justfile b/justfile index a95a4831..c3c1e294 100644 --- a/justfile +++ b/justfile @@ -9,11 +9,13 @@ build-release: just _build-release agb clippy: just _all-crates _clippy + just _clippy tools test: just _test-debug agb just _test-debug agb-fixnum just _test-debug-arm agb + just _test-debug tools test-release: just _test-release agb From c74c23186301c393d2c69137544924766c3a62ca Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 22:24:25 +0100 Subject: [PATCH 12/13] Deny clippy::all --- tools/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/src/main.rs b/tools/src/main.rs index aba2046c..09fce409 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -1,3 +1,4 @@ +#![deny(clippy::all)] use clap::Command; mod publish; From 0e1eb41852f6879a557be047bf764a74d932558b Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 4 Aug 2022 22:57:24 +0100 Subject: [PATCH 13/13] No longer hardcode the crates to publish --- tools/src/publish.rs | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/tools/src/publish.rs b/tools/src/publish.rs index edb64e35..9542f7e6 100644 --- a/tools/src/publish.rs +++ b/tools/src/publish.rs @@ -7,14 +7,6 @@ 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, @@ -44,11 +36,12 @@ pub fn publish(matches: &ArgMatches) -> Result<(), Error> { let mut fully_published_crates: HashSet = HashSet::new(); let mut published_crates: HashSet = HashSet::new(); - let dependencies = build_dependency_graph(&root_directory, CRATES_TO_PUBLISH)?; + let dependencies = build_dependency_graph(&root_directory)?; + let crates_to_publish: Vec<_> = dependencies.keys().collect(); - while published_crates.len() != CRATES_TO_PUBLISH.len() { + while published_crates.len() != crates_to_publish.len() { // find all crates which can be published now but haven't - let publishable_crates: Vec<_> = CRATES_TO_PUBLISH + let publishable_crates: Vec<_> = crates_to_publish .iter() .filter(|&&crate_to_publish| !published_crates.contains(crate_to_publish)) .filter(|&&crate_to_publish| { @@ -146,17 +139,27 @@ fn read_cargo_toml_version(folder: &Path) -> Result { Ok(version_value.to_owned()) } -fn build_dependency_graph( - root: &Path, - agb_crates: &[&str], -) -> Result>, Error> { +fn build_dependency_graph(root: &Path) -> Result>, Error> { let mut result = HashMap::new(); + result.insert("agb".to_owned(), get_agb_dependencies(&root.join("agb"))?); - for agb_crate in agb_crates { - result.insert( - agb_crate.to_string(), - get_agb_dependencies(&root.join(agb_crate))?, - ); + let mut added_new_crates = true; + while added_new_crates { + added_new_crates = false; + + let all_crates: HashSet = HashSet::from_iter(result.values().flatten().cloned()); + + for dep_crate in all_crates { + if result.contains_key(&dep_crate) { + continue; + } + + added_new_crates = true; + result.insert( + dep_crate.to_owned(), + get_agb_dependencies(&root.join(dep_crate))?, + ); + } } Ok(result)