ci(cd-infra): exclude brouter-host so a docs edit stops deploying the flagship #31

Merged
ullrich merged 1 commit from ci/cd-infra-exclude-brouter-host into main 2026-07-28 06:37:52 +00:00
Owner

infrastructure/brouter-host/** belongs to cd-brouter, but cd-infra's filter was a bare infrastructure/** — so anything under that directory fired both workflows. On 2026-07-27 a README-only edit redeployed BRouter on the dedicated host and ran a full infra deploy on the flagship (SIGHUP prometheus, restart loki/promtail, reload caddy) for no reason.

CLAUDE.md has documented this exclusion since the workflow was written — "infrastructure/ (except brouter-host/**)". It never existed. This makes the docs true rather than rewording them to match a weaker reality.

Why the implementation was worth reading first

paths and paths-ignore can't be combined for one event, so this needs a negative pattern inside paths. Forgejo's docs mention neither negation nor paths-ignore, so I checked the v16.0.1 source instead of guessing — and the failure mode justifies it:

patterns, err := workflowpattern.CompilePatterns(vals...)
if err != nil {
    break          // <- matchTimes never incremented
}

A malformed pattern doesn't fail loudly. It breaks out without matching, which would silently stop infra deploys altogether. Exactly the class of bug this repo has been chasing all week.

What the code actually does:

  • CompilePattern strips a leading ! and sets Negative: true — so it cannot error on this input
  • Skip evaluates the sequence per changed file with later matches winning, so the negative pattern must come after the positive one (it does)

Verified semantics

Reimplemented the compiled matcher and ran the real pattern list over six scenarios:

changed files runs? intended
infrastructure/brouter-host/README.md no no
infrastructure/brouter-host/poi-extract/poi-extract.sh no no
infrastructure/docker-compose.yml yes yes
infrastructure/Caddyfile + brouter-host/x.md yes yes — mixed
infrastructure/scripts/poi-import.sh yes yes
apps/journal/app/root.tsx no no

The mixed case is the one that matters: a commit touching brouter-host and real infrastructure still deploys, because the non-excluded file stays matched. A naive paths-ignore would have got that wrong.

Verifying after merge

This PR's own CI can't test the trigger — it's a push-to-main filter. The real confirmation is the next brouter-host-only change: cd-brouter should run and cd-infra should not. Worth watching for, since a mistake here is silent in the direction of "infra never deploys".

`infrastructure/brouter-host/**` belongs to `cd-brouter`, but `cd-infra`'s filter was a bare `infrastructure/**` — so anything under that directory fired **both** workflows. On 2026-07-27 a README-only edit redeployed BRouter on the dedicated host *and* ran a full infra deploy on the flagship (SIGHUP prometheus, restart loki/promtail, reload caddy) for no reason. CLAUDE.md has documented this exclusion since the workflow was written — *"`infrastructure/` (except `brouter-host/**`)"*. **It never existed.** This makes the docs true rather than rewording them to match a weaker reality. ## Why the implementation was worth reading first `paths` and `paths-ignore` can't be combined for one event, so this needs a negative pattern inside `paths`. Forgejo's docs mention **neither** negation nor `paths-ignore`, so I checked the v16.0.1 source instead of guessing — and the failure mode justifies it: ```go patterns, err := workflowpattern.CompilePatterns(vals...) if err != nil { break // <- matchTimes never incremented } ``` A malformed pattern doesn't fail loudly. It `break`s out without matching, which would **silently stop infra deploys altogether**. Exactly the class of bug this repo has been chasing all week. What the code actually does: - `CompilePattern` strips a leading `!` and sets `Negative: true` — so it cannot error on this input - `Skip` evaluates the sequence per changed file with **later matches winning**, so the negative pattern must come *after* the positive one (it does) ## Verified semantics Reimplemented the compiled matcher and ran the real pattern list over six scenarios: | changed files | runs? | intended | |---|---|---| | `infrastructure/brouter-host/README.md` | no | no | | `infrastructure/brouter-host/poi-extract/poi-extract.sh` | no | no | | `infrastructure/docker-compose.yml` | **yes** | yes | | `infrastructure/Caddyfile` + `brouter-host/x.md` | **yes** | yes — mixed | | `infrastructure/scripts/poi-import.sh` | **yes** | yes | | `apps/journal/app/root.tsx` | no | no | The mixed case is the one that matters: a commit touching brouter-host *and* real infrastructure still deploys, because the non-excluded file stays matched. A naive `paths-ignore` would have got that wrong. ## Verifying after merge This PR's own CI can't test the trigger — it's a `push`-to-`main` filter. The real confirmation is the next brouter-host-only change: `cd-brouter` should run and `cd-infra` should **not**. Worth watching for, since a mistake here is silent in the direction of "infra never deploys".
ci(cd-infra): exclude brouter-host so a docs edit stops deploying the flagship
All checks were successful
CI / Security Scan (pull_request) Successful in 46s
CI / Dockerfile Package Check (pull_request) Successful in 16s
CI / Checks (pull_request) Successful in 3m45s
CI / Visual Tests (pull_request) Successful in 2m15s
CI / Journal Image Smoke Test (pull_request) Successful in 4m33s
CI / E2E Tests (pull_request) Successful in 9m58s
CD Staging / Build & Push Docker Images (pull_request) Has been skipped
CD Staging / Build & Push Docker Images-1 (pull_request) Has been skipped
CD Staging / Deploy Staging (pull_request) Has been skipped
CD Staging / Deploy PR Preview (pull_request) Has been skipped
CD Staging / Tear Down PR Preview (pull_request) Successful in 38s
70be846aaf
`infrastructure/brouter-host/**` belongs to cd-brouter, but cd-infra's filter
was a bare `infrastructure/**`, so anything under it fired BOTH workflows. On
2026-07-27 a README-only change to that directory redeployed BRouter on the
dedicated host *and* ran a full infra deploy on the flagship — SIGHUP
prometheus, restart loki/promtail, reload caddy — for no reason.

CLAUDE.md has documented this exclusion since the workflow was written
("`infrastructure/` (except `brouter-host/**`)"). It never existed. This makes
the docs true rather than rewording them.

`paths` and `paths-ignore` cannot be combined for one event, so this uses a
negative pattern inside `paths`. That is not syntax worth guessing at here:
Forgejo compiles these with act's `workflowpattern` and, per v16.0.1
`modules/actions/workflows.go`, a compile error `break`s out without
incrementing matchTimes — i.e. a malformed pattern would silently stop infra
deploys altogether rather than failing loudly.

Verified against the actual implementation rather than the docs, which mention
neither negation nor paths-ignore:

  - `CompilePattern` strips a leading `!` and sets Negative, so it cannot error
    on this input
  - `Skip` evaluates the sequence per changed file with later matches winning,
    so the negative pattern must follow the positive one (it does)

Simulated the compiled semantics over six scenarios; the notable one is that a
commit touching brouter-host *and* other infrastructure still deploys, because
the non-excluded file stays matched:

  infrastructure/brouter-host/README.md                    -> no run
  infrastructure/brouter-host/poi-extract/poi-extract.sh   -> no run
  infrastructure/docker-compose.yml                        -> run
  infrastructure/Caddyfile + brouter-host/x.md             -> run  (mixed)
  infrastructure/scripts/poi-import.sh                     -> run
  apps/journal/app/root.tsx                                -> no run

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8hNxgYp777FRqYtVmQNaU
ullrich deleted branch ci/cd-infra-exclude-brouter-host 2026-07-28 06:37:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
trails-cool/trails!31
No description provided.