1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.
//! Metadata about a Materialize build.
//!
//! These types are located in a dependency-free crate so they can be used
//! from any layer of the stack.
/// Build information.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BuildInfo {
/// The version number of the build.
pub version: &'static str,
/// The 40-character SHA-1 hash identifying the Git commit of the build.
pub sha: &'static str,
/// The time of the build in UTC as an ISO 8601-compliant string.
pub time: &'static str,
}
/// Dummy build information.
///
/// Intended for use in contexts where getting the correct build information is
/// impossible or unnecessary, like in tests.
pub const DUMMY_BUILD_INFO: BuildInfo = BuildInfo {
version: "0.0.0+dummy",
sha: "0000000000000000000000000000000000000000",
time: "",
};
/// The target triple of the platform.
pub const TARGET_TRIPLE: &str = env!("TARGET_TRIPLE");
impl BuildInfo {
/// Constructs a human-readable version string.
pub fn human_version(&self, helm_chart_version: Option<String>) -> String {
if let Some(ref helm_chart_version) = helm_chart_version {
format!(
"v{} ({}, helm chart: {})",
self.version,
&self.sha[..9],
helm_chart_version
)
} else {
format!("v{} ({})", self.version, &self.sha[..9])
}
}
/// Returns the version as a rich [semantic version][semver].
///
/// This method is only available when the `semver` feature is active.
///
/// # Panics
///
/// Panics if the `version` field is not a valid semantic version.
///
/// [semver]: https://semver.org
#[cfg(feature = "semver")]
pub fn semver_version(&self) -> semver::Version {
self.version
.parse()
.expect("build version is not valid semver")
}
/// Returns the version as an integer along the lines of Pg's server_version_num
#[cfg(feature = "semver")]
pub fn version_num(&self) -> i32 {
let semver: semver::Version = self
.version
.parse()
.expect("build version is not a valid semver");
let ver_string = format!(
"{:0>2}{:0>3}{:0>2}",
semver.major, semver.minor, semver.patch
);
ver_string.parse::<i32>().unwrap()
}
}
/// Generates an appropriate [`BuildInfo`] instance.
///
/// This macro should be invoked at the leaf of the crate graph, usually in the
/// final binary, and the resulting `BuildInfo` struct plumbed into whatever
/// libraries require it. Invoking the macro in intermediate crates may result
/// in a build info with stale, cached values for the build SHA and time.
#[macro_export]
macro_rules! build_info {
() => {
$crate::BuildInfo {
version: env!("CARGO_PKG_VERSION"),
sha: $crate::__git_sha_internal!(),
time: $crate::__build_time_internal!(),
}
};
}
#[cfg(all(bazel, stamped))]
#[macro_export]
macro_rules! __git_sha_internal {
() => {
$crate::private::bazel_variables::GIT_COMMIT_HASH
};
}
// To improve the effectiveness of remote caching we only stamp "release" builds
// and otherwise side-channel git status through a known file.
//
// See: <https://github.com/bazelbuild/bazel/issues/10075>
#[cfg(all(bazel, not(stamped)))]
#[macro_export]
macro_rules! __git_sha_internal {
() => {
$crate::private::run_command_str!(
"sh",
"-c",
r#"if [ -f /tmp/mz_git_hash.txt ]; then
cat /tmp/mz_git_hash.txt
else
echo "0000000000000000000000000000000000000000"
fi"#
)
};
}
#[cfg(not(bazel))]
#[macro_export]
macro_rules! __git_sha_internal {
() => {
$crate::private::run_command_str!(
"sh",
"-c",
r#"if [ -n "$MZ_DEV_BUILD_SHA" ]; then
echo "$MZ_DEV_BUILD_SHA"
else
# Unfortunately we need to suppress error messages from `git`, as
# run_command_str will display no error message at all if we print
# more than one line of output to stderr.
git rev-parse --verify HEAD 2>/dev/null || {
printf "error: unable to determine Git SHA; " >&2
printf "either build from working Git clone " >&2
printf "(see https://materialize.com/docs/install/#build-from-source), " >&2
printf "or specify SHA manually by setting MZ_DEV_BUILD_SHA environment variable" >&2
printf "If you are using git worktrees, you must be in the primary worktree" >&2
printf "for automatic detection to work." >&2
exit 1
}
fi"#
)
}
}
#[macro_export]
macro_rules! __build_time_internal {
() => {
$crate::private::run_command_str!("date", "-u", "+%Y-%m-%dT%H:%M:%SZ")
};
}
#[doc(hidden)]
pub mod private {
pub use compile_time_run::run_command_str;
// Bazel has a "workspace status" feature that allows us to collect info from
// the workspace (e.g. git hash) at build time. These values get incleded via
// a generated file.
#[cfg(all(bazel, stamped))]
#[allow(unused)]
pub mod bazel_variables {
include!(std::env!("BAZEL_GEN_BUILD_INFO"));
}
}
#[cfg(test)]
mod test {
#[test] // allow(test-attribute)
fn smoketest_build_info() {
let build_info = crate::build_info!();
assert_eq!(build_info.sha.len(), 40);
assert_eq!(build_info.time.len(), 20);
}
}