same uses Nix as its underlying package manager to guarantee that builds run in a strictly hermetic environment. This ensures that a build running on one developer’s machine behaves exactly the same as on another’s, or in CI, effectively solving “it works on my machine” problems.
The core component responsible for this is the Environment Factory (internal/adapters/nix/env_factory.go).
The Environment Factory
TheEnvFactory is responsible for translating a high-level list of required tools (e.g., go, nodejs) into a set of environment variables (like PATH, GOPATH, etc.) that define the execution context for a task.
The process involves four main steps:
- Tool Resolution: converting aliases to precise commits.
- Nix Expression Generation: Creating a request for a Nix shell.
- Environment Extraction: Running Nix to get the environment variables.
- Caching: Storing the result to speed up future runs.
1. Tool Resolution
Insame.yaml, users define tools with simple aliases and versions:
- It uses a
DependencyResolverto query the Nix database. - Resolutions happen concurrently (using an error group with a limit based on CPU cores).
- Each tool resolves to a
CommitHash(e.g.,8d15...) and anAttributePath(e.g.,go_1_25).
[email protected] always points to the exact same bytes of software, regardless of when it is run.
2. Nix Expression Generation
Once the tools are resolved, the factory generates a temporary.nix file (a Nix Expression) that defines a shell environment.
The generator (generateNixExpr):
- Sorts all inputs to ensure the generated file is deterministic.
- Uses
builtins.getFlaketo fetch the specificnixpkgsrevisions resolved in the previous step. - Constructs a
mkShellderivation that includes all requested packages in itsbuildInputs.
3. Environment Extraction
To get the actual environment variables without spawning an interactive shell,same executes:
PATH including the bin directories of the resolved tools).
The factory parses this JSON and filters the variables:
- Included: Build-critical variables (e.g.,
PATH,CC, library paths). - Excluded: Interactive shell variables (e.g.,
TERM,SHELL,HOME,USER) to prevent leaking the host user’s configuration into the build.
same appends its own overrides to enforce hermeticity, such as setting GOCACHE and forcing TMPDIR to a safe location.
4. Caching
New environments are expensive to resolve and evaluate. To mitigate this,EnvFactory uses:
- Singleflight: Ensures that if multiple concurrent tasks request the exact same environment, it is computed only once.
- Persistent Cache: Results are stored in
cache/environments/ENV_ID.json, whereENV_IDis a hash of the requested tools. If a task requests a known environment, it is loaded instantly from disk.