Skip to main content

hermit/
build_info.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9//! Producer values for the shared machine-readable build record.
10
11#![allow(
12    unexpected_cfgs,
13    reason = "`fbcode_build` is supplied by the internal Buck build"
14)]
15
16pub use detcore_model::build_info::BuildFeatures;
17pub use detcore_model::build_info::BuildInfo;
18
19#[cfg(feature = "buck-release-provenance")]
20const fn is_ascii_hex(byte: u8) -> bool {
21    byte.is_ascii_hexdigit()
22}
23
24#[cfg(feature = "buck-release-provenance")]
25const fn valid_release_sha(value: &str) -> bool {
26    let bytes = value.as_bytes();
27    let valid_length = bytes.len() == 12
28        || (bytes.len() == 18
29            && bytes[12] == b'-'
30            && bytes[13] == b'd'
31            && bytes[14] == b'i'
32            && bytes[15] == b'r'
33            && bytes[16] == b't'
34            && bytes[17] == b'y');
35    if !valid_length {
36        return false;
37    }
38    let mut index = 0;
39    while index < 12 {
40        if !is_ascii_hex(bytes[index]) {
41            return false;
42        }
43        index += 1;
44    }
45    true
46}
47
48#[cfg(feature = "buck-release-provenance")]
49const fn valid_release_date(value: &str) -> bool {
50    let bytes = value.as_bytes();
51    if bytes.len() != 10 || bytes[4] != b'-' || bytes[7] != b'-' {
52        return false;
53    }
54    let mut index = 0;
55    while index < bytes.len() {
56        if index != 4 && index != 7 && !bytes[index].is_ascii_digit() {
57            return false;
58        }
59        index += 1;
60    }
61    true
62}
63
64#[cfg(feature = "buck-release-provenance")]
65const fn valid_release_version(value: &str) -> bool {
66    let bytes = value.as_bytes();
67    if bytes.is_empty()
68        || (bytes.len() == 7
69            && bytes[0] == b'u'
70            && bytes[1] == b'n'
71            && bytes[2] == b'k'
72            && bytes[3] == b'n'
73            && bytes[4] == b'o'
74            && bytes[5] == b'w'
75            && bytes[6] == b'n')
76    {
77        return false;
78    }
79    let mut index = 0;
80    while index < bytes.len() {
81        let byte = bytes[index];
82        if !(byte.is_ascii_alphanumeric() || byte == b'.' || byte == b'-' || byte == b'+') {
83            return false;
84        }
85        index += 1;
86    }
87    true
88}
89
90#[cfg(feature = "buck-release-provenance")]
91const fn valid_reverie_pin(value: &str) -> bool {
92    let bytes = value.as_bytes();
93    if bytes.len() != 40 {
94        return false;
95    }
96    let mut index = 0;
97    while index < bytes.len() {
98        if !is_ascii_hex(bytes[index]) {
99            return false;
100        }
101        index += 1;
102    }
103    true
104}
105
106// A release target is unsafe when invoked without the provenance wrapper. Keep
107// this assertion in the Rust compilation itself so a direct Buck invocation
108// cannot silently emit an empty/unknown release identity.
109#[cfg(feature = "buck-release-provenance")]
110const _: () = {
111    assert!(valid_release_version(env!("CARGO_PKG_VERSION")));
112    assert!(valid_release_date(env!("HERMIT_BUILD_DATE")));
113    assert!(valid_release_sha(env!("HERMIT_BUILD_GIT_SHA")));
114    assert!(valid_reverie_pin(env!("HERMIT_REVERIE_PIN")));
115};
116
117/// Construct the record from values embedded in this binary.
118///
119/// `hermit --version` remains presentation text and must not be parsed for
120/// provenance or feature decisions.
121pub fn current() -> BuildInfo {
122    #[cfg(fbcode_build)]
123    let (version, build_date, git_sha) = {
124        use build_info::BuildInfo as FbBuildInfo;
125
126        let revision = Some(FbBuildInfo::get_revision().to_owned())
127            .filter(|value| !value.is_empty())
128            .unwrap_or_else(|| "unknown".to_owned());
129        let package = Some(FbBuildInfo::get_package_version().to_owned())
130            .filter(|value| !value.is_empty())
131            .unwrap_or_else(|| "unknown".to_owned());
132        (package, None, revision)
133    };
134
135    #[cfg(not(fbcode_build))]
136    let (version, build_date, git_sha) = (
137        env!("CARGO_PKG_VERSION").to_owned(),
138        Some(env!("HERMIT_BUILD_DATE").to_owned()),
139        env!("HERMIT_BUILD_GIT_SHA").to_owned(),
140    );
141
142    BuildInfo {
143        schema: BuildInfo::SCHEMA,
144        version,
145        build_date,
146        git_sha,
147        features: BuildFeatures {
148            dbt: false,
149            e9patch: false,
150            sabre: false,
151        },
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn current_build_info_round_trips_through_the_closed_schema() {
161        let expected = current();
162        let bytes = serde_json::to_vec(&expected).unwrap();
163        let observed: BuildInfo = serde_json::from_slice(&bytes).unwrap();
164
165        assert_eq!(observed, expected);
166        assert_eq!(observed.schema, BuildInfo::SCHEMA);
167    }
168}