CI/CD & GitOps

Gitea

Self-hosted git as the source of truth - every change is a branch and a pull request into a protected main, opened through exactly one script whether a person or the automation is doing it.

What it is and whyHow I use it inArchitectureLessons learned and gotchasImpactFurther reading

What it is & why I use it

Gitea is the self-hosted git service at the center of AutomationLab. It is where the truth lives: pipeline definitions, infrastructure code, configuration, and this site all sit in Gitea repositories, and nothing reaches the platform except through a merge into a protected branch. If it is not in git, it is not real.

I self-host it because the source of truth for a self-contained platform should itself be part of the platform - not a dependency on an outside service. Owning the git server means owning the whole change-control story end to end.

How I use it in the lab

  • Branch per change, PR to merge. Every change is a feature branch and a pull request into main; direct pushes to the protected branch are not the path.
  • Branch protection with required checks. Merges are gated on the security and lint checks passing, so main stays green and reviewed by construction.
  • Automation opens PRs too. The tooling that assists development opens pull requests through the API, held to the same protections as any other contributor.
  • The hub the rest of the flow trusts. The GitOps reconciler and CI both take Gitea as their input, so the git server is the single upstream everything else follows.

Architecture

Gitea architecture: Protected change path A branch becomes a pull request, required checks report their results, branch protection controls the merge, and CI and GitOps consume protected main. ARCHITECTURE FLOW PROTECTED CHANGE PATH Branch: A scoped change starts here BRANCH A scoped change starts here Pull request: Review and discussion happen here PULL REQUEST Review and discussion happen here Required checks: CI reports pass or fail REQUIRED CHECKS CI reports pass or fail Protected main: Merge policy controls admission PROTECTED MAIN Merge policy controls admission CI and GitOps: Consume the accepted change CI AND GITOPS Consume the accepted change Cyan. Automated step or path. Amber. Operator or policy gate. Green. Recorded or healthy outcome. Dashed box. Stored state or record. Rounded box. Actor or process. Arrow. Direction of work or data.
The change path runs from a branch through review and required checks into protected main, then on to CI and GitOps. Hostnames and repository details are omitted.

Lessons learned & gotchas

Gotcha - the credential that pushes cannot call the API The one that cost me a genuinely confusing afternoon: the OS/browser git credential helper authenticates git push perfectly, but the same credential returns 401 against the REST API used to open a pull request. It is scoped to git transport operations, not to the API surface, so "my git works, why is the API rejecting me?" has a real answer. The fix is to stop reusing the git credential for API calls entirely: automation authenticates to the API with a dedicated personal access token, fetched from the secrets manager at run time and never written to disk. Push and API are two different doors with two different keys.
  • One canonical way to open a PR. Rather than hand-curling the API and rediscovering the 401 every time, the lab has a single PR-opening path that pulls the right token and does it correctly - so the gotcha is solved once, in one place.
  • Keep the token out of the command line. An API token pasted as an argument leaks into shell history and process listings. The helper fetches it and uses it in memory; it never becomes a visible argument or a file.
  • Protect the branch, then honour it everywhere. Branch protection only means something if automation is subject to it too - the moment a bot can bypass the gate, the gate is decorative.

Impact

PR-only
path to main
1 way
to open a PR, done right
Token
from the secrets manager

Gitea is the anchor the whole change-control story hangs from: branch, review, gate, merge - for people and machines alike. The auth gotcha is a small, sharp reminder that "it authenticated once" does not mean "it is authorized for what you are about to do" - scope matters, and the clean fix is a purpose-scoped credential, not reusing the one you already had.

Further reading