Safeguard
Developer Tools•

How do you add a fresh clone build check to CI?

A fresh clone build catches files and settings that live on one developer machine. Add one CI job that clones clean, installs frozen, builds uncached.

Safeguard Security Team
4 min read

Add one CI job that clones the repository into an empty directory, installs dependencies in frozen mode with no cache, and builds using the same script as releases. If it fails, the build depends on something that is not in the repository. Run it on pull requests that touch build files and on a nightly schedule.

What this check catches

Builds quietly depend on things that never made it into version control: a global tool installed once, an environment variable in someone's profile, a generated file that was never committed, a leftover directory from an earlier build. These pass on the machine where they were created and fail for the next person. The reproducible-builds definition says the relevant build environment, instructions and source are what define a build, so a check that starts from just the source and the written instructions tests the definition directly.

The job, step by step

  1. Start empty. Use a new runner or a clean container. Do not reuse a workspace.
  2. Clone the exact commit. Fetch what the build needs. If your version stamp needs tags or history, fetch those deliberately.
  3. Skip caches on this job. A warm cache hides missing declarations. GitHub's cache action creates a cache on a miss and restores on a key match, so a job with a cache key can pass because of past runs. Use a separate job without it for this check.
  4. Install in frozen mode. For npm, the documentation says the clean install command requires a lockfile, errors if the lockfile and package.json disagree, and deletes any existing node_modules first. Other tools have equivalents.
  5. Run the release build script. Not a shortcut command, the same one that makes releases.
  6. Run the tests.
  7. Hash the outputs and print the hashes in the log.
git clone --no-local . /tmp/fresh && cd /tmp/fresh
./scripts/install-frozen.sh
./scripts/build.sh
sha256sum dist/*

Add a second build for comparison

Once the check is stable, extend it: build twice in the same job in two different directories and compare hashes. Differences show non-determinism such as timestamps or build paths. The reproducible-builds documentation notes that some tools record source paths in output, so a second build in a different directory is a quick way to find those.

Making it fail usefully

FailureLikely cause
Command not foundA global tool is assumed
Import or module errorA dependency is missing from the manifest
Lockfile mismatch errorManifest edited without updating the lockfile
Missing generated fileGenerated code is not produced by the build
Network errorThe build fetches something unpinned

Print the tool versions at the start of the log so a failure points straight at the gap.

Where to run it

  • On pull requests that change build files, lockfiles or the Dockerfile.
  • Nightly on the main branch, so slow drift in external inputs shows up the next morning and not on release day.
  • Before every release.

Try it yourself

Click through it at your own pace, or open it full screen.

Watch how a pull request is checked before merge. The fresh clone build is one more gate in that same position.

Where Safeguard fits

Safeguard policies and gates can check pull requests for dependency risk alongside your build job. Free scan: gold.safeguard.sh/scan.

Quick check

Will this slow down every pull request?

It can. Restrict it to pull requests that touch build files, and run the full version nightly.

Is a container enough for a clean environment?

A fresh container is usually enough, as long as you do not mount host caches or reuse volumes.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.