Zig treats paths, arguments and environment values as byte slices; the port converted them lossily in many places. Now (final-Z1.md): - argv, env (incl. present non-UTF-8 values), HOME, PATH lookup, passwd, resources dir, TMPDIR, cgroup, config path canonicalization, +validate-config, +list-themes and Zig-string escapes (\xFF) keep raw bytes. String-based config values carry bytes via ghostty_os::osbytes, a bijective escape (noncharacter U+FDD0 + byte) that leaves all valid UTF-8 - including Nerd Font private-use icons - unchanged. - os/file rlimit fallbacks exist on all targets. - i18n locale canonicalization is an exact port of libintl's tables (e.g. pa-Guru -> pa_IN). - New ghostty-build-info crate is the single source of version, channel, commit and build mode for all consumers. Test Plan: - cargo test -p ghostty-os -p ghostty-config -p ghostty-cli (incl. randomized osbytes round-trip property tests)
107 lines
3.2 KiB
Rust
107 lines
3.2 KiB
Rust
//! Single source of truth for the build version metadata (Zig:
|
|
//! `build_config.zig` `version`, `version_string`, `release_channel`, plus
|
|
//! `builtin.mode`).
|
|
//!
|
|
//! Everything is derived at compile time by this crate's `build.rs` (git
|
|
//! describe / workspace `app-version`, overridable with the
|
|
//! `GHOSTTY_VERSION_STRING` environment variable). Consumers (XTVERSION,
|
|
//! `TERM_PROGRAM_VERSION`, the About dialog, crash metadata, `+version`) must
|
|
//! use these constants instead of hardcoding a version.
|
|
|
|
/// `build_config.version_string`, e.g. `1.3.1` for tagged releases or
|
|
/// `1.3.1-main+abc1234` for development builds.
|
|
pub const VERSION_STRING: &str = env!("GHOSTTY_VERSION_STRING");
|
|
|
|
/// The git commit hash (`build_config.version.build`), if the version has
|
|
/// one (tagged releases do not).
|
|
pub const COMMIT: Option<&str> = {
|
|
let c = env!("GHOSTTY_COMMIT");
|
|
if c.is_empty() { None } else { Some(c) }
|
|
};
|
|
|
|
/// `build_config.release_channel` tag name: `stable` or `tip`.
|
|
pub const RELEASE_CHANNEL: &str = env!("GHOSTTY_RELEASE_CHANNEL");
|
|
|
|
/// `{}` of `builtin.mode` (`.Debug`, `.ReleaseSafe`, `.ReleaseFast`).
|
|
pub const BUILD_MODE: &str = env!("GHOSTTY_BUILD_MODE");
|
|
|
|
/// A semantic version (Zig: `std.SemanticVersion`).
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct SemanticVersion<'a> {
|
|
pub major: u64,
|
|
pub minor: u64,
|
|
pub patch: u64,
|
|
/// Pre-release part (after `-`), e.g. `main`.
|
|
pub pre: Option<&'a str>,
|
|
/// Build metadata (after `+`), e.g. the commit hash.
|
|
pub build: Option<&'a str>,
|
|
}
|
|
|
|
/// Parse `X.Y.Z[-pre][+build]`.
|
|
pub fn parse(v: &str) -> Option<SemanticVersion<'_>> {
|
|
let (rest, build) = match v.split_once('+') {
|
|
Some((a, b)) => (a, Some(b)),
|
|
None => (v, None),
|
|
};
|
|
let (core, pre) = match rest.split_once('-') {
|
|
Some((a, b)) => (a, Some(b)),
|
|
None => (rest, None),
|
|
};
|
|
let mut it = core.split('.');
|
|
let major = it.next()?.parse().ok()?;
|
|
let minor = it.next()?.parse().ok()?;
|
|
let patch = it.next()?.parse().ok()?;
|
|
if it.next().is_some() {
|
|
return None;
|
|
}
|
|
Some(SemanticVersion {
|
|
major,
|
|
minor,
|
|
patch,
|
|
pre,
|
|
build,
|
|
})
|
|
}
|
|
|
|
/// `build_config.version` parsed from [`VERSION_STRING`].
|
|
pub fn version() -> Option<SemanticVersion<'static>> {
|
|
parse(VERSION_STRING)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#![allow(clippy::unwrap_used)]
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn version_string_is_semver() {
|
|
let v = version().expect("derived version must be semantic");
|
|
assert!(v.major >= 1);
|
|
assert_eq!(v.build, COMMIT.filter(|_| v.build.is_some()));
|
|
}
|
|
|
|
#[test]
|
|
fn channel_matches_prerelease() {
|
|
let v = version().unwrap();
|
|
assert_eq!(RELEASE_CHANNEL == "tip", v.pre.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn parse_cases() {
|
|
assert_eq!(
|
|
parse("1.3.1"),
|
|
Some(SemanticVersion {
|
|
major: 1,
|
|
minor: 3,
|
|
patch: 1,
|
|
pre: None,
|
|
build: None
|
|
})
|
|
);
|
|
let v = parse("1.4.0-main+abc1234").unwrap();
|
|
assert_eq!((v.pre, v.build), (Some("main"), Some("abc1234")));
|
|
assert_eq!(parse("1.3"), None);
|
|
assert_eq!(parse("1.3.1.2"), None);
|
|
}
|
|
}
|