T-3: add Gitea Actions CI/CD pipeline
Single workflow with three jobs: - backend-test: JDK 25, ./mvnw -B verify (Checkstyle, JUnit, ArchUnit, SpotBugs) - frontend-test: Node 24, lint, type generation, type-check, unit tests, production build - build-and-publish: Buildah image build + push with rolling SemVer tags (only on vX.Y.Z tags) Includes research report and implementation plan. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
213
docs/agents/research/2026-03-04-t3-cicd-pipeline.md
Normal file
213
docs/agents/research/2026-03-04-t3-cicd-pipeline.md
Normal file
@@ -0,0 +1,213 @@
|
||||
---
|
||||
date: "2026-03-04T18:19:10.241698+00:00"
|
||||
git_commit: 316137bf1c391577e884ce525af780f45e34da86
|
||||
branch: master
|
||||
topic: "T-3: CI/CD Pipeline — Gitea Actions"
|
||||
tags: [research, codebase, ci-cd, gitea, docker, pipeline]
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Research: T-3 CI/CD Pipeline
|
||||
|
||||
## Research Question
|
||||
|
||||
What is the current state of the project relevant to implementing T-3 (CI/CD pipeline with Gitea Actions), what are its requirements, dependencies, and what infrastructure already exists?
|
||||
|
||||
## Summary
|
||||
|
||||
T-3 requires a Gitea Actions workflow in `.gitea/workflows/` that runs on every push: tests both backend and frontend, builds a Docker image, and publishes it to the Gitea container registry. The task is currently unstarted but all dependencies (T-1, T-2) are completed. The project already has a working multi-stage Dockerfile, comprehensive test suites for both backend and frontend, and clearly defined build commands. No CI/CD configuration files exist yet.
|
||||
|
||||
## Detailed Findings
|
||||
|
||||
### T-3 Specification
|
||||
|
||||
Defined in `spec/setup-tasks.md:41-55`.
|
||||
|
||||
**Acceptance Criteria (all unchecked):**
|
||||
1. Gitea Actions workflow file in `.gitea/workflows/` runs on push: test, build, publish Docker image
|
||||
2. Backend tests run via Maven
|
||||
3. Frontend tests run via Vitest
|
||||
4. Docker image is published to the Gitea container registry on the same instance
|
||||
5. Pipeline fails visibly if any test fails or the build breaks
|
||||
6. Docker image is only published if all tests pass and the build succeeds
|
||||
|
||||
**Dependencies:** T-1 (completed), T-2 (completed)
|
||||
|
||||
**Platform Decision (Q-5):** Per `.ralph/review-findings/questions.md:12-22`, Gitea is confirmed as the exclusive CI/CD platform. Only Gitea infrastructure will be used — Gitea Actions for pipelines, Gitea container registry for Docker image publishing.
|
||||
|
||||
### Dependencies — What Already Exists
|
||||
|
||||
#### Dockerfile (repo root)
|
||||
|
||||
A working 3-stage Dockerfile exists (`Dockerfile:1-26`):
|
||||
|
||||
| Stage | Base Image | Purpose |
|
||||
|-------|-----------|---------|
|
||||
| `frontend-build` | `node:24-alpine` | `npm ci` + `npm run build` (includes OpenAPI type generation) |
|
||||
| `backend-build` | `eclipse-temurin:25-jdk-alpine` | Maven build with frontend assets baked into `static/` |
|
||||
| `runtime` | `eclipse-temurin:25-jre-alpine` | `java -jar app.jar`, exposes 8080, HEALTHCHECK configured |
|
||||
|
||||
The Dockerfile skips tests and static analysis during build (`-DskipTests -Dcheckstyle.skip -Dspotbugs.skip` at `Dockerfile:17`), with the explicit design rationale that quality gates belong in CI (T-3).
|
||||
|
||||
The OpenAPI spec is copied into the frontend build stage (`Dockerfile:8-9`) because `npm run build` triggers `generate:api` as a pre-step.
|
||||
|
||||
#### .dockerignore
|
||||
|
||||
`.dockerignore:18-19` already excludes `.gitea/` from the Docker build context, anticipating this directory's creation.
|
||||
|
||||
#### Backend Build & Test
|
||||
|
||||
- **Build:** `./mvnw package` (or `./mvnw -B package` for batch mode)
|
||||
- **Test:** `./mvnw test`
|
||||
- **Full verify:** `./mvnw verify` (includes Checkstyle, SpotBugs, ArchUnit, JUnit 5)
|
||||
- **Config:** `backend/pom.xml` — Spring Boot 3.5.11, Java 25
|
||||
- **Quality gates:** Checkstyle (Google Style, `validate` phase), SpotBugs (`verify` phase), ArchUnit (9 rules, runs with JUnit), Surefire (fail-fast at 1 failure)
|
||||
|
||||
#### Frontend Build & Test
|
||||
|
||||
- **Build:** `npm run build` (runs `generate:api` + parallel type-check and vite build)
|
||||
- **Test:** `npm run test:unit` (Vitest)
|
||||
- **Lint:** `npm run lint` (oxlint + ESLint)
|
||||
- **Type check:** `vue-tsc --build`
|
||||
- **Config:** `frontend/package.json` — Vue 3.5, Vite 7.3, TypeScript 5.9
|
||||
- **Node engines:** `^20.19.0 || >=22.12.0`
|
||||
|
||||
### Scheduling and Parallelism
|
||||
|
||||
Per `spec/implementation-phases.md:15`, T-3 is at priority level 4* — parallelizable with T-4 (Development infrastructure). It can be implemented at any time now since T-1 and T-2 are done.
|
||||
|
||||
From `review-findings.md:105-107`: T-3 should be completed before the first user story is finished, otherwise code will exist without a pipeline.
|
||||
|
||||
### Gitea Actions Specifics
|
||||
|
||||
Gitea Actions uses the same YAML format as GitHub Actions with minor differences:
|
||||
|
||||
- Workflow files go in `.gitea/workflows/` (not `.github/workflows/`)
|
||||
- Uses `runs-on: ubuntu-latest` or custom runner labels
|
||||
- Container registry URL pattern: `{gitea-host}/{owner}/{repo}` (no separate registry domain)
|
||||
- Supports `docker/login-action`, `docker/build-push-action`, and direct `docker` CLI
|
||||
- Secrets are configured in the Gitea repository settings (e.g., `secrets.GITHUB_TOKEN` equivalent is typically `secrets.GITEA_TOKEN` or the built-in `gitea.token`)
|
||||
|
||||
### Build Commands for the Pipeline
|
||||
|
||||
The pipeline needs to execute these commands in order:
|
||||
|
||||
| Step | Command | Runs in |
|
||||
|------|---------|---------|
|
||||
| Backend test | `cd backend && ./mvnw -B verify` | JDK 25 environment |
|
||||
| Frontend install | `cd frontend && npm ci` | Node 24 environment |
|
||||
| Frontend lint | `cd frontend && npm run lint` | Node 24 environment |
|
||||
| Frontend type-check | `cd frontend && npm run type-check` | Node 24 environment (needs OpenAPI spec) |
|
||||
| Frontend test | `cd frontend && npm run test:unit -- --run` | Node 24 environment |
|
||||
| Docker build | `docker build -t {registry}/{owner}/{repo}:{tag} .` | Docker-capable runner |
|
||||
| Docker push | `docker push {registry}/{owner}/{repo}:{tag}` | Docker-capable runner |
|
||||
|
||||
Note: `npm run build` is implicitly tested by the Docker build stage (the Dockerfile runs `npm run build` in stage 1). Running it separately in CI is redundant but could provide faster feedback.
|
||||
|
||||
The `--run` flag on Vitest ensures it runs once and exits (non-watch mode).
|
||||
|
||||
### Container Registry
|
||||
|
||||
The Gitea container registry is built into Gitea. Docker images are pushed using the Gitea instance hostname as the registry. Authentication uses a Gitea API token or the built-in `GITHUB_TOKEN`-equivalent that Gitea Actions provides.
|
||||
|
||||
Push format: `docker push {gitea-host}/{owner}/{repo}:{tag}`
|
||||
|
||||
### What Does NOT Exist Yet
|
||||
|
||||
- No `.gitea/` directory
|
||||
- No `.github/` directory
|
||||
- No workflow YAML files anywhere
|
||||
- No CI/CD configuration of any kind
|
||||
- No Makefile or build orchestration script
|
||||
- No documentation about the Gitea instance URL or registry configuration
|
||||
|
||||
## Code References
|
||||
|
||||
- `spec/setup-tasks.md:41-55` — T-3 specification and acceptance criteria
|
||||
- `spec/implementation-phases.md:15` — T-3 scheduling (parallel with T-4)
|
||||
- `.ralph/review-findings/questions.md:12-22` — Q-5 resolution (Gitea confirmed)
|
||||
- `Dockerfile:1-26` — Multi-stage Docker build
|
||||
- `Dockerfile:17` — Tests skipped in Docker build (deferred to CI)
|
||||
- `.dockerignore:18-19` — `.gitea/` already excluded from Docker context
|
||||
- `backend/pom.xml:56-168` — Maven build plugins (Checkstyle, Surefire, SpotBugs, OpenAPI generator)
|
||||
- `frontend/package.json:6-17` — npm build and test scripts
|
||||
- `CLAUDE.md` — Build commands reference table
|
||||
|
||||
## Architecture Documentation
|
||||
|
||||
### Pipeline Architecture Pattern
|
||||
|
||||
The project follows a "test in CI, skip in Docker" pattern:
|
||||
- The Dockerfile is a pure build artifact — it produces a runnable image as fast as possible
|
||||
- All quality gates (tests, linting, static analysis) are expected to run in the CI pipeline before the Docker build
|
||||
- The Docker image is only published if all preceding steps pass
|
||||
|
||||
### Gitea Actions Workflow Pattern (GitHub Actions compatible)
|
||||
|
||||
Gitea Actions workflows follow the same `on/jobs/steps` YAML structure as GitHub Actions. The runner is a self-hosted instance with Docker available on the host, but the pipeline uses Buildah for container image builds to avoid Docker-in-Docker complexity.
|
||||
|
||||
### Image Tagging Strategy (resolved)
|
||||
|
||||
SemVer with rolling tags, following standard Docker convention. When a tag like `2.3.9` is pushed, the pipeline publishes the **same image** under all four tags:
|
||||
|
||||
| Tag | Type | Example | Updated when |
|
||||
|-----|------|---------|-------------|
|
||||
| `2.3.9` | Immutable | Exact version | Only once, on this release |
|
||||
| `2.3` | Rolling | Latest `2.3.x` | Overwritten by any `2.3.x` release |
|
||||
| `2` | Rolling | Latest `2.x.x` | Overwritten by any `2.x.x` release |
|
||||
| `latest` | Rolling | Newest release | Overwritten by every release |
|
||||
|
||||
This means the pipeline does four pushes per release (one per tag). Users can pin `2` to get automatic minor and patch updates, or pin `2.3.9` for exact reproducibility.
|
||||
|
||||
Images are only published on tagged releases (Git tags matching a SemVer pattern), not on every push.
|
||||
|
||||
### Release Process (resolved)
|
||||
|
||||
Manual Git tags trigger releases. The workflow is:
|
||||
|
||||
```bash
|
||||
git tag 1.2.3
|
||||
git push --tags
|
||||
```
|
||||
|
||||
The pipeline triggers on tags matching a SemVer pattern (e.g., `1.2.3`) and publishes the image with rolling SemVer tags. No `v` prefix — tags are pure SemVer. No automated versioning tools (release-please, semantic-release) — the developer decides the version.
|
||||
|
||||
### Container Build Tool (resolved)
|
||||
|
||||
Buildah is used instead of Docker for building and pushing container images. This avoids Docker-in-Docker issues entirely and works cleanly on self-hosted runners regardless of whether the runner process runs inside a container or on the host.
|
||||
|
||||
### Pipeline Quality Scope (resolved)
|
||||
|
||||
The pipeline runs the maximum quality gates available:
|
||||
- **Backend:** `./mvnw verify` (full lifecycle — Checkstyle, JUnit 5, ArchUnit, SpotBugs)
|
||||
- **Frontend:** Lint (`npm run lint`), type-check (`npm run type-check`), tests (`npm run test:unit -- --run`)
|
||||
|
||||
### Gitea Registry Authentication (researched)
|
||||
|
||||
Key findings from Gitea documentation and community:
|
||||
- `GITHUB_TOKEN` / `GITEA_TOKEN` (the built-in runner token) does **not** have permissions to push to the Gitea container registry. It only has repository read access.
|
||||
- A **Personal Access Token** (PAT) with package write permissions must be created and stored as a repository secret (e.g., `REGISTRY_TOKEN`).
|
||||
- The registry URL is the Gitea instance hostname (e.g., `gitea.example.com`). `${{ github.server_url }}` provides it with protocol prefix — needs stripping or use a repository variable.
|
||||
- Push URL format: `{registry-host}/{owner}/{repo}:{tag}`
|
||||
|
||||
### Reference Workflow (existing project)
|
||||
|
||||
An existing Gitea Actions workflow in the sibling project `../arr/.gitea/workflows/deploy.yaml` provides a reference:
|
||||
- Runner label: `ubuntu-latest`
|
||||
- Uses `${{ vars.DEPLOY_PATH }}` for repository variables
|
||||
- Simple deploy pattern (git pull + docker compose up)
|
||||
|
||||
## Resolved Questions
|
||||
|
||||
1. **Gitea instance URL:** Configured via repository variable or derived from `${{ github.server_url }}`. The registry hostname is the same as the Gitea instance.
|
||||
2. **Runner label:** `ubuntu-latest` (consistent with the existing `arr` project workflow).
|
||||
3. **Runner setup:** Self-hosted runner on the host with Docker available. Buildah used for image builds to avoid DinD.
|
||||
4. **Container build tool:** Buildah (no DinD needed).
|
||||
5. **Image tagging strategy:** SemVer with rolling tags (`latest`, `2`, `2.3`, `2.3.9`). Published only on tagged releases.
|
||||
6. **Branch protection / publish trigger:** Images are only published from tagged releases, not from branch pushes. Every push triggers test + build (without publish).
|
||||
7. **Maven lifecycle scope:** `./mvnw verify` (full lifecycle including SpotBugs). Frontend also runs all available quality gates (lint, type-check, tests).
|
||||
8. **Registry authentication:** Personal Access Token stored as repository secret (built-in `GITHUB_TOKEN` lacks package write permissions).
|
||||
|
||||
## Open Questions
|
||||
|
||||
None — all questions resolved.
|
||||
Reference in New Issue
Block a user