Repository information in version
This commit is contained in:
parent
8a60f89110
commit
b8fe678ff0
2 changed files with 134 additions and 6 deletions
108
build.rs
Normal file
108
build.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::{absolute, Path, PathBuf};
|
||||||
|
|
||||||
|
/// Fetch the contents of the given file, and also tell cargo that we looked
|
||||||
|
/// in there.
|
||||||
|
fn file_contents<P: AsRef<Path>>(path: P) -> String {
|
||||||
|
let path =
|
||||||
|
absolute(path.as_ref()).expect("Unable to make the path absolute");
|
||||||
|
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
stdout
|
||||||
|
.write_all(b"cargo::rerun-if-changed=")
|
||||||
|
.expect("Unable to write stdout");
|
||||||
|
stdout
|
||||||
|
.write_all(path.as_os_str().as_encoded_bytes())
|
||||||
|
.expect("Unable to write path to stdout");
|
||||||
|
stdout
|
||||||
|
.write_all(b"\n")
|
||||||
|
.expect("Unable to write newline to stdout");
|
||||||
|
|
||||||
|
std::fs::read_to_string(path).expect("Unable to read file")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit the current git commit.
|
||||||
|
fn emit_git_commit() {
|
||||||
|
// Fetch the current commit from the head. We do it this way instead of
|
||||||
|
// asking `git rev-parse` to do it for us because we want to reliably
|
||||||
|
// tell cargo which files it should monitor for changes.
|
||||||
|
let head = file_contents("./.git/HEAD");
|
||||||
|
let rev = if let Some(r) = head.strip_prefix("ref: ") {
|
||||||
|
let mut ref_path = PathBuf::from("./.git/");
|
||||||
|
ref_path.push(r.trim());
|
||||||
|
file_contents(ref_path)
|
||||||
|
} else {
|
||||||
|
head
|
||||||
|
};
|
||||||
|
|
||||||
|
// But *now* we ask git rev-parse to make this into a short hash (a) to
|
||||||
|
// make sure we got it right and (b) because git knows how to quickly
|
||||||
|
// determine how much of a commit is required to be unique. We don't need
|
||||||
|
// to tell cargo anything here, no file that git consults will be
|
||||||
|
// mutable.
|
||||||
|
let output = std::process::Command::new("git")
|
||||||
|
.arg("rev-parse")
|
||||||
|
.arg("--short")
|
||||||
|
.arg(rev.trim())
|
||||||
|
.output()
|
||||||
|
.expect("could not spawn `git` to get the hash");
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = std::str::from_utf8(&output.stderr)
|
||||||
|
.expect("git failed and stderr was not utf8");
|
||||||
|
eprintln!("`git rev-parse --short HEAD` failed, stderr: {stderr}");
|
||||||
|
panic!("`git rev-parse --short HEAD` failed");
|
||||||
|
}
|
||||||
|
let rev =
|
||||||
|
std::str::from_utf8(&output.stdout).expect("git did not output utf8");
|
||||||
|
|
||||||
|
println!("cargo::rustc-env=REPO_REV={rev}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_git_dirty() {
|
||||||
|
// Here is the way to see if anything is up with the repository: run `git
|
||||||
|
// status --porcelain=v1`. The status output in the v1 porcelain format
|
||||||
|
// has one line for every file that's modified in some way: staged,
|
||||||
|
// changed but unstaged, untracked, you name it. Files in the working
|
||||||
|
// tree that are up to date with the repository are not emitted. This is
|
||||||
|
// exactly what we want.
|
||||||
|
//
|
||||||
|
// (Yes, I want to track untracked files, because they can mess with the
|
||||||
|
// build too. The only good build is a clean build!)
|
||||||
|
let output = std::process::Command::new("git")
|
||||||
|
.arg("status")
|
||||||
|
.arg("-z")
|
||||||
|
.arg("--porcelain=v1")
|
||||||
|
.output()
|
||||||
|
.expect("could not spawn `git` to get repository status");
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = std::str::from_utf8(&output.stderr)
|
||||||
|
.expect("git failed and stderr was not utf8");
|
||||||
|
eprintln!("`git status` failed, stderr: {stderr}");
|
||||||
|
panic!("`git status` failed");
|
||||||
|
}
|
||||||
|
let output =
|
||||||
|
std::str::from_utf8(&output.stdout).expect("git did not output utf8");
|
||||||
|
|
||||||
|
// If there *was* any output, parse it and tell cargo to re-run if any of
|
||||||
|
// these files changed. (Maybe they get reverted! Then the repo status
|
||||||
|
// will change.)
|
||||||
|
for line in output.lines() {
|
||||||
|
let fields: Vec<_> = line.split('\x00').collect();
|
||||||
|
let parts: Vec<_> = fields[0].split(' ').collect();
|
||||||
|
let path = parts[1];
|
||||||
|
println!("cargo::rerun-if-changed={path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the repository status.
|
||||||
|
let dirty = if output.trim().is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
" *dirty*"
|
||||||
|
};
|
||||||
|
println!("cargo::rustc-env=REPO_DIRTY={dirty}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
emit_git_commit();
|
||||||
|
emit_git_dirty();
|
||||||
|
}
|
||||||
32
src/main.rs
32
src/main.rs
|
|
@ -2,6 +2,8 @@
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
const REV: &str = env!("REPO_REV");
|
||||||
|
const DIRTY: &str = env!("REPO_DIRTY");
|
||||||
|
|
||||||
fn usage() {
|
fn usage() {
|
||||||
println!(indoc! {"
|
println!(indoc! {"
|
||||||
|
|
@ -85,13 +87,21 @@ fn parse_args(args: Vec<String>) -> Args {
|
||||||
if rest.len() == 2 {
|
if rest.len() == 2 {
|
||||||
Args::Browse(rest[1].to_string())
|
Args::Browse(rest[1].to_string())
|
||||||
} else if rest.len() == 1 {
|
} else if rest.len() == 1 {
|
||||||
Args::Client(rest[0].to_string(), sudo.unwrap_or(false), log_filter.unwrap_or("warn".to_owned()))
|
Args::Client(
|
||||||
|
rest[0].to_string(),
|
||||||
|
sudo.unwrap_or(false),
|
||||||
|
log_filter.unwrap_or("warn".to_owned()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Args::Error
|
Args::Error
|
||||||
}
|
}
|
||||||
} else if rest[0] == "clip" {
|
} else if rest[0] == "clip" {
|
||||||
if rest.len() == 1 {
|
if rest.len() == 1 {
|
||||||
Args::Client(rest[0].to_string(), sudo.unwrap_or(false), log_filter.unwrap_or("warn".to_owned()))
|
Args::Client(
|
||||||
|
rest[0].to_string(),
|
||||||
|
sudo.unwrap_or(false),
|
||||||
|
log_filter.unwrap_or("warn".to_owned()),
|
||||||
|
)
|
||||||
} else if rest.len() == 2 {
|
} else if rest.len() == 2 {
|
||||||
Args::Clip(rest[1].to_string())
|
Args::Clip(rest[1].to_string())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -131,7 +141,7 @@ async fn main() {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
Args::Version => {
|
Args::Version => {
|
||||||
println!("fwd {VERSION}");
|
println!("fwd {VERSION} (rev {REV}{DIRTY})");
|
||||||
}
|
}
|
||||||
Args::Server => {
|
Args::Server => {
|
||||||
fwd::run_server().await;
|
fwd::run_server().await;
|
||||||
|
|
@ -193,7 +203,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn client() {
|
fn client() {
|
||||||
assert_arg_parse!(&["foo.com"], Args::Client( _, false, _));
|
assert_arg_parse!(&["foo.com"], Args::Client(_, false, _));
|
||||||
assert_arg_parse!(&["a"], Args::Client(_, false, _));
|
assert_arg_parse!(&["a"], Args::Client(_, false, _));
|
||||||
assert_arg_parse!(&["browse"], Args::Client(_, false, _));
|
assert_arg_parse!(&["browse"], Args::Client(_, false, _));
|
||||||
assert_arg_parse!(&["clip"], Args::Client(_, false, _));
|
assert_arg_parse!(&["clip"], Args::Client(_, false, _));
|
||||||
|
|
@ -205,10 +215,20 @@ mod tests {
|
||||||
assert_client_parse(&["a"], "a", false, "warn");
|
assert_client_parse(&["a"], "a", false, "warn");
|
||||||
assert_client_parse(&["a", "--log-filter", "info"], "a", false, "info");
|
assert_client_parse(&["a", "--log-filter", "info"], "a", false, "info");
|
||||||
assert_client_parse(&["a", "--log-filter=info"], "a", false, "info");
|
assert_client_parse(&["a", "--log-filter=info"], "a", false, "info");
|
||||||
assert_client_parse(&["a", "--sudo", "--log-filter=info"], "a", true, "info");
|
assert_client_parse(
|
||||||
|
&["a", "--sudo", "--log-filter=info"],
|
||||||
|
"a",
|
||||||
|
true,
|
||||||
|
"info",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_client_parse(x: &[&str], server: &str, sudo: bool, log_filter: &str) {
|
fn assert_client_parse(
|
||||||
|
x: &[&str],
|
||||||
|
server: &str,
|
||||||
|
sudo: bool,
|
||||||
|
log_filter: &str,
|
||||||
|
) {
|
||||||
let args = parse_args(args(x));
|
let args = parse_args(args(x));
|
||||||
assert_matches!(args, Args::Client(_, _, _));
|
assert_matches!(args, Args::Client(_, _, _));
|
||||||
if let Args::Client(s, sdo, lf) = args {
|
if let Args::Client(s, sdo, lf) = args {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue