Deterministic Homebrew Builds: UTC Timestamps vs. HOMEBREW_NO_AUTO_UPDATE
0 reputation · 06 Apr 2021, 20:49 UTC
0 reputation · 06 Apr 2021, 20:49 UTC
The goal is to obtain identical Homebrew formula builds on macOS machines located in different regions. Homebrew formulae are Ruby scripts that may invoke Time.now or the system date command during brew install --build-from-source. These calls depend on the machine’s locale and timezone, which can shift timestamps, alter patch application, or invalidate checksum calculations. Two documented strategies exist: setting HOMEBREW_NO_AUTO_UPDATE=1 to freeze formulae to the current revision, or editing formulas to use UTC‑based timestamps or Time.utc to eliminate timezone dependency. Neither approach is officially endorsed for full determinism, and the impact on checksum verification remains unclear.
HOMEBREW_NO_AUTO_UPDATE reliably prevent date‑driven checksum mismatches across time zones?33275 reputation · 07 Apr 2021, 06:27 UTC
No — HOMEBREW_NO_AUTO_UPDATE=1 does not, by itself, prevent date-driven checksum mismatches across time zones. It freezes the formula revision (stopping brew from pulling newer formulae before the build), but it does nothing to change how the Ruby code inside a formula evaluates Time.now or how a build script invokes the system date command. Those calls still resolve against the machine's local timezone, so two machines in different regions can produce different embedded timestamps — and therefore different artifacts — from the exact same pinned formula.
The reliable combination is: pin the revision and neutralize the timezone at build time, either by editing the formula to use UTC or by scoping TZ=UTC to the build invocation.
HOMEBREW_NO_AUTO_UPDATE=1 — eliminates revision drift. Both machines build from the same formula text. It says nothing about runtime behavior of that text.Time.utc / UTC timestamps — eliminates timezone drift at the source. Robust, but you inherit a maintenance burden: every upstream formula update can reintroduce local-time calls, and your patches may conflict.TZ=UTC on the build command — eliminates timezone drift without touching formula code. Ruby's Time.now and most shells' date honor TZ, so this covers both Ruby-level and subprocess-level time calls in one move.Before changing anything, check whether the formula is even time-sensitive:
grep -nE 'Time\.now|Time\.parse|`date|system "date' \
"$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/<first-letter>/<formula>.rb"
If nothing matches, pinning alone is likely sufficient. If matches exist, build like this on every machine:
HOMEBREW_NO_AUTO_UPDATE=1 TZ=UTC brew install --build-from-source <formula>
Scope TZ to the single command rather than exporting it globally — a session-wide TZ=UTC can surprise other tools. Reserve formula edits (Time.now → Time.utc.now, etc.) for cases where a build script hard-codes local-time assumptions that TZ can't reach, since edited formulae must be re-patched on every version bump.
TZ=UTC, or set the two machines to different timezones.brew list --verbose <formula> and hash the resulting artifacts:shasum -a 256 <built-file>
brew install --verbose) for embedded date strings and patch the offending call.Two honest limits: first, TZ=UTC only helps processes that respect TZ; a build script that reads the hardware clock or calls an API returning local time needs a formula edit. Second, Homebrew's build behavior evolves, so re-verify this workflow against the Homebrew version you actually deploy — treat the grep-and-hash check above as a repeatable gate rather than a one-time fix.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.