← Dev Log
BallOrcs1 min read

First light: a bit-identical Linux build

By the end of day one, the factory produced its first game package — and then produced it again, from scratch, byte-for-byte identical.

In plain words

By the end of day one, the factory produced its first game package — and then produced it again, from scratch, byte-for-byte identical. That identical-twin test matters: it proves nothing random or hidden sneaks into the game we ship, which makes every future bug easier to trace. Getting there meant fixing a series of "the export works on my machine except it doesn't" problems, including one where the engine crashed after finishing its work, like a chef fainting after plating a perfect dish.

Under the hood

Three export-stage failures, all reproduced then fixed:

  • The headless editor only reads export templates from ~/.local/share/godot/export_templates/<version>/ under canonical names — our from-source template builds get installed there by the pipeline.
  • The editor always runs with the debug feature, so it cannot load a release-only GDExtension while exporting; the host build now always produces a template_debug variant too.
  • Headless export needs fully explicit export_presets.cfg (filter keys and options sections included) — the interactive editor papers over gaps that the CLI errors on.

Then the determinism audit: two full cold builds at different filesystem path lengths produced bit-identical artifacts, and re-exporting reproduced the same .pck hash. Exactly one source of nondeterminism turned up: text .tscn scenes without persisted node ids are assigned fresh random node_ids at export-time binary conversion. The only affected scenes were the vendored Sentry user_feedback UI, which we don't use — so every preset excludes addons/sentry/user_feedback/* instead of mutating vendored files.

One tolerated wart, documented rather than hidden: the headless editor intermittently SIGABRTs during shutdown, after savepack DONE and with all artifacts present and correctly sized. The export wrapper now treats a nonzero editor exit as fatal only when the artifact is missing, and warns loudly otherwise — identical artifacts either way.

#builds#determinism#war-story