Repository navigation
fix(distribute): keep a user's edit to a distributed rule file - #189
Conversation
Refs: sideshow#90
Refs: sideshow#90
…ng receipt as an edit Refs: sideshow#90
arcavenai
left a comment
There was a problem hiding this comment.
Approving. A user's edit to a distributed rule now survives every path I could construct, where main drops it on the next init --scope project. The 8736668 reorder also closes the one case where unedited current bytes read as an edit forever.
End to end, main against this head. I built both binaries and ran install and init --scope project with an isolated SIDESHOW_HOME and a two-rule pack. Each path:
| Path | main | 8736668 |
|---|---|---|
| Edit, then run twice | edit lost on run 2 | modified since sideshow wrote it (user edit preserved) on runs 2 and 3 |
| Edit, then upgrade the pack | edit lost | kept; the other rule updates |
| Edit, remove the rule from the pack, re-add it a version later | edit lost on re-add | kept; the re-add reports the no-receipt message |
| Edit, with the second rule written in the same run | edit lost | kept on that run and the next, still reported as modified (receipt carried) |
Edit, then --dry-run, then a real run |
edit lost | dry run says would skip: modified ...; the real run keeps it |
| User removes the marker line | left alone | left alone (user-authored) |
Upstream changes still write. An unedited rule updates on a pack version bump, then reads already current on the next run.
Registry written by main. A registry first written by the main binary works with this one. An edit made under main is kept, and an unedited file reads already current, because main already recorded rule checksums on write.
Lagging receipt (9, 10, 11). I wrote the 1.0.1 bytes over a 1.0.0 receipt, then installed 1.0.1, and read the registry after each run:
--dry-runreportswould skip: already currentand leaves the receipt at the 1.0.0 sha.- The real run reports
already currentand moves the receipt to the sha of this run's bytes. The next run is unchanged. - A later edit is skipped as modified on two runs, the receipt stays, and the edit stays.
At 7645298 the same sequence reported modified forever. On the reorder: the write path is reached only with a known receipt that equals the bytes on disk, while those bytes differ from this run's. An edit with no receipt, or one that differs from both the receipt and this run's bytes, cannot reach it.
Red, and mutants. At cd0f8cb, with the stub reader, every new TestRules_ test fails for the stated reason: updated (sideshow-managed) where a skip is wanted. Head passes the full suite and two runs of -count=20.
The eight listed mutants and the old-order mutant each fail a test. So do these, which I added:
- the lagging case keeping the old receipt;
- the lagging case not recording;
PriorChecksumsreading rules entries.
The last is the reverse cross-contamination, killed by TestRules_ReceiptsAreKeyedByType and TestPriorChecksums_RoundTripsWithRecordResults.
The two changed tests. TestDistributeRule_UpdatesMarkedFile and the rule scene of TestDistributeRule_AtomicPins keep every assertion they had; only the setup adds a receipt for the seeded bytes. The guarantee they no longer exercise, that a marked file with no receipt is overwritten, is the behavior the ruling removes. It is now pinned the other way by TestRules_NoReceiptBytesDifferIsLeftAsIs.
CI on 8736668 passes.
Non-blocking:
- The lagging
already currentcase setsRecordReceipt = trueeven under--dry-run, where the no-receipt case uses!opts.DryRun. Today the onlyRecordResultscaller skips dry runs (cmd/sideshow/main.go:425), so nothing records. Matching the other case would keep that true for a future caller. - As the body notes, a
files:skip for a user edit still drops its receipt when another file is written in the same run. Nothing is lost there, only the message.
Seat: reviewer / reviewer
A user's edit to a distributed rule file was silently discarded on the next
init --scope project, reported as a routineupdated (sideshow-managed), becauserules:decided ownership by its marker alone. This makesrules:compare the file against the receipt sideshow already records for it, asfiles:does since #84, so an edit is kept and reported instead of dropped.Behavior change: a rule file an earlier run wrote and nobody edited updates as before; an edited one is skipped with "modified since sideshow wrote it (user edit preserved)". A marker file with no receipt (a lost registry, say) is compared with what this run would write: equal records a receipt and reports "unchanged (receipt recorded)"; different is left as is with "has a sideshow marker but no receipt; left as is; delete it to take the pack's version" and nothing is recorded. There is no
--force; deleting the file takes the pack's version. An unedited, current file is now skipped as "already current" instead of rewritten with identical bytes.Closes #90
Receipts are read per type:
PriorRuleChecksumsreturns rules entries andPriorChecksumsstill returns files entries, so a rule and a file at the same path never read each other's receipt.RecordResultsalso keeps the receipt of an action that asks for it (RecordReceipt), so the receipt of an edited rule survives a run that writes another rule; without that the list is replaced by what the run wrote.Repro, before and after, with an isolated
SIDESHOW_HOMEand a one-rule pack: at main2ce9fe46the second run printedupdated (sideshow-managed)and the appended line was gone. With this branch the second and third runs printmodified since sideshow wrote it (user edit preserved)and the line stays.Acceptance:
TestRules_EditAfterFirstRunIsPreserved)Commits: (1) tests with a stub reader, red; (2) the fix and the CLI wiring; (3) the case order: the current-bytes case is tested before the edited case, whether or not a receipt is known.
Blast radius:
distributeRule,RecordResults, one added reader, and one line inrunInitProject. Two existing tests changed because they relied on the old rule:TestDistributeRule_UpdatesMarkedFileand the rule scene ofTestDistributeRule_AtomicPinsnow give the file a receipt, since a marker file with no receipt and different bytes is now left alone.Mutants, each compiles and fails a test:
TestRules_EditAfterFirstRunIsPreservedPriorRuleChecksumsreads files entries: killed byTestRules_ReceiptsAreKeyedByTypeRecordResultsignoresRecordReceipt: killed byTestRules_NoReceiptBytesEqualRecordsTheReceiptTestRules_NoReceiptBytesDifferIsLeftAsIsTestRules_NoReceiptBytesEqualRecordsTheReceiptTestRules_DryRunMirrorsEachCaseTestRules_AnEditedRuleKeepsItsReceiptWhileAnotherRuleIsWritten; the single-rule tests alone did not catch it, becauseRecordResultskeeps the old list when a run records nothingonDisk == wantSumcase tested after the edited case, as in the first head: killed byTestRules_ALaggingReceiptWithCurrentBytesIsMovedNotReadAsAnEdit; it arrived with the reorder, so the mutant is the old code and the test is its red evidenceTestRules_CurrentFileIsLeftAloneAndKeepsItsReceiptOpportunity, not touched:
files:skips for a user edit do not carry their receipt either, so after a run that writes another file the edited one reads as "no record (user-authored)". It stays skipped, so nothing is lost, only the message changes.