agb/tools/src/publish.rs

186 lines
4.7 KiB
Rust
Raw Normal View History

2022-08-05 06:36:24 +10:00
use clap::{Arg, ArgAction, ArgMatches};
2023-04-12 07:29:23 +10:00
use dependency_graph::DependencyGraph;
use std::cell::RefCell;
use std::collections::HashMap;
2022-08-05 06:11:44 +10:00
use std::fs;
use std::path::{Path, PathBuf};
2022-08-05 06:10:11 +10:00
use std::process::Command;
2024-03-21 00:26:02 +11:00
use toml_edit::DocumentMut;
2022-08-05 06:10:11 +10:00
2022-10-02 06:36:42 +11:00
use crate::utils::*;
2022-08-05 06:10:11 +10:00
#[derive(Debug)]
pub enum Error {
FindRootDirectory,
PublishCrate,
Poll,
CrateVersion,
ReadingDependencies,
CargoToml,
2022-08-05 06:10:11 +10:00
}
2023-04-12 07:29:23 +10:00
struct Package {
name: String,
dependencies: Vec<String>,
directory: PathBuf,
2023-04-12 07:29:23 +10:00
}
impl dependency_graph::Node for Package {
type DependencyType = String;
fn dependencies(&self) -> &[Self::DependencyType] {
&self.dependencies
}
fn matches(&self, dependency: &Self::DependencyType) -> bool {
&self.name == dependency
}
}
2022-10-02 05:42:39 +11:00
pub fn command() -> clap::Command {
2022-08-05 06:36:24 +10:00
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::<bool>("Dry run").expect("defined by clap");
2023-04-12 07:29:23 +10:00
let dry_run = if *dry_run { vec!["--dry-run"] } else { vec![] };
2022-08-05 06:36:24 +10:00
2022-10-02 06:36:42 +11:00
let root_directory = find_agb_root_directory().map_err(|_| Error::FindRootDirectory)?;
2022-08-05 06:10:11 +10:00
2023-04-12 07:29:23 +10:00
let mut in_progress: HashMap<_, RefCell<std::process::Child>> = HashMap::new();
2022-08-05 07:17:54 +10:00
let mut dependencies = build_dependency_graph(&root_directory)?;
let mut tracker_dependencies = build_dependency_graph(&root_directory.join("tracker"))?;
dependencies.append(&mut tracker_dependencies);
2023-04-12 07:29:23 +10:00
let graph = DependencyGraph::from(&dependencies[..]);
for package in graph {
let package = package.as_resolved().unwrap();
for dep in &package.dependencies {
assert!(in_progress
.get(dep)
.unwrap()
.borrow_mut()
.wait()
.map_err(|_| Error::PublishCrate)?
.success());
2022-08-05 06:10:11 +10:00
}
2023-04-12 07:29:23 +10:00
println!("Publishing {}", package.name);
2022-08-05 06:10:11 +10:00
2023-04-12 07:29:23 +10:00
let publish_cmd = Command::new("cargo")
.arg("publish")
.args(&dry_run)
.current_dir(&package.directory)
2023-04-12 07:29:23 +10:00
.spawn()
.map_err(|_| Error::PublishCrate)?;
2022-08-05 06:10:11 +10:00
2023-04-12 07:29:23 +10:00
in_progress.insert(package.name.clone(), RefCell::new(publish_cmd));
}
2022-08-05 06:10:11 +10:00
2023-04-12 07:29:23 +10:00
for (_, in_progress) in in_progress {
assert!(in_progress
.borrow_mut()
.wait()
.map_err(|_| Error::PublishCrate)?
.success());
}
2022-08-05 06:10:11 +10:00
2023-04-12 07:29:23 +10:00
Ok(())
2022-08-05 06:10:11 +10:00
}
2023-04-12 07:29:23 +10:00
fn build_dependency_graph(root: &Path) -> Result<Vec<Package>, Error> {
let dirs = fs::read_dir(root).map_err(|_| Error::ReadingDependencies)?;
let mut packages = vec![];
for dir in dirs {
let dir = dir.map_err(|_| Error::ReadingDependencies)?;
if !dir
.file_type()
.map_err(|_| Error::ReadingDependencies)?
.is_dir()
{
continue;
}
2023-04-12 07:29:23 +10:00
if !dir.file_name().to_string_lossy().starts_with("agb") {
continue;
}
2022-08-05 07:17:54 +10:00
2023-04-12 07:29:23 +10:00
let crate_path = root.join(dir.path());
2023-04-12 07:29:23 +10:00
packages.push(Package {
name: dir.file_name().to_string_lossy().to_string(),
dependencies: get_agb_dependencies(&crate_path)?,
directory: crate_path,
2023-04-12 07:29:23 +10:00
});
2022-08-05 07:17:54 +10:00
}
2023-04-12 07:29:23 +10:00
Ok(packages)
2022-08-05 07:17:54 +10:00
}
fn get_agb_dependencies(folder: &Path) -> Result<Vec<String>, 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)
}
2024-03-21 00:26:02 +11:00
fn read_cargo_toml(folder: &Path) -> Result<DocumentMut, Error> {
let cargo_toml_contents =
fs::read_to_string(folder.join("Cargo.toml")).map_err(|_| Error::CargoToml)?;
2024-03-21 00:26:02 +11:00
let cargo_toml: DocumentMut = cargo_toml_contents.parse().map_err(|_| Error::CargoToml)?;
Ok(cargo_toml)
}
2022-08-05 06:10:11 +10:00
#[cfg(test)]
mod test {
use super::*;
2022-10-02 05:42:39 +11:00
#[test]
fn verify_cli() {
command().debug_assert();
}
#[test]
fn should_detect_dependencies() -> Result<(), Error> {
2022-10-02 06:36:42 +11:00
let root_directory = crate::utils::find_agb_root_directory().unwrap();
let deps = get_agb_dependencies(&root_directory.join("agb"))?;
assert_eq!(
deps,
&[
"agb-image-converter",
"agb-sound-converter",
"agb-macros",
2023-04-24 02:58:59 +10:00
"agb-fixnum",
"agb-hashmap",
]
);
Ok(())
}
}