fix(app): Stop panicking at startup when parsing the app version - release blocker (#6888)

* Fix a startup panic in app_version()

* Fix a potential RPC panic in get_info()

* Fix typo
This commit is contained in:
teor 2023-06-09 15:07:34 +10:00 committed by GitHub
parent 286ebdbf8c
commit a18f47d5f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 33 deletions

View File

@ -374,10 +374,10 @@ where
// remove everything after the `+` character if any // remove everything after the `+` character if any
.split('+') .split('+')
.next() .next()
.expect("always at least 1 slice") .expect("always at least 1 slice");
// remove the previously added `v` character at the start since it's not a part of the user agent. // Remove the previously added `v` character at the start since it's not a part of the user agent.
.strip_prefix('v') let release_version = release_version.strip_prefix('v').unwrap_or(release_version);
.expect("we are always expecting the `v` prefix");
let user_agent = format!("/Zebra:{release_version}/"); let user_agent = format!("/Zebra:{release_version}/");
let response = GetInfo { let response = GetInfo {

View File

@ -36,45 +36,98 @@ pub static APPLICATION: AppCell<ZebradApp> = AppCell::new();
/// ///
/// For details, see <https://semver.org/> /// For details, see <https://semver.org/>
pub fn app_version() -> Version { pub fn app_version() -> Version {
// CARGO_PKG_VERSION is always a valid SemVer 2.0 version.
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
let vergen_git_describe: Option<&str> = option_env!("VERGEN_GIT_DESCRIBE");
match vergen_git_describe { // VERGEN_GIT_DESCRIBE should be in the format:
// change the git describe format to the semver 2.0 format // - v1.0.0-rc.9-6-g319b01bb84
Some(mut vergen_git_describe) if !vergen_git_describe.is_empty() => { // - v1.0.0-6-g319b01bb84
// strip the leading "v", if present // but sometimes it is just a short commit hash. See #6879 for details.
if &vergen_git_describe[0..1] == "v" { //
vergen_git_describe = &vergen_git_describe[1..]; // Currently it is the output of `git describe --tags --dirty --match='v*.*.*'`,
} // or whatever is specified in zebrad/build.rs.
const VERGEN_GIT_DESCRIBE: Option<&str> = option_env!("VERGEN_GIT_DESCRIBE");
// split into tag, commit count, hash // We're using the same library as cargo uses internally, so this is guaranteed.
let rparts: Vec<_> = vergen_git_describe.rsplitn(3, '-').collect(); let fallback_version = CARGO_PKG_VERSION.parse().unwrap_or_else(|error| {
panic!(
"unexpected invalid CARGO_PKG_VERSION: {error:?} in {CARGO_PKG_VERSION:?}, \
should have been checked by cargo"
)
});
match rparts.as_slice() { // The SemVer 2.0 format is:
// assume it's a cargo package version or a git tag with no hash // - 1.0.0-rc.9+6.g319b01bb84
[_] | [_, _] => vergen_git_describe.parse().unwrap_or_else(|_| { // - 1.0.0+6.g319b01bb84
panic!( //
"VERGEN_GIT_DESCRIBE without a hash {vergen_git_describe:?} must be valid semver 2.0" // Or as a pattern:
) // - version: major`.`minor`.`patch
}), // - optional pre-release: `-`tag[`.`tag ...]
// - optional build: `+`tag[`.`tag ...]
// change the git describe format to the semver 2.0 format
let Some(vergen_git_describe) = VERGEN_GIT_DESCRIBE else {
return fallback_version;
};
// it's the "git describe" format, which doesn't quite match SemVer 2.0 // Split using "git describe" separators.
[hash, commit_count, tag] => { let mut vergen_git_describe = vergen_git_describe.split('-').peekable();
let semver_fix = format!("{tag}+{commit_count}.{hash}");
semver_fix.parse().unwrap_or_else(|_|
panic!("Modified VERGEN_GIT_DESCRIBE {vergen_git_describe:?} -> {rparts:?} -> {semver_fix:?} must be valid. Note: CARGO_PKG_VERSION was {CARGO_PKG_VERSION:?}."))
}
_ => unreachable!("split is limited to 3 parts"), // Check the "version core" part.
} let version = vergen_git_describe.next();
} let Some(mut version) = version else {
_ => CARGO_PKG_VERSION.parse().unwrap_or_else(|_| { return fallback_version;
panic!("CARGO_PKG_VERSION {CARGO_PKG_VERSION:?} must be valid semver 2.0") };
}),
// strip the leading "v", if present.
version = version.strip_prefix('v').unwrap_or(version);
// If the initial version is empty, just a commit hash, or otherwise invalid.
if Version::parse(version).is_err() {
return fallback_version;
} }
let mut semver = version.to_string();
// Check if the next part is a pre-release or build part,
// but only consume it if it is a pre-release tag.
let Some(part) = vergen_git_describe.peek() else {
// No pre-release or build.
return semver.parse().expect("just checked semver is valid");
};
if part.starts_with(char::is_alphabetic) {
// It's a pre-release tag.
semver.push('-');
semver.push_str(part);
// Consume the pre-release tag to move on to the build tags, if any.
let _ = vergen_git_describe.next();
}
// Check if the next part is a build part.
let Some(build) = vergen_git_describe.peek() else {
// No build tags.
return semver.parse().unwrap_or(fallback_version);
};
if !build.starts_with(char::is_numeric) {
// It's not a valid "commit count" build tag from "git describe".
return fallback_version;
}
// Append the rest of the build parts with the correct `+` and `.` separators.
let build_parts: Vec<_> = vergen_git_describe.collect();
let build_parts = build_parts.join(".");
semver.push('+');
semver.push_str(&build_parts);
semver.parse().unwrap_or(fallback_version)
} }
/// The Zebra current release version. /// The Zebra current release version.
//
// TODO: deduplicate this code with release_version in zebra_rpc::get_info()
pub fn release_version() -> String { pub fn release_version() -> String {
app_version() app_version()
.to_string() .to_string()
@ -89,6 +142,8 @@ pub fn release_version() -> String {
/// This must be a valid [BIP 14] user agent. /// This must be a valid [BIP 14] user agent.
/// ///
/// [BIP 14]: https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki /// [BIP 14]: https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki
//
// TODO: deduplicate this code with the user agent in zebra_rpc::get_info()
pub fn user_agent() -> String { pub fn user_agent() -> String {
let release_version = release_version(); let release_version = release_version();
format!("/Zebra:{release_version}/") format!("/Zebra:{release_version}/")