Files
civitai__civitai/scripts/lib
Zachary Lowden 088a9d09b1 fix(release): refuse an app release from a branch behind its released history (#4046)
`release-app.mjs` derives the next version from apps/<app>/package.json on the
CURRENT branch. When that branch is behind the app's released history the tag it
computes does not continue that line, in one of two ways:

  * it already exists  -> `git tag` aborts, but only after the release commit has
    been made, leaving a junk commit on the branch;
  * it is a different line (a minor/major bump off a stale base) -> nothing
    collides, the tag becomes the HIGHEST for that app, and the Flux ImagePolicy
    selects the highest semver rather than the most recent push. That stale build
    is then what production runs.

apps/moderator is in exactly this state, measured 2026-08-17: 0.0.1 on main,
0.0.26 live, all 26 releases cut from `moderator-app-pages` — 211 commits and
+38,630 lines that never merged to main. `pnpm release:moderator` from main
collides on the existing 0.0.2 and aborts; `release:moderator:minor` computes
0.1.0, which does NOT exist, and would deploy main's stale copy to production.
One command, no collision.

So this does NOT bump moderator's version to 0.0.26. That is the obvious fix and
it is the wrong one: it removes the collision that is currently the only brake,
while leaving the app itself 211 commits stale. The real remedy is to land the
branch; the guard is what makes the trap loud until someone does.

Checked after `git pull --rebase` so the tag list is current, and before
`npm version` so a refusal leaves the tree exactly as it found it.

Coverage: 19 tests. The version arithmetic is unit-tested, and — because a guard
nothing calls is not a guard — release-app.mjs is also driven end-to-end as a
real process against a throwaway git repo with a local bare remote (offline; no
tags or commits touch this repository).

Mutation-tested, 4 mutants, each the narrowest expression that can be wrong:
  invert the behind-comparison        -> 5 tests fail
  lexicographic highest-tag compare   -> 6 tests fail
  guard computed but never acted on   -> ONLY the 2 behavioural tests fail,
                                         which is what pins reachability
  drop the unparseable-tag skip       -> exactly 1 test fails
Baseline restored green (19/19) after the battery.

NOTE: scripts/**/*.test.ts runs in the `unit` vitest project, which is
`continue-on-error: true` in lint.yml — so these tests run but cannot currently
fail CI. That is tracked separately (868kp7fdr); it does not make them useless,
it makes them a local and post-fix gate.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:15:50 -05:00
..