.gitea/workflows/deploy.yaml is the only workflow, and it does two things that belong apart.
A pushed branch runs nothing. The trigger is push: branches: [main] plus workflow_dispatch. Pushing intents-swaps on 2026-10-03 started no run; the latest is still #86 for 0de203c on main. So far, a branch's only gate is whatever its author ran locally. The first time CI sees a change is the push to main, which is also the deploy.
validate does not mean "check only".workflow_dispatch offers mode: deploy | validate, described as "deploy (apply then validate) or validate (check only)". No job reads inputs.mode. deploy-api and deploy-web hang off build with no guard on the input. A validate dispatch therefore builds and deploys whatever ref it is dispatched on, including a feature branch. Nothing has done that yet as far as I know, but the option invites it.
What
A CI-only workflow (e.g. .gitea/workflows/ci.yaml) on pushes to any branch except main. It runs the same gate as the build job, verbatim per CLAUDE.md "Run the gate CI runs":
SQLX_OFFLINE=true cargo test --all, then the ts-rs drift check (git status --porcelain web/src/api/generated/ empty)
pnpm format:check && pnpm lint && pnpm build
It goes on the rust runner, with no deploy jobs and no secrets. Ideally it shares its steps with build rather than copying them, so the two gates cannot drift apart.
Either make validate real (if: inputs.mode != 'validate' on both deploy jobs, and check what "validate" should still do: the post-deploy health checks?) or remove the input.
Decide whether a workflow_dispatch from a non-main ref may deploy at all. Probably not: guard the deploy jobs on github.ref == 'refs/heads/main' too.
## Why
`.gitea/workflows/deploy.yaml` is the only workflow, and it does two things that belong apart.
**A pushed branch runs nothing.** The trigger is `push: branches: [main]` plus `workflow_dispatch`. Pushing `intents-swaps` on 2026-10-03 started no run; the latest is still #86 for `0de203c` on `main`. So far, a branch's only gate is whatever its author ran locally. The first time CI sees a change is the push to `main`, which is also the deploy.
**`validate` does not mean "check only".** `workflow_dispatch` offers `mode: deploy | validate`, described as "deploy (apply then validate) or validate (check only)". No job reads `inputs.mode`. `deploy-api` and `deploy-web` hang off `build` with no guard on the input. A `validate` dispatch therefore builds and deploys whatever ref it is dispatched on, including a feature branch. Nothing has done that yet as far as I know, but the option invites it.
## What
- A CI-only workflow (e.g. `.gitea/workflows/ci.yaml`) on pushes to any branch except `main`. It runs the same gate as the `build` job, verbatim per CLAUDE.md "Run the gate CI runs":
- `cargo fmt --all -- --check`
- `SQLX_OFFLINE=true cargo clippy --all-targets --all-features -- -D warnings`
- `SQLX_OFFLINE=true cargo test --all`, then the ts-rs drift check (`git status --porcelain web/src/api/generated/` empty)
- `pnpm format:check && pnpm lint && pnpm build`
It goes on the `rust` runner, with no deploy jobs and no secrets. Ideally it shares its steps with `build` rather than copying them, so the two gates cannot drift apart.
- Either make `validate` real (`if: inputs.mode != 'validate'` on both deploy jobs, and check what "validate" should still do: the post-deploy health checks?) or remove the input.
- Decide whether a `workflow_dispatch` from a non-`main` ref may deploy at all. Probably not: guard the deploy jobs on `github.ref == 'refs/heads/main'` too.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Why
.gitea/workflows/deploy.yamlis the only workflow, and it does two things that belong apart.A pushed branch runs nothing. The trigger is
push: branches: [main]plusworkflow_dispatch. Pushingintents-swapson 2026-10-03 started no run; the latest is still #86 for0de203conmain. So far, a branch's only gate is whatever its author ran locally. The first time CI sees a change is the push tomain, which is also the deploy.validatedoes not mean "check only".workflow_dispatchoffersmode: deploy | validate, described as "deploy (apply then validate) or validate (check only)". No job readsinputs.mode.deploy-apianddeploy-webhang offbuildwith no guard on the input. Avalidatedispatch therefore builds and deploys whatever ref it is dispatched on, including a feature branch. Nothing has done that yet as far as I know, but the option invites it.What
A CI-only workflow (e.g.
.gitea/workflows/ci.yaml) on pushes to any branch exceptmain. It runs the same gate as thebuildjob, verbatim per CLAUDE.md "Run the gate CI runs":cargo fmt --all -- --checkSQLX_OFFLINE=true cargo clippy --all-targets --all-features -- -D warningsSQLX_OFFLINE=true cargo test --all, then the ts-rs drift check (git status --porcelain web/src/api/generated/empty)pnpm format:check && pnpm lint && pnpm buildIt goes on the
rustrunner, with no deploy jobs and no secrets. Ideally it shares its steps withbuildrather than copying them, so the two gates cannot drift apart.Either make
validatereal (if: inputs.mode != 'validate'on both deploy jobs, and check what "validate" should still do: the post-deploy health checks?) or remove the input.Decide whether a
workflow_dispatchfrom a non-mainref may deploy at all. Probably not: guard the deploy jobs ongithub.ref == 'refs/heads/main'too.