2022-10-02 06:57:41 +11:00
|
|
|
use std::{io::Read, path::Path, process::Command};
|
|
|
|
|
|
|
|
use crate::utils::find_agb_root_directory;
|
|
|
|
|
2022-10-02 06:21:15 +11:00
|
|
|
pub fn command() -> clap::Command {
|
|
|
|
clap::Command::new("release")
|
|
|
|
.about("Prepares and commits the changes required to release agb")
|
|
|
|
.arg(
|
|
|
|
clap::Arg::new("version")
|
2022-10-02 06:30:45 +11:00
|
|
|
.required(true)
|
2022-10-02 06:21:15 +11:00
|
|
|
.help("New version to release")
|
|
|
|
.value_parser(version_parser),
|
|
|
|
)
|
2022-10-02 06:30:45 +11:00
|
|
|
.arg(
|
|
|
|
clap::Arg::new("Dry run")
|
|
|
|
.long("dry-run")
|
|
|
|
.help("Don't do anything with git (but does everything else)")
|
|
|
|
.action(clap::ArgAction::SetTrue),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn release(matches: &clap::ArgMatches) -> Result<(), Error> {
|
|
|
|
let dry_run = matches.get_one::<bool>("Dry run").expect("defined by clap");
|
|
|
|
let version = matches
|
|
|
|
.get_one::<Version>("version")
|
|
|
|
.expect("defined by clap");
|
|
|
|
|
2022-10-02 06:57:41 +11:00
|
|
|
let root_directory = find_agb_root_directory().map_err(|_| Error::FindRootDirectory)?;
|
|
|
|
|
|
|
|
// if not dry run, check that there are no out-standing changes in git
|
|
|
|
if !dry_run && !execute_git_command(&root_directory, &["status", "--porcelain"])?.is_empty() {
|
|
|
|
println!("Uncommitted changes, please commit first");
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we are in the master branch
|
|
|
|
if !dry_run
|
|
|
|
&& execute_git_command(&root_directory, &["symbolic-ref", "--short", "HEAD"])? != "master"
|
|
|
|
{
|
|
|
|
println!("You must be on the master branch before releasing");
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2022-10-02 06:30:45 +11:00
|
|
|
todo!()
|
2022-10-02 06:21:15 +11:00
|
|
|
}
|
|
|
|
|
2022-10-02 06:57:41 +11:00
|
|
|
fn execute_git_command(root_directory: &Path, args: &[&str]) -> Result<String, Error> {
|
|
|
|
let git_cmd = Command::new("git")
|
|
|
|
.args(args)
|
|
|
|
.current_dir(root_directory)
|
|
|
|
.spawn()
|
|
|
|
.map_err(|_| Error::GitError)?;
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
git_cmd
|
|
|
|
.stdout
|
|
|
|
.ok_or(Error::GitError)?
|
|
|
|
.read_to_end(&mut buf)
|
|
|
|
.map_err(|_| Error::GitError)?;
|
|
|
|
|
|
|
|
String::from_utf8(buf).map_err(|_| Error::GitError)
|
|
|
|
}
|
|
|
|
|
2022-10-02 06:30:45 +11:00
|
|
|
#[derive(Debug)]
|
2022-10-02 06:57:41 +11:00
|
|
|
pub enum Error {
|
|
|
|
FindRootDirectory,
|
|
|
|
GitError,
|
|
|
|
}
|
2022-10-02 06:30:45 +11:00
|
|
|
|
2022-10-02 06:21:15 +11:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
struct Version {
|
|
|
|
major: u32,
|
|
|
|
minor: u32,
|
|
|
|
patch: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Version {
|
|
|
|
#[cfg(test)]
|
|
|
|
pub fn new(major: u32, minor: u32, patch: u32) -> Self {
|
|
|
|
Self {
|
|
|
|
major,
|
|
|
|
minor,
|
|
|
|
patch,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-02 06:57:41 +11:00
|
|
|
impl std::fmt::Display for Version {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-02 06:21:15 +11:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
struct ParseVersionError;
|
|
|
|
|
|
|
|
impl std::str::FromStr for Version {
|
|
|
|
type Err = ParseVersionError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
let version_array: Vec<_> = s
|
|
|
|
.split('.')
|
|
|
|
.map(|v| v.parse())
|
|
|
|
.collect::<Result<Vec<_>, _>>()
|
|
|
|
.map_err(|_| ParseVersionError)?;
|
|
|
|
|
|
|
|
if version_array.len() > 3 || version_array.is_empty() {
|
|
|
|
return Err(ParseVersionError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Version {
|
|
|
|
major: version_array[0],
|
|
|
|
minor: *version_array.get(1).unwrap_or(&0),
|
|
|
|
patch: *version_array.get(2).unwrap_or(&0),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn version_parser(maybe_version: &str) -> Result<Version, &'static str> {
|
2022-10-02 06:30:45 +11:00
|
|
|
maybe_version
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| "Failed to parse version, must be of the format x.y.z")
|
2022-10-02 06:21:15 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn verify_cli() {
|
|
|
|
command().debug_assert();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_parse_versions() {
|
|
|
|
assert_eq!(Version::from_str("0.1.2").unwrap(), Version::new(0, 1, 2));
|
|
|
|
assert_eq!(Version::from_str("0.1").unwrap(), Version::new(0, 1, 0));
|
|
|
|
assert_eq!(
|
|
|
|
Version::from_str("33.23.4000").unwrap(),
|
|
|
|
Version::new(33, 23, 4000)
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(Version::from_str("abc").unwrap_err(), ParseVersionError);
|
|
|
|
assert_eq!(Version::from_str("").unwrap_err(), ParseVersionError);
|
|
|
|
assert_eq!(Version::from_str("0.2.4.5").unwrap_err(), ParseVersionError);
|
|
|
|
assert_eq!(Version::from_str("0.2.4a").unwrap_err(), ParseVersionError);
|
|
|
|
}
|
|
|
|
}
|