A lock file and a version fix themselves only where their tool refuses them¶
- Status: accepted
- Date: 2026-10-25
Context¶
The fix bot (decision 0023) commits what the commands of autofix in .github/checks.toml change when a check fails. A check of scripts/check.sh runs the commands of every one of its steps, not only of the step that failed, on the head of the pull request and with the tools of the check. Four checks had no fix yet (#225): the version of the release bot, which the step Version compares with .release-please-manifest.json, the lock files of Composer and of npm, and the licenses of REUSE.
A lock file depends on the version of the tool that writes it: Composer writes its own plugin-api-version, and another npm can add or drop fields. Renovate writes the lock files of its pull requests with its own versions. composer update --lock, which the explanation of the step Composer named, only writes the hash of composer.json into the lock file: a package that composer.json adds stays missing, and composer install still stops.
Options¶
- Lock again whenever a check fails. Every failed check of a Nextcloud app or a Node package would commit a lock file of another Composer or npm, a change that has nothing to do with what failed.
- Lock again only where the tool itself refuses the lock file, and then with the least change.
composer validatewithout--strictfails only for a brokencomposer.jsonor a lock file out of date, andnpm ci --dry-runfails only for a lock file thatnpm cirefuses, without changing anything, not evennode_modules.composer update --minimal-changes --no-installkeeps every other package at its version and adds and drops whatcomposer.jsonasks for, andnpm install --package-lock-onlydoes the same for npm. - As 2, and REUSE fixes itself with
reuse annotatefor a file without a header.
Decision¶
Option 2.
- The version:
python3 .github/blueprint.py fix versiontakes the files fromrelease-please-config.json, those of its release type and itsextra-files, and writes the version of the manifest at the place where the step Version reads it, the first match of its pattern, without changing any other byte of the file. A file without a version at that place stays for a person. A test renders every kind of project and checks that the fix writes exactly the files that the step checks, and that the step passes after it. - Composer: the autofix of the step Composer runs
composer update --minimal-changes --no-install --no-scripts --no-plugins --no-interactiononcecomposer validate --no-check-publishfails, without the scripts and plugins of the packages, as Renovate locks them. - npm: a new step, Package lock, the first one of a Node package, runs
npm ci --dry-run, so thatscripts/check.shfinds such a lock file on your machine as well. Its autofix runsnpm install --package-lock-only --ignore-scriptsonce the dry run fails. In the CI, thenpm ciof the workflow stops beforescripts/check.sh, and the fix of the check runs all the same. - REUSE: no command.
REUSE.tomlgives every file the license of the project withpath = "**", so the tool finds only a header of a file's own that names a license whose textLICENSES/lacks or one that is no identifier of SPDX, or a text inLICENSES/that no file names. Its fix adds a file toLICENSES/or deletes one, which the bot never does, and the license of a file is the decision of a person. - Prettier and ESLint, which #225 named for Node, are Biome in the blueprint (decision 0043), whose
biome check --writealready fixes the format and the safe rules.
Consequences¶
A pull request that changes composer.json or package.json by hand, or a version, passes after one commit of the bot. Every other lock file stays as Renovate wrote it. The lock file that the bot writes comes from the Composer or the npm of the CI, which can differ from that of Renovate; Renovate writes it its own way again with its next update. A check that fails for another reason runs the dry run of npm or the validation of Composer once more, a second or two.
The manifest decides: a pull request that changes .release-please-manifest.json by hand gets the version of every file changed to match. Only the release bot changes the manifest, in its own pull request, which writes every file at once and so never needs the fix. The cost: a manifest changed by hand passes after the fix instead of failing for a person to look; a fix that leaves the files alone when the pull request changed the manifest, from the manifest of the base, follows in #429.
The other points of #225 need designs of their own and follow in #429: a check of the lock files of pip-compile, which nothing checks yet, the draft of an entry of the changelog, a new file that the bot never commits, the title that the PR coach suggests, a /fix of a maintainer on a fork, a weekly run of the fixers on main, the fixes of the workflows of a project of its own, runs that end after the bot, and gitIgnoredAuthors for Renovate.