670f6303fe
## Summary Promotes [ADR-0028](docs/decisions/0028-migrate-cicd-and-git-hosting-to-gitlab.md) from `proposed` to `accepted`. Shipped as `proposed` in [#226](#226); no open question left on either drivers, considered options, or the 4-phase migration sequence. Same shape as [#219](#219) (ADR-0026 + ADR-0027 acceptance). Once merged, **Phase 1** of the migration (`mirror-and-bootstrap` — repos pushed to GitLab, branch protection / MR templates / deploy keys / Renovate reconfig) is unblocked. ## What lands | File | Change | | --- | --- | | `docs/decisions/0028-migrate-cicd-and-git-hosting-to-gitlab.md` | Frontmatter `status: proposed → accepted`. | | `docs/decisions/0015-cicd-gitea-actions.md` | **Status-update note** added at the top, right under the title — calls out that the platform choice "Gitea Actions" is superseded by ADR-0028, while the rest of ADR-0015's architectural principles (trunk-based + squash, all-gates-blocking, thin YAML, on-prem runners, signed commits, Conventional Commits) carry over unchanged. ADR-0015 stays `accepted`. | | `docs/decisions/README.md` | ADR-0028 row: `proposed → accepted`. | | `CLAUDE.md` | Roll-up bumped to `ADRs 0001 → 0028 accepted` with a one-sentence explanation that ADR-0028 supersedes only ADR-0015's platform choice. **CI/CD architecture bullet rewritten** to reflect the accepted-but-not-yet-implemented state — "Gitea Actions today, migrating to GitLab CE on `vm-gitlab` per ADR-0028 (4-phase rollout in follow-up PRs)". The architectural detail (gates list, thin YAML, signed commits) was preserved; only the platform headline and the act_runner→GitLab Runner line are touched. Also corrected: `ci:scan` (which doesn't exist as a script) → the real script names (`ci:catalogue-drift`, `ci:audit`, `ci:perf`, `ci:gzip-budgets`). | ## Notes for the reviewer - **No new Architecture bullet for ADR-0028 itself.** The decision is a *platform shift* for the existing CI/CD architecture, not a new architectural concern — so it folds into the existing ADR-0015 bullet rather than adding a sibling. - **The annotation pattern on ADR-0015** (status-update blockquote at the top) is the canonical MADR way to handle partial supersession without changing the frontmatter status. The architectural principles are still accepted; only the platform implementation moves. A future reader hitting ADR-0015 first sees the redirect immediately. - **The CLAUDE.md script-list correction** is a side-fix — `ci:scan` is not a real script name; the actual gates are `ci:check`, `ci:catalogue-drift`, `ci:audit`, `ci:commits`, `ci:perf`, `ci:gzip-budgets`. Updated in the same touch since the bullet was being rewritten anyway. - **No code changes**, so no `pnpm ci:check` impact. `pnpm exec prettier --check` clean on the four touched files. ## Test plan - [x] `pnpm exec prettier --check` — clean on the four touched files. - [x] ADR-0015 → ADR-0028 cross-reference resolves (the new blockquote link). - [x] `ADRs 0001 → 0028 accepted` matches reality (`grep '^status: ' docs/decisions/*.md` shows everything below 0029 as `accepted`). - [ ] **Review focus** — the ADR-0015 status-update note phrasing, the CLAUDE.md CI/CD bullet rewrite (especially the "carry over" wording), the roll-up sentence about partial supersession. ## What's next (post-merge) 1. **Phase 1 — `mirror-and-bootstrap`** — `git push --mirror gitlab` for `apf_portal` and the proto vendoring in `apf-ai-service`. GitLab side: groups, projects, branch protection (mirror Gitea's), MR templates, deploy keys, Renovate reconfigured for GitLab. **No `.gitlab-ci.yml` yet** — Gitea pipelines continue to gate. Ops work primarily; the only PR-shaped output is a Renovate config update on the apf-portal repo if its host detection changes. 2. **Phase 2 — `gitlab-ci-pipeline`** — `.gitlab-ci.yml` lands alongside `.gitea/workflows/ci.yml`. Both pipelines run in parallel for ~1 calendar week. GitLab Runner registered on `vm-gitlab`. 3. **Phase 3 — `cutover`** — remotes flip in CLAUDE.md, READMEs, `docs/setup/01-dev-debian-vm-setup.md` §8.3. `.gitea/workflows/` + `infra/ci-runners.compose.yml` deleted, Gitea read-only / archive. 4. **Phase 4 — `cleanup`** — stale references sweep, required signed-commits on `main` enabled. In parallel — once you've finished walking through the dev VM bootstrap — the paused **ADR-0027 PR 1** (Region / Delegation / Structure Prisma schema + inline seed) and **ADR-0026 PR 1** (Person / User / UserScope schema + provisioner) can ship on Gitea; they're decoupled from the migration and don't need to wait for it. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #227
328 lines
21 KiB
Markdown
328 lines
21 KiB
Markdown
---
|
||
status: accepted
|
||
date: 2026-04-30
|
||
decision-makers: R&D Lead
|
||
tags: [infrastructure, process]
|
||
---
|
||
|
||
# CI/CD pipeline — Gitea Actions, trunk-based + squash-merge, thin YAML over portable scripts
|
||
|
||
> **Status update — 2026-05-26.** The platform choice **"Gitea Actions"** in §"Decision Outcome" below is superseded by [ADR-0028](0028-migrate-cicd-and-git-hosting-to-gitlab.md) — migration to GitLab CE on `vm-gitlab` accepted, 4-phase rollout in progress. The architectural principles in the rest of this ADR (trunk-based + squash-merge, branch protection, all CI gates blocking, **thin YAML over portable scripts**, on-prem runners, Conventional Commits double-check, signed commits) **carry over unchanged**. This ADR's frontmatter status stays `accepted`; only the platform implementation shifts.
|
||
|
||
## Context and Problem Statement
|
||
|
||
The repository currently lives on Gitea (`gitea@git.unespace.com:julien/apf_portal.git`). The organisation plans to migrate to GitLab on a 6–18-month horizon. The pipeline shape we adopt now must be (a) operational on Gitea immediately, (b) low-cost to migrate later, and (c) consistent with the project's anti-bricolage and security-first values.
|
||
|
||
We also need to fix the branch model, the merge strategy, the required gates, the protection rules on `main`, and the location of the orchestration logic — all of these have first-order effects on the Nx scaffold (`package.json` scripts, lint configs, branch tags, conventional-commits validation surface).
|
||
|
||
This ADR is split into two levels of decision:
|
||
|
||
- **Level 1 — vendor-neutral.** Survives the future GitLab migration unchanged.
|
||
- **Level 2 — Gitea Actions implementation.** Will be rewritten when GitLab is adopted; a future ADR will supersede the level-2 sections without affecting level-1.
|
||
|
||
## Decision Drivers
|
||
|
||
- Pipeline portability across CI vendors (Gitea now, GitLab later, possibly other platforms in the future).
|
||
- All gates blocking — no "warnings ignored". Either we fail or we adjust the threshold via ADR.
|
||
- Trunk-based development for fast feedback and continuous integration discipline.
|
||
- Compatibility with the Nx monorepo's `affected` model and remote cache.
|
||
- Self-hosted runners (on-prem context — see ADR-0008's hosting constraint).
|
||
- Mature, mainstream tooling — anti-bricolage applies particularly here.
|
||
- Local enforcement (Husky + lint-staged + commitlint, ADR-0007) plus CI defense in depth — the same checks run twice, on purpose.
|
||
|
||
## Considered Options
|
||
|
||
### Branch / merge strategy (level 1)
|
||
|
||
- **Trunk-based + squash-merge.** (Chosen.)
|
||
- Trunk-based + rebase-merge.
|
||
- Trunk-based + merge commit.
|
||
- GitFlow.
|
||
|
||
### Required reviewer count on PRs to `main` (level 1)
|
||
|
||
- **0 in v1, ≥1 once a second active contributor exists.** (Chosen.)
|
||
- 1 always (blocks if solo).
|
||
- 2 (heavy for a small team).
|
||
|
||
### Signed commits (level 1)
|
||
|
||
- **Recommended but not required in v1; reconsidered at the GitLab migration ADR.** (Chosen.)
|
||
- Required in v1.
|
||
- Never.
|
||
|
||
### Conventional Commits validation (level 1)
|
||
|
||
- **Local `commit-msg` hook (ADR-0007) + CI defense-in-depth on the PR commit range.** (Chosen.)
|
||
- Local hook only.
|
||
- CI only.
|
||
|
||
### Pipeline orchestration logic location (level 1)
|
||
|
||
- **Thin YAML — logic lives in `package.json` scripts and Nx targets, the workflow file orchestrates.** (Chosen.)
|
||
- Logic in YAML, scripts called step by step.
|
||
|
||
### CI engine (level 2 — Gitea-specific)
|
||
|
||
- **Gitea Actions** (built-in since Gitea 1.19, GitHub Actions-compatible YAML). (Chosen.)
|
||
- Drone CI alongside Gitea.
|
||
- Concourse / Tekton / Buildkite / etc.
|
||
|
||
### Runner topology (level 2)
|
||
|
||
- **≥ 3 self-hosted `act_runner` instances on-prem.** (Chosen.)
|
||
- Single runner.
|
||
- Cloud-hosted (rejected — on-prem constraint).
|
||
|
||
## Decision Outcome
|
||
|
||
### Level 1 — vendor-neutral decisions
|
||
|
||
**Branch model.** Trunk-based with `main` always deployable. Feature branches are short-lived (hours to days), named `feat/<short-slug>` or `fix/<slug>` or `chore/<slug>`. Releases happen by tagging `vX.Y.Z` on `main`.
|
||
|
||
**Merge strategy.** Squash-merge only. The squash subject is the PR title and must be a valid Conventional Commits message; the squash body inherits the PR body. This produces a clean linear history on `main` where each commit corresponds 1:1 to a PR. Rebase-merge and merge-commit are disabled at the platform level.
|
||
|
||
**Branch protection on `main`:**
|
||
|
||
- direct push: forbidden (no exceptions, including the project lead);
|
||
- force push: forbidden;
|
||
- linear history: required (consistent with squash-merge);
|
||
- required status checks: every CI gate listed below, all blocking;
|
||
- required PR review approvals: 0 in v1 (solo), revisited to ≥1 once a second active contributor joins (a follow-up ADR or amendment will mark the date);
|
||
- branch deletion after merge: required;
|
||
- merge of stale branches: PRs must be up-to-date with `main` before merging (or use a merge queue once GitLab provides one).
|
||
|
||
**Required CI gates** (every gate is blocking; failing any blocks the merge):
|
||
|
||
| Gate | What it runs | Tooling |
|
||
| ------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||
| `format` | Prettier check, no auto-fix | `prettier --check` via `pnpm nx format:check` |
|
||
| `lint` | ESLint across affected projects, including `@nx/enforce-module-boundaries` | `pnpm nx affected -t lint` |
|
||
| `type-check` | TypeScript strict, no emit | `pnpm nx affected -t type-check` |
|
||
| `test` | Unit/component tests | Vitest (front, ADR-0004) and Jest (back, ADR-0005), via `pnpm nx affected -t test` |
|
||
| `build` | Production builds of affected apps and libs | `pnpm nx affected -t build` |
|
||
| `audit` | Dependency vulnerabilities | `pnpm audit` + Trivy filesystem scan |
|
||
| `secret-scan` | Repo-wide secret detection | `gitleaks` |
|
||
| `commit-lint` | Conventional Commits validation on the PR commit range | `commitlint --from <base> --to HEAD` |
|
||
| `a11y` | Pending — defined by future a11y ADR (axe-core in e2e) | (placeholder) |
|
||
| `perf` | Pending — defined by future perf ADR (Lighthouse CI) | (placeholder) |
|
||
|
||
**Conventional Commits.** Already enforced locally via the `commit-msg` hook (ADR-0007). In CI, `commitlint --from origin/main --to HEAD` runs against the PR commit range as defense in depth — even if a contributor bypasses the local hook, the CI gate catches it.
|
||
|
||
**Signed commits.** Recommended but not required in v1. Setup overhead for contributors (GPG or SSH signing key) is non-trivial relative to the marginal value with a single contributor and a well-controlled host. The decision is revisited as part of the GitLab migration ADR (GitLab has stronger built-in tooling for centralised signing policies than current Gitea).
|
||
|
||
**Logic location — the "thin YAML" pattern.** All non-trivial CI logic lives in `package.json` scripts and Nx targets, _not_ in the workflow YAML. The YAML's role is restricted to: checkout, runtime setup, cache restoration, and calling a single high-level script per job. Concretely:
|
||
|
||
```jsonc
|
||
// package.json (excerpt — lands with the scaffold)
|
||
"scripts": {
|
||
"ci:check": "pnpm exec nx affected -t format:check lint type-check test build",
|
||
"ci:scan": "pnpm audit --audit-level=moderate && pnpm exec trivy fs --skip-dirs node_modules --exit-code 1 . && pnpm exec gitleaks detect --no-banner --redact",
|
||
"ci:commits": "pnpm exec commitlint --from $COMMIT_LINT_FROM --to HEAD --verbose"
|
||
}
|
||
```
|
||
|
||
The migration to GitLab then becomes a rewrite of the YAML wrappers (a few dozen lines) and not a re-derivation of the gates — these scripts are platform-agnostic, runnable locally, and serve as the source of truth.
|
||
|
||
**Caching.** Two cache surfaces, both portable:
|
||
|
||
- the `pnpm` store, keyed on `pnpm-lock.yaml`;
|
||
- the Nx local cache (`.nx/cache`), keyed on the project graph.
|
||
|
||
The level-2 implementation wires both via the CI vendor's cache action; the level-1 contract is "these two paths must be cached".
|
||
|
||
**Secrets policy.**
|
||
|
||
- Secrets live exclusively in CI vendor variables (Gitea → GitLab later). Naming convention: `SCOPE_PURPOSE` (e.g. `BFF_DATABASE_URL`, `OBO_CACHE_ENCRYPTION_KEY`).
|
||
- No secret ever in source. `gitleaks` enforces.
|
||
- Rotation procedures, key vault, and operator runbooks belong in the future operations / secret-management ADR.
|
||
|
||
**Container images / deployment.** Explicitly out of scope of this ADR. CI v1 builds and tests; image build and deploy will land with the on-prem infrastructure ADR (phase 3).
|
||
|
||
### Level 2 — Gitea-specific implementation
|
||
|
||
**Engine.** Gitea Actions, available since Gitea 1.19. The workflow YAML is GitHub Actions-syntax-compatible, which means most third-party actions (`actions/checkout`, `actions/setup-node`, `pnpm/action-setup`, etc.) work unchanged. This compatibility is also a partial migration hedge: the same workflows can be ported to GitHub Actions with near-zero changes if the org pivots to GitHub instead of GitLab.
|
||
|
||
**Runners.** Three self-hosted `act_runner` instances on internal infrastructure. The first runner is deployed to validate the pipeline; the second and third are added before the project hits any non-trivial PR volume. Runners are labelled `self-hosted` and `on-prem`, plus capacity labels (`size:default`) for future job differentiation. **Job container image** is `catthehacker/ubuntu:act-22.04` — Ubuntu LTS, the de facto standard image for `act`-compatible runners; bundles Node, Python, git, common build tools, and the Docker CLI, so JavaScript actions (`actions/checkout`, `actions/setup-node`, etc.) work without per-job container declarations. Labels are registered in the `<label>:docker://<image>` format; without the `docker://` suffix, `act_runner` falls back to a minimal default image and JavaScript actions fail with `Cannot find: node in PATH`. The image is pinned by tag; the `security-scheduled.yml` workflow re-pulls it weekly to pick up patches.
|
||
|
||
**Workflow file structure.**
|
||
|
||
- `.gitea/workflows/ci.yml` — runs on `pull_request` and `push` to `main`. Hosts the `check`, `scan`, and `commits` jobs.
|
||
- `.gitea/workflows/release.yml` — runs on `push` of a `vX.Y.Z` tag. Builds release artefacts. Empty stub in v1; gains content when the on-prem deploy ADR lands.
|
||
- `.gitea/workflows/security-scheduled.yml` — runs weekly via `schedule:` cron. Re-runs Trivy and gitleaks on the full tree (not just affected), and triggers Renovate (configuration covered by the security baseline ADR).
|
||
|
||
Three files; small surface; clear scope per file.
|
||
|
||
**Workflow shape (illustrative — final lands with the scaffold):**
|
||
|
||
```yaml
|
||
# .gitea/workflows/ci.yml
|
||
name: CI
|
||
on:
|
||
pull_request:
|
||
branches: [main]
|
||
push:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
check:
|
||
runs-on: [self-hosted, on-prem]
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with: { fetch-depth: 0 }
|
||
- uses: nrwl/nx-set-shas@v4
|
||
- uses: pnpm/action-setup@v3
|
||
- uses: actions/setup-node@v4
|
||
with: { node-version-file: '.nvmrc', cache: pnpm }
|
||
- run: pnpm install --frozen-lockfile
|
||
- run: pnpm ci:check
|
||
scan:
|
||
runs-on: [self-hosted, on-prem]
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: pnpm/action-setup@v3
|
||
- uses: actions/setup-node@v4
|
||
with: { node-version-file: '.nvmrc', cache: pnpm }
|
||
- run: pnpm install --frozen-lockfile
|
||
- run: pnpm ci:scan
|
||
commits:
|
||
if: github.event_name == 'pull_request'
|
||
runs-on: [self-hosted, on-prem]
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with: { fetch-depth: 0 }
|
||
- uses: pnpm/action-setup@v3
|
||
- uses: actions/setup-node@v4
|
||
with: { node-version-file: '.nvmrc', cache: pnpm }
|
||
- run: pnpm install --frozen-lockfile
|
||
- run: COMMIT_LINT_FROM=origin/main pnpm ci:commits
|
||
```
|
||
|
||
This is a sketch, not the final file — the final lands with the scaffold and any iteration on it is not by itself an ADR-worthy change.
|
||
|
||
**Branch protection (Gitea-side configuration):** `Settings → Branches → Add rule` on `main`:
|
||
|
||
- Disable force push: yes;
|
||
- Disable direct push: yes (only PR merge);
|
||
- Required status checks: `check`, `scan`, `commits`, plus future `a11y`, `perf`;
|
||
- Require pull request reviews: 0 in v1, raise to 1 when a second contributor joins;
|
||
- Require linear history: yes;
|
||
- Delete head branches after merge: yes.
|
||
|
||
### Migration to GitLab — what changes, what doesn't
|
||
|
||
When the GitLab migration happens (6–18-month horizon), a new ADR will be written that **supersedes only the level-2 sections** of this ADR. The level-1 decisions stand unchanged. Concretely the migration touches:
|
||
|
||
- `.gitea/workflows/*.yml` → `.gitlab-ci.yml` (rewrite — the _gates_ are the same, the _DSL_ is different). Estimate: 1–2 days.
|
||
- Self-hosted `act_runner` → GitLab Runner. Estimate: 0.5–1 day.
|
||
- Gitea branch protection rules → GitLab merge request approval rules. Same concepts, different UI. Estimate: a few hours.
|
||
- Secrets re-creation in GitLab CI/CD variables. Same naming convention, copy values. Estimate: a few hours.
|
||
|
||
The `package.json` scripts (`ci:check`, `ci:scan`, `ci:commits`), the Nx workspace, the dependency manifest, the entire code surface, and the level-1 decisions are unchanged.
|
||
|
||
### Consequences
|
||
|
||
- Good, because the gates are written once and run twice (locally via hooks, in CI as defense). Drift is impossible without breaking both layers.
|
||
- Good, because thin YAML keeps CI reproducible locally — anyone can run `pnpm ci:check` to mirror what the runner does.
|
||
- Good, because Gitea Actions' GHA-compatible syntax doubles as a hedge: the same YAML can land on GitHub Actions if the org's plans change again.
|
||
- Good, because squash-merge produces a Conventional-Commits-only history on `main` — clean changelog generation, predictable release notes, smooth semver inference.
|
||
- Good, because branch protection is enforced at the platform level, not at convention level.
|
||
- Good, because the migration weight is bounded and transparent: ~3–5 days of dev/ops work, no code rewrite.
|
||
- Bad, because the level-2 sections will need to be rewritten at the GitLab migration. This is the explicit trade-off; it is accepted because the alternatives (going GitLab now without the existing tooling, or staying CI-less until GitLab) cost more.
|
||
- Bad, because trunk-based with required-reviewers=0 in v1 leaves the project lead as the sole gatekeeper. Mitigated by mandating green CI (which the lead cannot bypass without rewriting branch protection — a deliberate, audit-visible action).
|
||
- Bad, because `act_runner` is younger than GitLab Runner; expect occasional rough edges, especially around large action ecosystems. Mitigated by pinning third-party actions by SHA and cadence-rebuilding the runner image.
|
||
- Bad, because deferring signed commits to v2 means the v1 history won't carry attribution-grade signatures. Reasonable for a small team; revisit at GitLab migration.
|
||
|
||
### Confirmation
|
||
|
||
- `.gitea/workflows/ci.yml`, `release.yml`, `security-scheduled.yml` exist with the structure above. The `release.yml` may be a stub until the deploy ADR lands.
|
||
- `package.json` exposes `ci:check`, `ci:scan`, `ci:commits` scripts. Each is runnable locally and produces the same exit code as the CI job.
|
||
- Gitea branch protection on `main` has the rules above; configuration is documented in `docs/operations/branch-protection.md` (created with the scaffold).
|
||
- `Trivy`, `gitleaks`, and `commitlint` are in `devDependencies` (or available as actions pinned by SHA — both acceptable).
|
||
- At least three `act_runner` instances are registered to the org; their bootstrap and update procedure live in an operations doc.
|
||
- CI exits non-zero on any gate failure; no gate is `continue-on-error: true`.
|
||
- Runner images are pinned by SHA in the workflows. The `security-scheduled.yml` job rebuilds the runner image weekly and reports to the security audit feed.
|
||
- A future migration ADR (GitLab) explicitly references this ADR, supersedes only level 2, and inherits level 1.
|
||
|
||
## Pros and Cons of the Options
|
||
|
||
### Branch / merge strategy
|
||
|
||
#### Trunk-based + squash-merge (chosen)
|
||
|
||
- Good, because clean linear history on `main`, one squash commit per PR.
|
||
- Good, because Conventional Commits + squash-merge yields a directly machine-readable changelog.
|
||
- Good, because feature branches stay short-lived — pressure against long-lived branches becomes structural, not cultural.
|
||
- Bad, because contributors lose granular commit history on the merged branch (the squash collapses it). Mitigated: the PR retains the full history for review purposes.
|
||
|
||
#### Trunk-based + rebase-merge
|
||
|
||
- Good, because preserves individual commits without merge bubbles.
|
||
- Bad, because contributors must groom every commit to be CI-clean (each commit must compile and pass tests if we want a clean bisect history). High discipline cost; squash-merge gets most of the benefit at lower cost.
|
||
|
||
#### Trunk-based + merge commit
|
||
|
||
- Good, because preserves the full history, including the branch topology.
|
||
- Bad, because produces messy merge bubbles on `main`; conflicts with the "linear history" branch protection.
|
||
|
||
#### GitFlow
|
||
|
||
- Good, because release branches isolate stabilisation.
|
||
- Bad, because heavy for a continuously deployable monorepo; redundant with semver tags on a trunk-based main; introduces the "long-lived `develop` branch" anti-pattern.
|
||
|
||
### CI engine (level 2)
|
||
|
||
#### Gitea Actions (chosen)
|
||
|
||
- Good, because built-in to Gitea, no extra deployment.
|
||
- Good, because GitHub Actions syntax means transferable skills and a partial portability hedge.
|
||
- Good, because actively developed by the Gitea team.
|
||
- Bad, because younger ecosystem than GitLab CI or GitHub Actions proper — expect occasional rough edges.
|
||
|
||
#### Drone CI
|
||
|
||
- Good, because mature, lean, opinionated.
|
||
- Bad, because separate deployment and operational surface; YAML is Drone-specific (less portable than GHA-compatible Gitea Actions).
|
||
|
||
#### Concourse / Tekton / Buildkite
|
||
|
||
- Good, because powerful for complex pipelines.
|
||
- Bad, because over-engineered for the v1 scope, and orthogonal to the Gitea/GitLab decision axis.
|
||
|
||
### Required reviewer count
|
||
|
||
#### 0 in v1, raise later (chosen)
|
||
|
||
- Good, because doesn't block solo development.
|
||
- Good, because the green-CI requirement still prevents the project lead from merging broken code without effort.
|
||
- Bad, because relies on the project lead's discipline (and CI's correctness) for code quality.
|
||
|
||
#### 1 always
|
||
|
||
- Good, because rigour.
|
||
- Bad, because blocks if there's only one contributor — would force the project lead to merge their own PRs by overriding protection, which is the opposite of the intended behaviour.
|
||
|
||
### Signed commits
|
||
|
||
#### Optional in v1, revisited at GitLab migration (chosen)
|
||
|
||
- Good, because no setup overhead for contributors during a phase where the priority is shipping the structural ADRs and scaffolding.
|
||
- Bad, because v1 history carries no attribution-grade signatures. Acceptable: the host (Gitea) records the user identity on each commit.
|
||
|
||
#### Required in v1
|
||
|
||
- Good, because rigorous attribution from day one.
|
||
- Bad, because every contributor must set up GPG or SSH signing — high friction for early stages.
|
||
|
||
## More Information
|
||
|
||
- Gitea Actions: https://docs.gitea.com/usage/actions/overview
|
||
- `act_runner`: https://gitea.com/gitea/act_runner
|
||
- GitHub Actions reference (compatible with Gitea Actions): https://docs.github.com/actions
|
||
- Conventional Commits: https://www.conventionalcommits.org/
|
||
- commitlint: https://commitlint.js.org/
|
||
- Trivy: https://github.com/aquasecurity/trivy
|
||
- gitleaks: https://github.com/gitleaks/gitleaks
|
||
- `nrwl/nx-set-shas`: https://github.com/nrwl/nx-set-shas
|
||
- Related ADRs: [ADR-0002](0002-adopt-nx-monorepo-apps-preset.md) (Nx workspace + `affected`), [ADR-0007](0007-pre-commit-hooks-and-conventional-commits.md) (local hooks + commitlint config), and the future ADRs for security baseline (Trivy / gitleaks / Renovate config), accessibility baseline (`a11y` gate), performance budgets (`perf` gate), on-prem infrastructure stack (deploy pipeline, runners hosting), and GitLab migration (level-2 supersession).
|