From cd5a88c8a2ab1f3e0c0929675e8ddfac2b67bb00 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 13 Jun 2026 11:22:05 -0400 Subject: [PATCH 1/4] ci(260613-fp9): skip Docker publish for .gitea/.planning-only pushes Add paths-ignore ['.gitea/**', '.planning/**'] to publish.yml's push trigger. Doc/CI-only pushes to main (planning docs push straight to main; workflow edits merge via PR) produce a byte-identical image (.dockerignore already excludes .planning), so the build + :latest re-push was wasted. paths-ignore skips only when every changed file matches; mixed code+docs pushes still publish. --- .gitea/workflows/publish.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml index 34fc80b..fcc2bad 100644 --- a/.gitea/workflows/publish.yml +++ b/.gitea/workflows/publish.yml @@ -1,6 +1,11 @@ # Publishing / Releases # -# Trigger: push to main — i.e. when any PR merges. +# Trigger: push to main — i.e. when any PR merges — EXCEPT pushes whose changed +# files are confined to .gitea/** (CI/workflow edits) and/or .planning/** (GSD +# planning docs, which push straight to main under the unprotected .planning/* +# branch-protection pattern). Those never alter the shipped image (.dockerignore +# already excludes .planning), so the paths-ignore filter below skips a wasted +# build + re-push. A push that also touches code/Dockerfile/manifests still publishes. # Image: git.bergerhouse.net/luckberg/familysync-api # Tags: # :latest — moving pointer for easy pulls @@ -25,6 +30,12 @@ name: Publish on: push: branches: [main] + # Doc/CI-only pushes produce a byte-identical image — skip the build entirely. + # paths-ignore skips the run only when EVERY changed file matches; a mixed + # push (code + .planning) still publishes. + paths-ignore: + - '.gitea/**' + - '.planning/**' env: MILESTONE: v1.1 From 4d4ffad0595958550f3dbbc665857abcd17a013c Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 13 Jun 2026 11:22:05 -0400 Subject: [PATCH 2/4] docs(quick-260613-fp9): .gitea/.planning pushes should not trigger a docker image publish --- .planning/STATE.md | 3 +- .../260613-fp9-PLAN.md | 62 +++++++++++++++++++ .../260613-fp9-SUMMARY.md | 61 ++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 .planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-PLAN.md create mode 100644 .planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index ca44274..cb4bcfa 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-06-10) Phase: 999.1 Plan: Not started Status: Phase complete — ready for verification -Last activity: 2026-06-13 - Completed quick task 260613-dmw: exclude .gitea/** from CI heavy-job paths-filter +Last activity: 2026-06-13 - Completed quick task 260613-fp9: .gitea/.planning-only pushes skip the Docker publish ### ✅ Resolved Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action) @@ -215,6 +215,7 @@ Recent decisions affecting current work: | 260611-ozt | Split publish job into standalone .gitea/workflows/publish.yml (on: push→main only, no redundant event-guard if:; MILESTONE env moved with it) and strip it + the push trigger from ci.yml — kills the orphaned `CI / publish (pull_request)` pending status (phase-8 code-review WR-01). name:CI + fast-checks/api/harness job ids held stable so the required branch-protection contexts stay valid. Documented the release model in README "Publishing / Releases" + publish.yml header. Both YAML validated (yq) | 2026-06-11 | 92353e1 | | [260611-ozt-split-publish-job-into-standalone-gitea-](./quick/260611-ozt-split-publish-job-into-standalone-gitea-/) | | 260611-tfc | Fix WR-01 (13-REVIEW): apps/pwa/src/sw.ts notificationclick openWindow fallback was unreachable when client.focus() rejects (window closed between matchAll/focus) or client.navigate() resolves null — chained a navigate-result check + a .catch, both falling through to self.clients.openWindow(url). lint/format:check/typecheck green, build emits sw.js, 191/191 pwa tests | 2026-06-12 | af78ccc | Verified | [260611-tfc-fix-wr-01-sw-ts-notificationclick-openwi](./quick/260611-tfc-fix-wr-01-sw-ts-notificationclick-openwi/) | | 260613-dmw | Exclude `.gitea/**` from the CI `changes` `code` paths-filter so workflow-only PRs skip the heavy api/harness jobs (treated like docs) while fast-checks + gate still run. Single `- '!.gitea/**'` negation appended after the yml/yaml globs (index 11 vs 5). Rides along on the Phase 16 branch / PR #15. | 2026-06-13 | 2d329a9 | | [260613-dmw-exclude-gitea-workflow-config-changes-fr](./quick/260613-dmw-exclude-gitea-workflow-config-changes-fr/) | +| 260613-fp9 | `.gitea`/`.planning`-only pushes to main no longer trigger the Docker publish — added `paths-ignore: ['.gitea/**', '.planning/**']` under `on.push` in `.gitea/workflows/publish.yml` (skips only when EVERY changed file matches; mixed code+docs pushes still publish). `.dockerignore` already excludes `.planning` so the image is byte-identical. Done in isolated worktree (phase-10 agent held main tree). | 2026-06-13 | cd5a88c | | [260613-fp9-gitea-and-planning-pushes-should-not-tri](./quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/) | ## Deferred Items diff --git a/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-PLAN.md b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-PLAN.md new file mode 100644 index 0000000..80630f2 --- /dev/null +++ b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-PLAN.md @@ -0,0 +1,62 @@ +--- +quick_id: 260613-fp9 +title: ".gitea and .planning pushes should not trigger a docker image publish" +status: ready +--- + +# Quick Task 260613-fp9: Skip Docker publish for `.gitea`/`.planning`-only pushes + +## Problem + +`.gitea/workflows/publish.yml` triggers on every `push` to `main` with no path +filter. Two classes of push currently fire a full Docker build + publish that +produce an identical image: + +- `.planning/**`-only commits, which push straight to `main` (the `.planning/*` + branch-protection pattern is unprotected). +- `.gitea/**`-only changes (CI/workflow edits) merged via PR. + +Neither changes the shipped artifact — `.dockerignore` already excludes +`.planning` (and `apps/api/tests`) from the image — so the rebuild is wasted +runner time and a needless `:latest` re-push / new `:vMILESTONE-` tag. + +## Change + +Add a `paths-ignore` filter to the `push` trigger in `publish.yml`: + +```yaml +on: + push: + branches: [main] + paths-ignore: + - '.gitea/**' + - '.planning/**' +``` + +Gitea Actions follows GitHub-compatible workflow syntax (the repo already relies +on the native `branches:` push filter). When every file changed in a push to +`main` matches a `paths-ignore` glob, the `publish` job is skipped. A push that +also touches code/Dockerfile/manifests still triggers publish — correct. + +Also update the header comment block to document the new skip behavior. + +## Tasks + +1. **Edit `.gitea/workflows/publish.yml`** + - files: `.gitea/workflows/publish.yml` + - action: Add `paths-ignore: ['.gitea/**', '.planning/**']` under `on.push`; + update the top-of-file `# Trigger:` comment to note doc/CI-only pushes skip. + - verify: `paths-ignore` present under `on.push`; YAML still parses; the + existing `--target production` self-assertion grep still matches. + - done: pushes touching only `.gitea/**` and/or `.planning/**` no longer + trigger the publish job; mixed pushes (code + docs) still publish. + +## must_haves + +- truths: + - publish.yml `on.push` carries a `paths-ignore` listing `.gitea/**` and `.planning/**` + - `branches: [main]` is retained +- artifacts: + - `.gitea/workflows/publish.yml` +- key_links: + - `.gitea/workflows/publish.yml` diff --git a/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md new file mode 100644 index 0000000..574eaff --- /dev/null +++ b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md @@ -0,0 +1,61 @@ +--- +quick_id: 260613-fp9 +title: ".gitea and .planning pushes should not trigger a docker image publish" +status: complete +date: 2026-06-13 +--- + +# Quick Task 260613-fp9 — Summary + +## What changed + +Added a `paths-ignore` filter to the `push` trigger in +`.gitea/workflows/publish.yml`: + +```yaml +on: + push: + branches: [main] + paths-ignore: + - '.gitea/**' + - '.planning/**' +``` + +Updated the file's header comment to document the new skip behavior. + +## Why + +Every push to `main` previously ran a full Docker build + push. Pushes confined +to `.planning/**` (planning docs push straight to main under the unprotected +`.planning/*` branch-protection pattern) or `.gitea/**` (CI/workflow edits) never +change the shipped image — `.dockerignore` already excludes `.planning`. The +rebuild and `:latest` re-push were wasted runner time. + +## Behavior + +- Push touching only `.gitea/**` and/or `.planning/**` → `publish` job skipped. +- Push touching code / Dockerfile / manifests (alone or mixed with docs) → + `publish` runs as before. `paths-ignore` skips only when **every** changed + file matches a glob. + +## Verification + +- `python3 yaml.safe_load` parses the file; `on.push` carries both + `branches: [main]` and `paths-ignore: ['.gitea/**', '.planning/**']`; the + `publish` job is intact. +- The in-workflow `grep "--target production"` self-assertion still matches + (D-10 hygiene check unaffected). + +## Isolation note + +Executed in a dedicated worktree (`familysync-wt-fp9`, branch +`quick/260613-fp9-publish-paths-ignore` off `origin/main`) because a concurrent +phase-10 agent has the main working tree checked out on +`gsd/phase-10-admin-role-settings`. No subagents spawned — trivial single-file +config edit done inline. + +## Follow-up + +`publish.yml` lives under `.gitea/**`, so merging this change will itself be a +`.gitea`-only push and will (correctly) not publish. Open a PR to `main` +(protected; code changes require PR). From ec85fe026ade8ca73151140e9da02f3c8f387cd1 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 13 Jun 2026 12:13:58 -0400 Subject: [PATCH 3/4] ci(260613-fp9): fix changes paths-filter so doc/.gitea/.planning PRs skip heavy jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dorny/paths-filter combines patterns with Array.some, and picomatch compiles '!.gitea/**' as 'any path not under .gitea'. The '- !.gitea/**' line (from quick task 260613-dmw) therefore matched every non-.gitea file — including .planning/** and *.md — so code=true for doc-only PRs and the heavy api/harness jobs ran anyway. It also never excluded .gitea (the **/*.yml glob already matched workflow files), regressing the Phase 15 doc-only skip. Switch to predicate-quantifier 'every' with negation-only globs: a file counts as code only if it is outside .gitea/, outside .planning/, and not Markdown. Verified with picomatch against representative file sets. --- .gitea/workflows/ci.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 6d8b0a2..e8548d4 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -16,20 +16,22 @@ jobs: - uses: dorny/paths-filter@v4 id: filter with: + # 'every' + negation-only globs. dorny combines a filter's patterns with + # Array.some by default, and picomatch compiles '!.gitea/**' as "matches any + # path NOT under .gitea" — so under 'some' that single line matched EVERY + # non-.gitea file (incl. .planning/** and *.md), flipping code=true for + # doc-only PRs and silently running the heavy api/harness jobs (regression + # introduced by quick task 260613-dmw; the old positive allowlist also never + # actually excluded .gitea because '**/*.yml' already matched workflow files). + # With predicate-quantifier 'every' a changed file counts as "code" ONLY if it + # matches ALL patterns — i.e. it is outside .gitea/, outside .planning/, and is + # not Markdown. Verified against representative file sets in quick task 260613-fp9. + predicate-quantifier: 'every' filters: | code: - - '**/*.ts' - - '**/*.tsx' - - '**/*.js' - - '**/*.json' - - '**/*.yaml' - - '**/*.yml' - - 'apps/**' - - 'packages/**' - - 'pnpm-lock.yaml' - - 'Dockerfile' - - 'docker-compose*.yml' - '!.gitea/**' + - '!.planning/**' + - '!**/*.md' fast-checks: runs-on: ubuntu-latest From 15fa89d4832a081ba99738d7e7a3e8ce94312b8a Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 13 Jun 2026 12:13:58 -0400 Subject: [PATCH 4/4] docs(quick-260613-fp9): document ci.yml heavy-job paths-filter fix --- .../260613-fp9-SUMMARY.md | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md index 574eaff..5f79ba9 100644 --- a/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md +++ b/.planning/quick/260613-fp9-gitea-and-planning-pushes-should-not-tri/260613-fp9-SUMMARY.md @@ -54,8 +54,40 @@ phase-10 agent has the main working tree checked out on `gsd/phase-10-admin-role-settings`. No subagents spawned — trivial single-file config edit done inline. +## Follow-up fix — ci.yml heavy-job paths-filter (added after PR opened) + +Opening PR #16 surfaced a second, related bug: the `api` + `harness` jobs ran on +this `.gitea`/`.planning`-only PR. Root cause in `.gitea/workflows/ci.yml`'s +`changes` job: + +- dorny/paths-filter combines a filter's patterns with `Array.some`, and + picomatch compiles `!.gitea/**` as "matches any path **not** under `.gitea`". +- So the `- '!.gitea/**'` line (added by quick task 260613-dmw) matched every + non-`.gitea` file — including `.planning/**` and `*.md` — making `code=true` + for doc-only PRs and silently running the heavy jobs. It also never excluded + `.gitea` (the `**/*.yml` glob already matched workflow files), so dmw's stated + goal was never achieved and it regressed the Phase 15 doc-only skip. + +Fix: switched the filter to `predicate-quantifier: 'every'` with negation-only +globs so a file counts as `code` ONLY if it is outside `.gitea/`, outside +`.planning/`, and not Markdown: + +```yaml +predicate-quantifier: 'every' +filters: | + code: + - '!.gitea/**' + - '!.planning/**' + - '!**/*.md' +``` + +Verified locally with picomatch against representative file sets: `.gitea`-only, +`.planning`-only, `*.md`-only, and this PR's mix all → `code=false` (heavy jobs +skip); real code (`.ts`), `pnpm-lock.yaml`, `package.json`, and code+docs mixes +all → `code=true` (heavy jobs run). + ## Follow-up -`publish.yml` lives under `.gitea/**`, so merging this change will itself be a -`.gitea`-only push and will (correctly) not publish. Open a PR to `main` +`publish.yml` and `ci.yml` both live under `.gitea/**`, so merging this PR is a +`.gitea`-only push and will (correctly) not publish. PR #16 opened against `main` (protected; code changes require PR).