cli: Add `rad version --json` flag
This will be used to automatically populated metadata around releases for the homepage.
This commit is contained in:
parent
fa4b929a06
commit
93ff59ae25
17
build.rs
17
build.rs
|
|
@ -19,6 +19,23 @@ fn main() {
|
||||||
})
|
})
|
||||||
.unwrap_or(env::var("GIT_HEAD").unwrap_or("unknown".into()));
|
.unwrap_or(env::var("GIT_HEAD").unwrap_or("unknown".into()));
|
||||||
|
|
||||||
|
// Set a build-time `GIT_COMMIT_TIME` env var which includes the commit time.
|
||||||
|
let commit_time = Command::new("git")
|
||||||
|
.arg("show")
|
||||||
|
.arg("--format=%ct")
|
||||||
|
.arg("HEAD")
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|output| {
|
||||||
|
if output.status.success() {
|
||||||
|
String::from_utf8(output.stdout).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(0.to_string());
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=GIT_COMMIT_TIME={commit_time}");
|
||||||
println!("cargo:rustc-env=GIT_HEAD={hash}");
|
println!("cargo:rustc-env=GIT_HEAD={hash}");
|
||||||
println!("cargo:rustc-rerun-if-changed=.git/HEAD");
|
println!("cargo:rustc-rerun-if-changed=.git/HEAD");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,30 @@
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::io;
|
use std::io::{self, Write};
|
||||||
use std::{io::ErrorKind, iter, process};
|
use std::{io::ErrorKind, iter, process};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use radicle::version;
|
use radicle::version::Version;
|
||||||
use radicle_cli::commands::*;
|
use radicle_cli::commands::*;
|
||||||
use radicle_cli::terminal as term;
|
use radicle_cli::terminal as term;
|
||||||
|
|
||||||
pub const NAME: &str = "rad";
|
pub const NAME: &str = "rad";
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub const DESCRIPTION: &str = "Radicle command line interface";
|
pub const DESCRIPTION: &str = "Radicle command line interface";
|
||||||
pub const GIT_HEAD: &str = env!("GIT_HEAD");
|
pub const GIT_HEAD: &str = env!("GIT_HEAD");
|
||||||
|
pub const TIMESTAMP: &str = env!("GIT_COMMIT_TIME");
|
||||||
|
pub const VERSION: Version = Version {
|
||||||
|
name: NAME,
|
||||||
|
version: PKG_VERSION,
|
||||||
|
commit: GIT_HEAD,
|
||||||
|
timestamp: TIMESTAMP,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Other(Vec<OsString>),
|
Other(Vec<OsString>),
|
||||||
Help,
|
Help,
|
||||||
Version,
|
Version { json: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -43,18 +50,24 @@ fn parse_args() -> anyhow::Result<Command> {
|
||||||
|
|
||||||
let mut parser = lexopt::Parser::from_env();
|
let mut parser = lexopt::Parser::from_env();
|
||||||
let mut command = None;
|
let mut command = None;
|
||||||
|
let mut json = false;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
Long("json") => {
|
||||||
|
json = true;
|
||||||
|
}
|
||||||
Long("help") | Short('h') => {
|
Long("help") | Short('h') => {
|
||||||
command = Some(Command::Help);
|
command = Some(Command::Help);
|
||||||
}
|
}
|
||||||
Long("version") => {
|
Long("version") => {
|
||||||
command = Some(Command::Version);
|
command = Some(Command::Version { json: false });
|
||||||
}
|
}
|
||||||
Value(val) if command.is_none() => {
|
Value(val) if command.is_none() => {
|
||||||
if val == *"." {
|
if val == *"." {
|
||||||
command = Some(Command::Other(vec![OsString::from("inspect")]));
|
command = Some(Command::Other(vec![OsString::from("inspect")]));
|
||||||
|
} else if val == "version" {
|
||||||
|
command = Some(Command::Version { json: false });
|
||||||
} else {
|
} else {
|
||||||
let args = iter::once(val)
|
let args = iter::once(val)
|
||||||
.chain(iter::from_fn(|| parser.value().ok()))
|
.chain(iter::from_fn(|| parser.value().ok()))
|
||||||
|
|
@ -66,12 +79,14 @@ fn parse_args() -> anyhow::Result<Command> {
|
||||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(Command::Version { json: j }) = &mut command {
|
||||||
|
*j = json;
|
||||||
|
}
|
||||||
Ok(command.unwrap_or_else(|| Command::Other(vec![])))
|
Ok(command.unwrap_or_else(|| Command::Other(vec![])))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help() -> anyhow::Result<()> {
|
fn print_help() -> anyhow::Result<()> {
|
||||||
version::print(&mut io::stdout(), NAME, VERSION, GIT_HEAD)?;
|
VERSION.write(&mut io::stdout())?;
|
||||||
println!("{DESCRIPTION}");
|
println!("{DESCRIPTION}");
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
|
@ -80,9 +95,16 @@ fn print_help() -> anyhow::Result<()> {
|
||||||
|
|
||||||
fn run(command: Command) -> Result<(), Option<anyhow::Error>> {
|
fn run(command: Command) -> Result<(), Option<anyhow::Error>> {
|
||||||
match command {
|
match command {
|
||||||
Command::Version => {
|
Command::Version { json } => {
|
||||||
version::print(&mut io::stdout(), NAME, VERSION, GIT_HEAD)
|
let mut stdout = io::stdout();
|
||||||
.map_err(|e| Some(e.into()))?;
|
if json {
|
||||||
|
VERSION
|
||||||
|
.write_json(&mut stdout)
|
||||||
|
.map_err(|e| Some(e.into()))?;
|
||||||
|
writeln!(&mut stdout).ok();
|
||||||
|
} else {
|
||||||
|
VERSION.write(&mut stdout).map_err(|e| Some(e.into()))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Command::Help => {
|
Command::Help => {
|
||||||
print_help()?;
|
print_help()?;
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,17 @@ use crossbeam_channel as chan;
|
||||||
use radicle::logger;
|
use radicle::logger;
|
||||||
use radicle::prelude::Signer;
|
use radicle::prelude::Signer;
|
||||||
use radicle::profile;
|
use radicle::profile;
|
||||||
use radicle::version;
|
use radicle::version::Version;
|
||||||
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
||||||
use radicle_node::signals;
|
use radicle_node::signals;
|
||||||
use radicle_node::Runtime;
|
use radicle_node::Runtime;
|
||||||
|
|
||||||
pub const NAME: &str = "radicle-node";
|
pub const VERSION: Version = Version {
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
name: env!("CARGO_PKG_NAME"),
|
||||||
pub const GIT_HEAD: &str = env!("GIT_HEAD");
|
commit: env!("GIT_HEAD"),
|
||||||
|
version: env!("CARGO_PKG_VERSION"),
|
||||||
|
timestamp: env!("GIT_COMMIT_TIME"),
|
||||||
|
};
|
||||||
|
|
||||||
pub const HELP_MSG: &str = r#"
|
pub const HELP_MSG: &str = r#"
|
||||||
Usage
|
Usage
|
||||||
|
|
@ -68,7 +71,7 @@ impl Options {
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
Long("version") => {
|
Long("version") => {
|
||||||
version::print(&mut io::stdout(), NAME, VERSION, GIT_HEAD)?;
|
VERSION.write(&mut io::stdout())?;
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
_ => anyhow::bail!(arg.unexpected()),
|
_ => anyhow::bail!(arg.unexpected()),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use radicle::version;
|
use radicle::version::Version;
|
||||||
|
|
||||||
pub const NAME: &str = "git-remote-rad";
|
pub const VERSION: Version = Version {
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
name: "git-remote-rad",
|
||||||
pub const GIT_HEAD: &str = env!("GIT_HEAD");
|
commit: env!("GIT_HEAD"),
|
||||||
|
version: env!("CARGO_PKG_VERSION"),
|
||||||
|
timestamp: env!("GIT_COMMIT_TIME"),
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut args = env::args();
|
let mut args = env::args();
|
||||||
|
|
@ -14,7 +17,7 @@ fn main() {
|
||||||
radicle::logger::set(radicle::logger::StderrLogger::new(lvl), lvl).ok();
|
radicle::logger::set(radicle::logger::StderrLogger::new(lvl), lvl).ok();
|
||||||
}
|
}
|
||||||
if args.nth(1).as_deref() == Some("--version") {
|
if args.nth(1).as_deref() == Some("--version") {
|
||||||
if let Err(e) = version::print(std::io::stdout(), NAME, VERSION, GIT_HEAD) {
|
if let Err(e) = VERSION.write(std::io::stdout()) {
|
||||||
eprintln!("error: {e}");
|
eprintln!("error: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,41 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
/// Print program version.
|
/// Program version metadata.
|
||||||
///
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
/// The program version follows [semantic versioning](https://semver.org).
|
pub struct Version<'a> {
|
||||||
///
|
pub name: &'a str,
|
||||||
/// Adjust with caution, third party applications parse the string for version info.
|
pub version: &'a str,
|
||||||
pub fn print(
|
pub commit: &'a str,
|
||||||
mut w: impl std::io::Write,
|
pub timestamp: &'a str,
|
||||||
name: &str,
|
}
|
||||||
version: &str,
|
|
||||||
git_head: &str,
|
impl<'a> Version<'a> {
|
||||||
) -> Result<(), io::Error> {
|
/// Write program version as string.
|
||||||
if version.ends_with("-dev") {
|
///
|
||||||
writeln!(w, "{name} {version}+{git_head}")?;
|
/// The program version follows [semantic versioning](https://semver.org).
|
||||||
} else {
|
///
|
||||||
writeln!(w, "{name} {version} ({git_head})")?;
|
/// Adjust with caution, third party applications parse the string for version info.
|
||||||
};
|
pub fn write(&self, mut w: impl std::io::Write) -> Result<(), io::Error> {
|
||||||
Ok(())
|
let Version {
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
commit,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
if version.ends_with("-dev") {
|
||||||
|
writeln!(w, "{name} {version}+{commit}")?;
|
||||||
|
} else {
|
||||||
|
writeln!(w, "{name} {version} ({commit})")?;
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the program version metadata as a JSON value.
|
||||||
|
pub fn write_json(&self, w: impl std::io::Write) -> Result<(), serde_json::Error> {
|
||||||
|
serde_json::to_writer(w, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -26,12 +45,26 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_version() {
|
fn test_version() {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
print(&mut buffer, "rad", "1.2.3", "28b341d").unwrap();
|
Version {
|
||||||
|
name: "rad",
|
||||||
|
version: "1.2.3",
|
||||||
|
commit: "28b341d",
|
||||||
|
timestamp: "",
|
||||||
|
}
|
||||||
|
.write(&mut buffer)
|
||||||
|
.unwrap();
|
||||||
let res = std::str::from_utf8(&buffer).unwrap();
|
let res = std::str::from_utf8(&buffer).unwrap();
|
||||||
assert_eq!("rad 1.2.3 (28b341d)\n", res);
|
assert_eq!("rad 1.2.3 (28b341d)\n", res);
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
print(&mut buffer, "rad", "1.2.3-dev", "28b341d").unwrap();
|
Version {
|
||||||
|
name: "rad",
|
||||||
|
version: "1.2.3-dev",
|
||||||
|
commit: "28b341d",
|
||||||
|
timestamp: "",
|
||||||
|
}
|
||||||
|
.write(&mut buffer)
|
||||||
|
.unwrap();
|
||||||
let res = std::str::from_utf8(&buffer).unwrap();
|
let res = std::str::from_utf8(&buffer).unwrap();
|
||||||
assert_eq!("rad 1.2.3-dev+28b341d\n", res);
|
assert_eq!("rad 1.2.3-dev+28b341d\n", res);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue