From 81043edfc64f917675aaaf15d0d4d5a200e0515a Mon Sep 17 00:00:00 2001 From: lilleman Date: Wed, 5 Aug 2026 10:15:09 +0200 Subject: [PATCH] Count both paths of a rename in the docs-only CI skip --- ci.sh | 5 ++++- src/ci-gate.test.ts | 8 ++++++-- todo.md | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ci.sh b/ci.sh index d82cc16..0e2aa9e 100755 --- a/ci.sh +++ b/ci.sh @@ -15,13 +15,16 @@ step() { printf '\n\033[1;34m==> %s\033[0m\n' "$1"; } # Docs-only fast path: nothing but *.md changed since main, so there is nothing here to break. # The working tree counts too — a dirty tree carrying real code must never skip. Anything # undeterminable (no git, no reachable main, no merge-base) falls through to the gate, never a skip. +# --no-renames on both channels: rename detection names only the destination, so `git mv src/app.ts +# notes.md` reads as a lone *.md and would skip the gate over a source file that is gone. docs_only() { local base changed git rev-parse --git-dir >/dev/null 2>&1 || return 1 git fetch --no-tags --quiet origin +refs/heads/main:refs/remotes/origin/main 2>/dev/null || true base=$(git merge-base refs/remotes/origin/main HEAD 2>/dev/null) || return 1 changed=$( - { git diff --name-only "$base" HEAD && git status --porcelain --untracked-files=all | cut -c4-; } 2>/dev/null + { git diff --name-only --no-renames "$base" HEAD \ + && git status --porcelain --no-renames --untracked-files=all | cut -c4-; } 2>/dev/null ) || return 1 [ -n "$changed" ] || return 1 ! printf '%s\n' "$changed" | grep -qvE '\.md$' diff --git a/src/ci-gate.test.ts b/src/ci-gate.test.ts index 884a0f5..6af7872 100644 --- a/src/ci-gate.test.ts +++ b/src/ci-gate.test.ts @@ -28,7 +28,11 @@ test("the commit-hash image is pushed even when the gate no-ops", () => { assert.doesNotMatch(step("docker push"), /^\s*if:/m); }); -test("only *.md counts as docs, and a dirty working tree counts as changed", () => { +test("only *.md counts as docs; a dirty tree and a rename both count as changed", () => { assert.ok(gate.includes("\\.md$"), "the non-docs match is a *.md suffix test"); - assert.match(gate, /git status --porcelain/, "uncommitted code can never be skipped over"); + assert.match(gate, /git status --porcelain --no-renames/, "uncommitted code can never be skipped over"); + // Rename detection hides the source path: `git mv src/app.ts notes.md` reads as the .md alone + // under --name-only, and as a single `R src/app.ts -> notes.md` line under --porcelain — so + // without --no-renames a deleted source file looks like docs and skips the gate. + assert.match(gate, /git diff --name-only --no-renames/, "a rename must list both of its paths"); }); diff --git a/todo.md b/todo.md index 62aee4c..066505e 100644 --- a/todo.md +++ b/todo.md @@ -2,7 +2,6 @@ ## Unfinnished work -- [ ] Don't run tests when only markdown files in the root have changed. - [ ] In Playwright tests, check for warnings and errors in all browsers on all the steps. If they exist, that is a failure we need to fix. - [ ] In Playwright tests, try different resolutions and sizes, from BIG desktop down to tiny phone. - [ ] Record the browser floor Plainpages actually requires, and whether the fallback is the contract or a courtesy. The stylesheet already needs `:has()` (Dec 2023); the menus now need the popover API (Safari 17, Sep 2023) and CSS anchor positioning for placement (newer still, and unguarded — the `@supports` test covers popover only). An iPadOS 16 tablet — capped at Safari 16, and exactly the "tablet on a factory floor, old thin client at a reception desk" README → Overview sells the zero-JS stance on — therefore gets panels flowing inline rather than working menus. Either state a supported floor in the README or accept the fallback as the answer for those devices; nobody has rendered that path on real hardware. Raised by the architecture review 2026-08-05. @@ -28,6 +27,7 @@ Prioritized. Overall verdict: architecture is sound (contract-first plugin API, ## Finnished work +- [x] Don't run tests when only markdown files in the root have changed. (Already shipped for *any* `*.md`, anywhere in the tree — `ci.sh`'s `docs_only()` no-ops the gate when every path changed since `main` ends in `.md`, and the workflow still pushes the commit-hash image so a merged docs commit stays releasable. Kept wider than "in the root" deliberately: no test reads a markdown file, so a nested `examples/plugins/admin/README.md` edit is as safe to skip as `README.md`, and narrowing it would spend the full gate on one. What was actually broken was rename detection — `git mv src/app.ts notes.md` names only the destination under `git diff --name-only`, and collapses to a single `R src/app.ts -> notes.md` line under `git status --porcelain`, so **moving code onto a `.md` path skipped the gate over a source file that was gone**. Both channels now pass `--no-renames`; verified against a scratch repo across ten scenarios — docs-only, mixed, empty diff, dirty tree, untracked code, deleted doc, and the rename staged *and* committed — the last two failing before the fix and passing after. `src/ci-gate.test.ts` locks both flags; it stays a text guard because the test image is `node:alpine` with neither `git` nor `bash`.) - [x] The little menues, like when choosing language or clicking my username, they do not dissapear when clicking outside them, I must click the original trigger or choose something. See if there are more modern ways of handling this with HTML and CSS. I think there is a modal-thing or something? (The modern thing is the **Popover API**. All three popup menus — language picker, profile, row kebab — are now a `