Skip to content

Pin transitive runtime dependencies, not just the tool

This is a principle

A reusable technical claim: something I would want true in any of my work.

Claim. When a tool drives an external runtime (a browser, an interpreter, a container image), pin that too - not only the tool's own version.

When to apply. Any tool whose behaviour depends on a heavyweight runtime it launches (headless browsers, Docker images, language runtimes). Skip for self-contained binaries.

Why. Pinning the tool but letting it pull "whatever browser is latest" leaves a mutable dependency in your supposedly-reproducible pipeline: the tool version is frozen, yet results change when the runtime updates underneath it. A link-checker that installs "latest Chrome" can start failing (or passing) differently on the same commit. Pin the runtime to an explicit version and tie it to the tool version with a comment so upgrades move together.

Snippet.

# linkspector pinned together with the exact Chrome it drives
entry: bash -c 'npx --yes puppeteer browsers install chrome@148.0.7778.97
                && linkspector check -c .linkspector.yml'
additional_dependencies: ['@umbrelladocs/linkspector@0.5.3', 'puppeteer']

The runtime here is not incidental: linkspector resolves a link it cannot settle with an HTTP request by loading the page in headless Chrome, so the browser is part of what decides the answer.

How enforced. Explicit version pins for the runtime alongside the tool, with a paired comment. Extends the SHA-pinning family: Pin GitHub Actions to full commit SHAs, Pin pre-commit hooks to frozen revisions. Same reproducibility goal, one layer up: Install from a frozen lockfile in CI.

The following pages link to this page: