fix(release): fail when a package's versioned file does not exist - #1064
Conversation
SonarQube — aucune nouvelle issueComparaison entre le projet bac à sable de cette PR et la branche par défaut : SonarQube Community n'analyse pas les PR, ce delta est calculé côté CI. Détail |
There was a problem hiding this comment.
The direction is right and the scoping argument holds, but the check does not land where the description says it does: placed above the skip returns, it also fails packages the run would never write. One blocking finding on that, plus two nits with suggestions.
There was a problem hiding this comment.
Follow-up on 880f992. The modifies_file() gate is sound: modifies_file already exists on the VersionFile trait with a true default and only gomod overrides it, and formats/mod.rs has tests pinning both sides of that, so the gate cannot silently widen. One nit on the test it ships with.
|
Right, and it is the exact failure mode I have been trying to avoid elsewhere in this PR. Applied: it now asserts Both guards are needed and neither is redundant: the |
ea886b6 to
bf0ac5b
Compare
There was a problem hiding this comment.
bf0ac5b resolves the blocking finding: the check now runs immediately before Ok(PackagePlan::Bump(...)), so a stale versionedFiles entry only fails the packages this run would actually write, and the new chore-only test pins that position. Both nits are addressed too. One new nit on the ./ prefix in the hint, not worth holding the PR for.
The multi-versioned-files fixture is out of scope here as described, and the Fixtures pin bump is the right sequencing.
| } | ||
|
|
||
| fn suggested_versioned_path(pkg: &PackageConfig, path: &str) -> Option<String> { | ||
| let prefix = pkg.path.trim_end_matches('/'); |
There was a problem hiding this comment.
Nit: the prefix is trimmed at the end but not at the start, unlike every other reader of pkg.path (PackageConfig::is_touched_by, why/mod.rs, run/mod.rs all do .trim_start_matches("./").trim_end_matches('/'), and config load does not normalise it). With path = "./api", Path::new("api/Cargo.toml").starts_with("./api") is false because the leading . survives as a CurDir component, so a correct config gets Did you mean "./api/api/Cargo.toml"?. Hint text only, but it points at a path that does not exist.
| let prefix = pkg.path.trim_end_matches('/'); | |
| let prefix = pkg.path.trim_start_matches("./").trim_end_matches('/'); |
bf0ac5b to
9bf544e
Compare
There was a problem hiding this comment.
5587f2d is sound, and it removes the Fixtures-side dependency the description was still blocked on. Checked against the pinned generator (FerrLabs/Fixtures@7b04ba1, v1.2.2): hooks[] is a generic file writer there, not script-only, and generate_explicit writes hook files before add_all_and_commit("chore: initial setup"), so version2.toml lands in the initial commit and under v1.0.0. Both versioned files now read 1.0.0, the feat: commit still yields the expected minor to 1.1.0, and the fixtures- cache key hashes tests/fixtures/definitions/**, so the fixture is regenerated rather than served stale.
The fixture jobs had not started on this head when I looked, so Fixtures — Config & Formats is reasoned about, not observed green.
Nit: the generator chmods every hooks[] entry 0755, so version2.toml is committed executable. Harmless here since run-tests.sh only runs check and greps stdout, and hooks[] is the only seeding mechanism v1.2.2 offers, but it is worth a word in the definition or in the PR body so the next reader does not take the mode as meaningful.
The description's "Known red check: multi-versioned-files" section is now stale and reads as if the PR is still blocked on a Fixtures release.
## [7.21.3] - 2026-09-16 ### Bug Fixes - fix(release): fail when a package's versioned file does not exist (#1064)
Closes #983.
compute_planread the package's first versioned file withread_version(...).ok(), so a path that does not exist becameNoneand planning carried on.checkthen reported a clean plan, exit 0, for a configuration that cannot be released. The only signal was the missingover <file>suffix on that package's line, which is exactly what nobody notices in a list of packages.Correcting the issue's stated impact
The issue says the release "runs, tags are created and the changelog is written, but the version file is never bumped". That is not what happens, at least not for a format that writes the file. Running a real
releaseon the issue's own repro againstmain:write_versionreads the file before editing it, so it fails and no tag is created. There is no silent drift and nothing to clean up afterwards.What is real is narrower and still worth fixing:
checkdisagrees withrelease. A dry run whose whole job is to say what a release will do reports success for somethingreleaserefuses, and when it does refuse, the message is a bare read error with no hint about the path convention. This PR makescheckanswer the question it was asked, at the point where the answer is useful.The error message and the E1024 docs are worded to that, rather than to the tag-drift claim.
Two scoping choices
Only packages this run would write. The check sits in
compute_planafter the excluded and not-touched early returns, rather than running across every configured package the wayvalidatedoes. A repository-wide check turns a partial or sparse checkout into a hard failure: a job that checks out only the packages it touches would stop being able to release any of them.validateanddoctorstill cover every configured package, which is their job.Only formats that write the file.
gomodis the case that matters: a Go module's version lives in the git tag,read_versiongets it fromgit describe, andwrite_versionis a no-op. A missinggo.modcannot cause the failure this check exists to catch, and theformat-gomodfixture is a real repository that has nogo.modat all. Caught by CI, which is exactly what that fixture is for.Tests
Four, on real git fixtures:
a_versioned_file_that_does_not_exist_fails_the_plan_rather_than_bumping_nothingreproduces the issue's config verbatim and asserts the error namesapi/Cargo.toml.a_versioned_file_that_exists_still_plans_normallyis the control, so the check cannot pass by rejecting everything.an_untouched_package_is_skipped_before_its_files_are_checkedpins the first scoping choice.a_format_that_never_writes_the_file_does_not_need_it_to_existpins the second, and asserts the fixture really has nogo.modfirst so it cannot pass vacuously.Known red check:
multi-versioned-filesFixtures — Config & Formatsis still failing on that one fixture, and the fixture is wrong rather than the change.multi-versioned-files.jsondeclares two toml versioned files,version.tomlandversion2.toml. The generator only ever writes oneversion.tomlper package, soversion2.tomlhas never existed. The fixture asserts thatcheckplans this repository fine, whileferrflow releaseon it would fail atversion2.tomlexactly as above. This PR makescheckagree withrelease, so the fixture's expectation no longer holds.It cannot be fixed from this repo.
PackageDefin the generator has noversioned_filesfield, so the block thatformat-gomod.jsonalready carries is silently dropped by serde. Making the fixture honest means:versioned_filestoPackageDefand materialising them,ci.yml(currently7b04ba1/ v1.2.2) here.Same shape as the schema-parity dance, and this PR stays red until step 3. Say the word and I will open the Fixtures side. The alternative, dropping the second file from the fixture, would delete the only coverage of multiple versioned files, which seems worse.