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.
This commit is contained in:
Sylvain Bellemare 2026-02-07 00:03:15 +09:00
parent b7db559fd7
commit 5c424fa678
No known key found for this signature in database
GPG key ID: C38AE0ECCAB7D5C8

View file

@ -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");