# Software factory architecture GitHub-style CI for Phyllome OS, self-hosted on the project's own forge and iron. ## Goal Runs CI/CD for Phyllome OS repos on `git.phyllo.me` (Gitea + Gitea Actions) with a self-hosted runner, without depending on any third-party CI service. ## Components ```mermaid flowchart LR subgraph forge["git.phyllo.me (Cloudron app, Gitea 1.40.2)"] act[Gitea Actions] pkg[(Package Registry)] rel[Releases] end subgraph runbox["runner VM (on the phyllome Cloudron host)"] r1[gitea-runner daemon] host[HOST labels: run-on-host jobs
mock / livemedia-creator / pykickstart] img[CONTAINER labels:
docker://git.phyllo.me/devops/fedora-runner-image] end repo[Product repos: roots/* , devops/*]
.gitea/workflows/*.yml → runs-on: fedora dev[devcontainer git.phyllo.me/roots/fedora-iso-creation:42] repo --> act act -->|picks runner| r1 r1 --> host r1 --> img act --> pkg act --> rel dev --> act ``` ## How a run works 1. A push/PR/tag to a Product repo triggers Gitea Actions. 2. `runs-on: fedora` matches the self-hosted runner labeled `fedora`. 3. Two execution modes, chosen per job: - **container** — steps run inside `devops/fedora-runner-image` (clean, hermetically sealed; good for pure build/lint/test jobs). - **host** — steps run directly on the runner VM (needed for jobs that rely on `mock`/QEMU nesting, kernel-level features, or the local build cache). 4. Steps use the mirrored actions (`devops/checkout@v5`, `devops/action-gh-release@v2`) so nothing reaches out to github.com at runtime. ## Products and their pipelines | Repo | Pipeline | Job type | Notes | |---|---|---|---| | `roots/phyllomeos` | `make lint` → `make test` → `make validate` | container | pykickstart data pipeline; generated recipes/dishes are untracked build products | | `roots/xml-definition-for-domains` | xmllint check | container | libvirt XML definitions | | `roots/rpm-sources` | mock smoke build | host | needs mock chroots (host-only cache) | | `devops/create-iso` | mock + livemedia-creator ISO build → release | host | already has a workflow (label `fedora`) | | `devops/test-actions` | checkout smoke tests | both | CI playground, already label `fedora` | ## Image inventory (Package Registry) | Image | Used by | |---|---| | `devops/fedora-runner-image:latest` | default job container on the runner | | `devops/fedora-oci` | base Fedora image | | `devops/qemu-libvirt-oci-image` | QEMU/libvirt tooling (VM-in-CI experiments) | | `roots/fedora-iso-creation:42` | devcontainer referenced by test workflows | ## Security posture - **Self-contained actions**: all `uses:` point at `git.phyllo.me/...` mirrors — no third-party action code, no github.com calls. - **Runner host**: dedicated VM on the phyllome Cloudron host; `gitea-runner` v3.5.0 runs as unprivileged `act_runner` system user; Docker access for container jobs only. Registered site-wide with label `fedora` (runs-on: fedora); the VM runs Fedora 44. - **Secrets**: CI secrets live in Gitea (repo/org) — never in the runner config. Registration token is UI-generated and consumed once. - **Read-only infra automation**: the `automation` repo's MCP servers scope Cloudron/Gitea reads and writes by credential pair (read token / write token), and opencode gates writes behind a dedicated `ops` agent.