CI/CD / Integration and candidate foundations / Lesson 4
Reproducible inputs and deterministic packaging
Describe the complete build recipe and distinguish repeatability from verified byte equality.
On this page
Lesson goals
- Identify mutable inputs beyond source code.
- Use a deterministic manifest to explain output comparison.
Source is only one build input
Parcel’s source revision does not completely identify its package. The interpreter or compiler, dependency resolution, build flags, generated resources, locale, timezone, and packaging metadata can all affect output. A successful rerun proves that a process completed again; it does not prove that both runs produced identical bytes. List which artifacts must be reproducible and which environment attributes matter. Logs naturally contain changing timestamps, so excluding logs from the primary artifact comparison is reasonable when the release contract says so explicitly.
Lock resolution and verify acquisition
A lockfile records resolved dependency choices in a format specific to the package manager. It helps prevent an unconstrained version range from selecting different releases, but it does not automatically establish trusted origin, availability, or a deterministic compiler. Review lockfile changes as source changes, use the package manager’s frozen installation mode where supported, and retain integrity metadata. Do not overwrite the lockfile during release just to make installation succeed. That converts a packaging job into an unreviewed dependency update.
Canonicalize a small manifest
The following complete program serializes declared metadata with stable key ordering and separators. Two dictionaries with different insertion orders become identical bytes. This is a narrow demonstration of deterministic encoding, not evidence that Parcel’s compiler or archive format is reproducible. Production formats need an agreed canonicalization scheme and explicit handling of encoding, numeric values, file modes, paths, and timestamps. Hash comparison is meaningful only when both parties know exactly which bytes and algorithm are being compared.
import json
a = {"revision": "lab-r7", "package": "parcel"}
b = {"package": "parcel", "revision": "lab-r7"}
def encode(value):
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8")
print(encode(a).decode())
print(encode(a) == encode(b))
{"package":"parcel","revision":"lab-r7"}
True
Stable serialization removes dictionary insertion order as a source of variation. lab-r7 is a teaching revision label, not a real Git identifier or cryptographic digest.
Find one source of variance at a time
When candidate bytes differ, first compare declared source, dependencies, toolchain, flags, and environment. Then inspect metadata differences such as absolute build paths or embedded build time. A reproducibility investigation should explain the difference rather than simply suppress every timestamp. Some timestamps represent meaningful release content and should be derived from a stable declared input. Rebuilding twice on the same long-lived worker can hide an undeclared local dependency, so independent clean environments provide stronger evidence than two warm reruns.
Declared inputs:
source: lab-r7
dependency lock: reviewed file
toolchain: recorded immutable identity
locale: declared
Compared outputs:
package bytes and manifest bytes
This is an illustrative record of required fields, not a complete build configuration. No fake toolchain digest or successful independent build is supplied. The actual package manager determines how identities and integrity are enforced.
Promote the tested output
Even a reproducible build should not be casually rebuilt between testing and production. Promote the same identified candidate so the runtime adoption refers to the tested bytes. Reproducibility provides an additional way to verify origins and investigate variance; artifact promotion provides continuity through the release path. Keep both ideas. If an environment-specific value must change, inject it through a reviewed runtime configuration contract instead of patching an archive in place. Record configuration identity separately from package identity so failures can be attributed correctly.
Common errors and fixes
The same commit produces a different dependency set.
- Why it happens
- The install step resolves mutable ranges without a reviewed lock.
- How to fix it
- Restore a reviewed resolution and use the manager’s strict installation mode.
- Prevent it next time
- Treat dependency resolution as an explicit change with retained integrity data.
Two successful builds are called reproducible without comparing outputs.
- Why it happens
- Completion status was substituted for byte-level evidence.
- How to fix it
- Declare the artifacts and compare their bytes or cryptographic hashes.
- Prevent it next time
- Include independent output comparison in any reproducibility claim.
Packaging embeds the worker’s absolute directory.
- Why it happens
- The build leaks an incidental local path into the release output.
- How to fix it
- Use supported path normalization or a documented stable build path.
- Prevent it next time
- Investigate environment variation in clean independent builds.
Check your understanding
1. A package changes only because its archive records the current time. Is deleting every timestamp the right fix?
Show worked solution
First decide what the timestamp means. Incidental archive creation time may be normalized to a declared source-derived value; a meaningful product timestamp needs a stable documented input. Verify the resulting artifact semantics and byte equality rather than applying a global deletion rule.
2. Why retain artifact promotion if independent builds are reproducible?
Show worked solution
Promotion directly links the tested candidate to the adopted bytes and avoids additional operational variation. Reproducibility independently strengthens verification and diagnosis. It does not remove the need to know which candidate was tested, approved, and adopted.
Key takeaways
- Lock source, dependency, toolchain, and environment inputs.
- Claim reproducibility only for declared outputs that were compared.