-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
all: Add serpent_buildinfo crate and use it from boulder/moss
Adds the serpent_buildinfo crate which performs several common build-metadata related tasks. Also updates boulder and moss to use the functions provided by this library instead of doing it themselves Signed-off-by: Reilly Brogan <[email protected]>
- Loading branch information
1 parent
4ecad5d
commit 9dd084e
Showing
12 changed files
with
317 additions
and
37 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "serpent_buildinfo" | ||
edition.workspace = true | ||
version.workspace = true | ||
rust-version.workspace = true | ||
build = "build.rs" | ||
|
||
[dependencies] | ||
chrono.workspace = true | ||
|
||
[build-dependencies] | ||
chrono.workspace = true | ||
thiserror.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// build.rs | ||
use std::{io, os::unix::ffi::OsStringExt}; | ||
|
||
use chrono::{DateTime, Utc}; | ||
use thiserror::Error; | ||
|
||
/// Returns value of given environment variable or error if missing. | ||
/// | ||
/// This also outputs necessary ‘cargo:rerun-if-env-changed’ tag to make sure | ||
/// build script is rerun if the environment variable changes. | ||
fn env(key: &str) -> Result<std::ffi::OsString, Error> { | ||
println!("cargo:rerun-if-env-changed={}", key); | ||
std::env::var_os(key).ok_or_else(|| Error::Env(key.to_string())) | ||
} | ||
|
||
/// Calls program with given arguments and returns its standard output. If | ||
/// calling the program fails or it exits with non-zero exit status returns an | ||
/// error. | ||
fn command(prog: &str, args: &[&str], cwd: Option<std::path::PathBuf>) -> Result<Vec<u8>, Error> { | ||
println!("cargo:rerun-if-env-changed=PATH"); | ||
let mut cmd = std::process::Command::new(prog); | ||
cmd.args(args); | ||
cmd.stderr(std::process::Stdio::inherit()); | ||
if let Some(cwd) = cwd { | ||
cmd.current_dir(cwd); | ||
} | ||
let out = cmd.output()?; | ||
if out.status.success() { | ||
let mut stdout = out.stdout; | ||
if let Some(b'\n') = stdout.last() { | ||
stdout.pop(); | ||
if let Some(b'\r') = stdout.last() { | ||
stdout.pop(); | ||
} | ||
} | ||
Ok(stdout) | ||
} else if let Some(code) = out.status.code() { | ||
Err(Error::CommandExit(prog.to_string(), code.to_string())) | ||
} else { | ||
Err(Error::CommandKilled(prog.to_string())) | ||
} | ||
} | ||
|
||
/// Checks to see if we're building from a git source and if so attempts to gather information about the git status | ||
fn get_git_info() -> Result<(), Error> { | ||
let pkg_dir = std::path::PathBuf::from(env("CARGO_MANIFEST_DIR")?); | ||
let git_dir = command("git", &["rev-parse", "--git-dir"], Some(pkg_dir.clone())); | ||
let git_dir = match git_dir { | ||
Ok(git_dir) => { | ||
println!("cargo:rustc-cfg=BUILDINFO_IS_GIT_BUILD"); | ||
|
||
std::path::PathBuf::from(std::ffi::OsString::from_vec(git_dir)) | ||
} | ||
Err(msg) => { | ||
// We're not in a git repo, most likely we're building from a source archive | ||
println!("cargo:warning=unable to determine git version (not in git repository?)"); | ||
println!("cargo:warning={}", msg); | ||
|
||
// It's unlikely, but possible that someone could run git init. Might as well catch that. | ||
println!("cargo::rerun-if-changed={}/.git", pkg_dir.display()); | ||
return Ok(()); | ||
} | ||
}; | ||
|
||
// Make Cargo rerun us if currently checked out commit or the state of the | ||
// working tree changes. We try to accomplish that by looking at a few | ||
// crucial git state files. This probably may result in some false | ||
// negatives but it’s best we’ve got. | ||
for subpath in ["HEAD", "logs/HEAD", "index"] { | ||
let path = git_dir.join(subpath).canonicalize()?; | ||
println!("cargo:rerun-if-changed={}", path.display()); | ||
} | ||
|
||
// Get the full git hash | ||
let args = &["rev-parse", "--output-object-format=sha1", "HEAD"]; | ||
let out = command("git", args, None)?; | ||
match String::from_utf8_lossy(&out) { | ||
std::borrow::Cow::Borrowed(full_hash) => { | ||
println!("cargo:rustc-env=BUILDINFO_GIT_FULL_HASH={}", full_hash.trim()); | ||
} | ||
std::borrow::Cow::Owned(full_hash) => return Err(Error::Git(full_hash)), | ||
} | ||
|
||
// Get the short git hash | ||
let args = &["rev-parse", "--output-object-format=sha1", "--short", "HEAD"]; | ||
let out = command("git", args, None)?; | ||
match String::from_utf8_lossy(&out) { | ||
std::borrow::Cow::Borrowed(short_hash) => { | ||
println!("cargo:rustc-env=BUILDINFO_GIT_SHORT_HASH={}", short_hash.trim()); | ||
} | ||
std::borrow::Cow::Owned(short_hash) => return Err(Error::Git(short_hash)), | ||
} | ||
|
||
// Get whether this is built from a dirty tree | ||
let args = &["status", "--porcelain"]; | ||
let out = command("git", args, None)?; | ||
match String::from_utf8_lossy(&out) { | ||
std::borrow::Cow::Borrowed(output) => match output.trim().len() { | ||
0 => {} | ||
_ => println!("cargo:rustc-cfg=BUILDINFO_IS_DIRTY"), | ||
}, | ||
std::borrow::Cow::Owned(output) => return Err(Error::Git(output)), | ||
} | ||
|
||
// Get the commit summary | ||
let args = &["show", "--format=\"%s\"", "-s"]; | ||
let out = command("git", args, None)?; | ||
match String::from_utf8_lossy(&out) { | ||
std::borrow::Cow::Borrowed(summary) => { | ||
println!("cargo:rustc-env=BUILDINFO_GIT_SUMMARY={}", summary.trim()); | ||
} | ||
std::borrow::Cow::Owned(summary) => return Err(Error::Git(summary)), | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_build_time() -> Result<(), Error> { | ||
// Propagate SOURCE_DATE_EPOCH if set | ||
if let Ok(epoch_env) = env("SOURCE_DATE_EPOCH") { | ||
if let Ok(seconds) = epoch_env.to_string_lossy().parse::<i64>() { | ||
if let Some(time) = DateTime::from_timestamp(seconds, 0) { | ||
println!("cargo:rustc-env=BUILDINFO_BUILD_TIME={}", time.timestamp()); | ||
return Ok(()); | ||
} | ||
} | ||
} | ||
|
||
println!("cargo:rustc-env=BUILDINFO_BUILD_TIME={}", Utc::now().timestamp()); | ||
Ok(()) | ||
} | ||
|
||
fn main() { | ||
if let Err(err) = try_main() { | ||
eprintln!("{}", err); | ||
std::process::exit(1); | ||
} | ||
} | ||
|
||
fn try_main() -> Result<(), Error> { | ||
// This should include all top-level directories that contain source code or otherwise modify the build in meaningful ways | ||
let top_level = std::path::PathBuf::from("../..").canonicalize()?; | ||
println!("cargo::rerun-if-changed={}/boulder", top_level.display()); | ||
println!("cargo::rerun-if-changed={}/crates", top_level.display()); | ||
println!("cargo::rerun-if-changed={}/moss", top_level.display()); | ||
println!("cargo::rerun-if-changed={}/test", top_level.display()); | ||
println!("cargo::rerun-if-changed={}/Cargo.toml", top_level.display()); | ||
|
||
let version = env("CARGO_PKG_VERSION")?; | ||
println!("cargo:rustc-env=BUILDINFO_VERSION={}", version.to_string_lossy()); | ||
|
||
get_build_time()?; | ||
|
||
get_git_info()?; | ||
|
||
Ok(()) | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
pub enum Error { | ||
#[error("Missing `{0}` environment variable")] | ||
Env(String), | ||
#[error("{0}: terminated with {1}")] | ||
CommandExit(String, String), | ||
#[error("{0}: killed by signal")] | ||
CommandKilled(String), | ||
#[error("git: invalid output: {0}")] | ||
Git(String), | ||
#[error("io")] | ||
Io(#[from] io::Error), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// SPDX-FileCopyrightText: Copyright © 2020-2024 Serpent OS Developers | ||
// | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
use std::num::ParseIntError; | ||
|
||
use chrono::DateTime; | ||
|
||
mod values; | ||
|
||
/// Returns the version of the project, as defined in the top-level Cargo.toml | ||
/// | ||
/// This will look like "0.1.0" | ||
pub const fn get_version() -> &'static str { | ||
values::VERSION | ||
} | ||
|
||
/// Returns the build time of the project, printed in UTC time format | ||
/// | ||
/// If SOURCE_DATE_EPOCH is set during the build then that will be the timestamp returned | ||
/// | ||
/// This will look like "Tue, 9 Jul 2024 03:28:36 +0000" | ||
pub fn get_build_time() -> Result<String, ParseIntError> { | ||
let time = values::BUILD_TIME.parse::<i64>()?; | ||
let build_time = DateTime::from_timestamp(time, 0).unwrap(); | ||
|
||
Ok(build_time.to_rfc2822()) | ||
} | ||
|
||
/// Returns `true` if the project was built from a git source, `false` otherwise | ||
pub const fn get_if_git_build() -> bool { | ||
cfg!(BUILDINFO_IS_GIT_BUILD) | ||
} | ||
|
||
/// Returns `true` if the project was built from a dirty git source, `false` otherwise | ||
pub const fn get_if_git_dirty() -> bool { | ||
cfg!(BUILDINFO_IS_DIRTY) | ||
} | ||
|
||
/// Returns the git hash that the project was built from if built from a git source | ||
/// | ||
/// This currently returns the SHA1 hash, though eventually it will return the SHA256 one | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub const fn get_git_full_hash() -> &'static str { | ||
values::GIT_FULL_HASH | ||
} | ||
|
||
/// Returns the git hash that the project was built from if built from a git source | ||
/// | ||
/// This currently returns the SHA1 hash, though eventually it will return the SHA256 one | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(not(BUILDINFO_IS_GIT_BUILD))] | ||
pub const fn get_git_full_hash() -> &'static str { | ||
"unknown" | ||
} | ||
|
||
/// Returns the shortened form of the git hash that this project was built from if built from git source | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub const fn get_git_short_hash() -> &'static str { | ||
values::GIT_SHORT_HASH | ||
} | ||
|
||
/// Returns the shortened form of the git hash that this project was built from if built from git source | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(not(BUILDINFO_IS_GIT_BUILD))] | ||
pub const fn get_git_short_hash() -> &'static str { | ||
"unknown" | ||
} | ||
|
||
/// Returns the summary of the git commit that the project was built from | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub const fn get_git_summary() -> &'static str { | ||
values::GIT_SUMMARY | ||
} | ||
|
||
/// Returns the summary of the git commit that the project was built from | ||
/// | ||
/// If built from a non-git source (like a release archive) this will return "unknown" | ||
#[cfg(not(BUILDINFO_IS_GIT_BUILD))] | ||
pub const fn get_git_summary() -> &'static str { | ||
"unknown" | ||
} | ||
|
||
/// For git builds this returns a string like `v0.1.0 (git 4ecad5d7e70c2cdc81350dc6b46fb55b1ccb18f5-dirty)` | ||
/// | ||
/// For builds from a non-git source just the version will be returned: `v0.1.0` | ||
pub fn get_simple_version() -> String { | ||
if cfg!(BUILDINFO_IS_GIT_BUILD) { | ||
let dirty = if get_if_git_dirty() { "-dirty" } else { "" }; | ||
format!("v{} (git {}{})", values::VERSION, get_git_full_hash(), dirty) | ||
} else { | ||
format!("v{}", values::VERSION) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-FileCopyrightText: Copyright © 2020-2024 Serpent OS Developers | ||
// | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
pub(crate) const VERSION: &str = env!("BUILDINFO_VERSION"); | ||
|
||
pub(crate) const BUILD_TIME: &str = env!("BUILDINFO_BUILD_TIME"); | ||
|
||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub(crate) const GIT_FULL_HASH: &str = env!("BUILDINFO_GIT_FULL_HASH"); | ||
|
||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub(crate) const GIT_SHORT_HASH: &str = env!("BUILDINFO_GIT_SHORT_HASH"); | ||
|
||
#[cfg(BUILDINFO_IS_GIT_BUILD)] | ||
pub(crate) const GIT_SUMMARY: &str = env!("BUILDINFO_GIT_SUMMARY"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.