Files
ghostty-rs/crates/ghostty-build-info/src/lib.rs
T
ddidderr 8519f1ba1b fix: preserve non-UTF-8 bytes in OS, config and CLI layers
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)
2026-10-12 08:14:02 +02:00

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);
}
}