Files
fete/specs/003-cicd-pipeline/research.md
nitrix 6aeb4b8bca Migrate project artifacts to spec-kit format
- Move cross-cutting docs (personas, design system, implementation phases,
  Ideen.md) to .specify/memory/
- Move cross-cutting research and plans to .specify/memory/research/ and
  .specify/memory/plans/
- Extract 5 setup tasks from spec/setup-tasks.md into individual
  specs/001-005/spec.md files with spec-kit template format
- Extract 20 user stories from spec/userstories.md into individual
  specs/006-026/spec.md files with spec-kit template format
- Relocate feature-specific research and plan docs into specs/[feature]/
- Add spec-kit constitution, templates, scripts, and slash commands
- Slim down CLAUDE.md to Claude-Code-specific config, delegate principles
  to .specify/memory/constitution.md
- Update ralph.sh with stream-json output and per-iteration logging
- Delete old spec/ and docs/agents/ directories
- Gitignore Ralph iteration JSONL logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:19:41 +01:00

11 KiB

date, git_commit, branch, topic, tags, status
date git_commit branch topic tags status
2026-03-04T18:19:10.241698+00:00 316137bf1c master T-3: CI/CD Pipeline — Gitea Actions
research
codebase
ci-cd
gitea
docker
pipeline
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:

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.