From 5c424fa6786a2fadfa581f690dec280ffd391151 Mon Sep 17 00:00:00 2001 From: Sylvain Bellemare <125458+sbellem@users.noreply.github.com> Date: Sat, 7 Feb 2026 00:03:15 +0900 Subject: [PATCH] xtask: deterministic builds with SOURCE_DATE_EPOCH When SOURCE_DATE_EPOCH is set, use it instead of Local::now() for the embedded build timestamp. This enables deterministic builds via Guix, Nix, and other reproducible build systems. See: https://reproducible-builds.org/docs/source-date-epoch/ Design notes: - Panics on invalid SOURCE_DATE_EPOCH rather than warn-and-fallback, because silent fallback would produce non-reproducible output while the user expects reproducibility. Build systems should fail loudly on misconfiguration. - Preserves Local::now() fallback when SOURCE_DATE_EPOCH is unset, keeping local timezone for developer convenience during debugging. --- xtask/src/versioning.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/xtask/src/versioning.rs b/xtask/src/versioning.rs index 00d4b7bc6..f8a4fff91 100644 --- a/xtask/src/versioning.rs +++ b/xtask/src/versioning.rs @@ -6,7 +6,7 @@ use std::fs::OpenOptions; use std::io::{Read, Write}; use std::process::Command; -use chrono::Local; +use chrono::{Local, TimeZone, Utc}; pub(crate) fn generate_version(add_timestamp: bool) { let output = if cfg!(target_os = "windows") { @@ -30,13 +30,16 @@ pub(crate) fn generate_version(add_timestamp: bool) { let mut new_data = Vec::new(); print_header(&mut new_data); if add_timestamp { - let now = Local::now(); - write!( - new_data, - "#[allow(dead_code)]\npub const TIMESTAMP: &'static str = \"{}\";\n", - now.to_rfc2822() - ) - .expect("couldn't add our timestamp"); + // Use SOURCE_DATE_EPOCH for reproducible builds if available + // See: https://reproducible-builds.org/docs/source-date-epoch/ + let timestamp_str = if let Ok(sde) = std::env::var("SOURCE_DATE_EPOCH") { + let secs: i64 = sde.parse().expect("SOURCE_DATE_EPOCH must be a valid Unix timestamp"); + Utc.timestamp_opt(secs, 0).single().expect("Invalid SOURCE_DATE_EPOCH timestamp").to_rfc2822() + } else { + Local::now().to_rfc2822() + }; + write!(new_data, "#[allow(dead_code)]\npub const TIMESTAMP: &'static str = \"{}\";\n", timestamp_str) + .expect("couldn't add our timestamp"); } else { write!(new_data, "#[allow(dead_code)]\npub const TIMESTAMP: &'static str = \"omitted\";\n") .expect("couldn't add our timestamp");