Skip to content

build: pin every dependency to an exact version, including runtime tool installs - #77

Merged
jimisola merged 2 commits into
mainfrom
build/pin-all-dependencies
Aug 17, 2026
Merged

build: pin every dependency to an exact version, including runtime tool installs#77
jimisola merged 2 commits into
mainfrom
build/pin-all-dependencies

Conversation

@jimisola

@jimisola jimisola commented Aug 17, 2026

Copy link
Copy Markdown
Member

Corrected after @jimisola asked "are all dependencies in all repos pinned now?" — they were not. My first survey only covered package manifests and missed a whole class.

What & Why

Org-wide policy: no ranges, in any repo, in any ecosystem. A range means the version you get depends on when you install — CI, a laptop and a release build can each resolve differently — and a bad upstream release lands without a PR anyone reviewed.

rangeStrategy: "pin" makes Renovate raise a Pin dependencies PR for anything that regresses. Every upgrade still happens; each is now a reviewable PR that CI must pass.

Two classes, not one

Manifests — surveyed first. Already almost entirely pinned:

Ecosystem State
Maven, Gradle exact everywhere, no ranges
Python 1 range — fixed in reqstool-client#440
npm 3 carets — fixed in reqstool-vscode#103

Runtime tool installs — the class I missed. pip install <name> / npm install -g <name> / docker run <image> resolve to whatever is newest when the job runs. They execute in CI and some feed a publish.

The worst was docker run renovate/renovate with no tag at all:latest on every run, so that job could change behaviour with no commit anywhere.

Pinned here: renovate/renovate 44.32.5, zizmor 1.29.0, yamllint 1.38.0, PyYAML 6.0.3, hatch 1.18.0, twine 7.0.0, @fission-ai/openspec 1.9.0. Companion PRs do the same in client#441, decorators#89, hatch-plugin#100, ai#38.

Every pin carries a # renovate: comment, so it is tracked, not merely frozen — the distinction that mattered when Renovate silently skipped the Nisse pin.

One deliberate exception

install-reqstool stays unpinned, and now says why: the build matrix runs it with reqstool-source: [pypi, main] precisely to test against whatever reqstool currently releases. Same for reqstool-regression#35.

A near-miss worth recording

My first attempt annotated the docker image with an inline `# renovate: …` comment. In a shell block backticks are command substitution — bash would have tried to execute the comment. Replaced with a VERSION="44.32.5" variable, which also reuses the existing matchString instead of needing a third pattern.

Author checklist

  • renovate-config-validator passes
  • All 10 annotations verified discovered by the manager regexes
  • actionlint, zizmor, yamllint, permissions check, action tests clean
  • Conventional Commit title, DCO sign-off

Org-wide policy: no ranges, in any repo, in any ecosystem. A range means the
version you get depends on when you install -- CI, a laptop and a release build
can each resolve differently -- and a bad upstream release lands without a PR
anyone reviewed.

Renovate now raises a "Pin dependencies" PR for anything that regresses to a
range, and every upgrade arrives as a reviewable PR that CI has to pass. That is
the point rather than the cost: the same number of upgrades, each one visible.

A survey found the repos were already almost entirely pinned -- Maven and Gradle
had no ranges at all, and only four dependencies across two repos needed
changing (reqstool-vscode#103, reqstool-client#440). This keeps them that way.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
The earlier survey only looked at package manifests, and missed a whole class of
dependency: tools installed inside a workflow. They execute in CI and some of
them feed a publish, so an unpinned one is exactly the risk pinning exists to
remove.

Worst of them was `docker run renovate/renovate` with no tag at all -- :latest on
every run, so that job's behaviour could change with no commit anywhere. Now
44.32.5.

Also pinned: zizmor 1.29.0, yamllint 1.38.0, PyYAML 6.0.3, hatch 1.18.0, twine
7.0.0, @fission-ai/openspec 1.9.0. Each carries a `# renovate:` comment, so they
are tracked rather than merely frozen -- the distinction the four earlier pins
were about.

The custom manager gains one matchString for the inline `tool==1.2.3` and
`pkg@1.2.3` forms. The docker image reuses the existing `VERSION="..."` form via
a variable, rather than a third pattern: an inline comment there would have
needed backticks, which bash would have run as command substitution.

install-reqstool stays unpinned on purpose, and now says so. The build matrix
runs it with reqstool-source: [pypi, main] precisely to test against whatever
reqstool currently releases; pinning it would defeat the check it exists for.

Verified all ten annotations are discovered by the manager's regexes.

Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
@jimisola jimisola changed the title build(renovate): pin every dependency to an exact version build: pin every dependency to an exact version, including runtime tool installs Aug 17, 2026
@jimisola
jimisola merged commit 82c0854 into main Aug 17, 2026
12 checks passed
@jimisola
jimisola deleted the build/pin-all-dependencies branch August 17, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant