186 lines
12 KiB
Markdown
186 lines
12 KiB
Markdown
---
|
|
name: devsecops-ci-cd-gitea-3darch
|
|
description: >
|
|
Generate and maintain Gitea Actions CI/CD pipelines that build & publish Docker images and
|
|
Helm charts for every application in a pnpm + Turborepo monorepo, pushing both to a Harbor
|
|
registry. Use when adding or fixing a `.gitea/workflows/*` pipeline; building or pushing
|
|
container images from `applications-backend/<svc>/Dockerfile` or
|
|
`applications-frontend/<app>/Dockerfile`; packaging and pushing OCI Helm charts from
|
|
`helm-charts/<chart>/`; wiring `docker login` / `helm registry login` against Harbor with a
|
|
robot account; setting the Gitea Actions secrets and variables a pipeline needs; or debugging
|
|
a runner that can't authenticate, can't find Docker, or can't reach an action — even when the
|
|
user only says "the CI/CD thing", "the docker pipeline", or names their monorepo.
|
|
license: Proprietary
|
|
metadata:
|
|
author: home-v1-skills-code-agent
|
|
version: "1.0"
|
|
spec: agentskills.io/specification
|
|
compatibility: >
|
|
Targets Gitea Actions (act_runner, GitHub-Actions-compatible) pushing to a Harbor OCI
|
|
registry, for pnpm + Turborepo monorepos (see the development-monorepo-pnpm skill). Requires
|
|
a runner whose jobs can run docker and helm. Designed for Claude Code, Cline, GitHub Copilot,
|
|
OpenAI Codex, and other compatible agents.
|
|
---
|
|
|
|
# DevSecOps CI/CD — Gitea Actions → Harbor
|
|
|
|
## 3D Architecture Wizzard Project Adoption
|
|
|
|
This is the primary project-adopted skill for **3D Architecture Wizzard** (`3darch`).
|
|
|
|
- Central source: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/devsecops-ci-cd-gitea
|
|
- Source branch: `test`
|
|
- Source commit: `8f46c3faa5ba074b8a48971b87365e0d3e2a4c40`
|
|
- Adopted repository: https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/devsecops-ci-cd-gitea-3darch
|
|
- Application: https://gitea.lego-cloud.eu/corp-v1-3darch/corp-v1-3darch
|
|
- Documentation: https://gitea.lego-cloud.eu/corp-v1-3darch/corp-v1-3darch-documentation
|
|
- Environment namespace: `CORP_V1_3DARCH_*`
|
|
- Global diagram skill: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/diagrams-drawio
|
|
- Global glossary skill: https://gitea.lego-cloud.eu/home-v1-skills-code-agent/corp-v1-glossary
|
|
- Project automation: none; no scheduler job is authorized.
|
|
|
|
Project engineering peers:
|
|
- `development-branching-strategy-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-branching-strategy-3darch
|
|
- `development-gitops-argo-cd-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-gitops-argo-cd-3darch
|
|
- `development-monorepo-pnpm-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-monorepo-pnpm-3darch
|
|
- `development-scripts-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/development-scripts-3darch
|
|
- `devsecops-ci-cd-gitea-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/devsecops-ci-cd-gitea-3darch
|
|
- `documentation-docusaurus-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/documentation-docusaurus-3darch
|
|
- `template-engine-copier-3darch` — https://gitea.lego-cloud.eu/corp-v1-3darch-skills-code-agent/template-engine-copier-3darch
|
|
|
|
|
|
Generate the pipeline that turns a monorepo push into **published images and charts**: for
|
|
every app that has a `Dockerfile`, build and push its image; for every chart under
|
|
`helm-charts/`, package and push it as an OCI artifact. Both land in **Harbor**, authenticated
|
|
by a project **robot account** whose credential lives in Gitea Actions secrets/variables.
|
|
|
|
This skill owns the pipeline up to *"the artifact is in Harbor"*. Deploying that artifact to
|
|
the cluster is the **development-gitops-argo-cd** skill's job — see [Handoff](#handoff).
|
|
|
|
## When to use — and which reference to open
|
|
|
|
| The task | Read |
|
|
|---|---|
|
|
| Wire or fix registry login (`docker login` / `helm registry login`), robot accounts, the Gitea secret/variable names, or the "bake into the runner" alternative | `references/harbor-auth.md` |
|
|
| Understand or edit the pipeline itself — jobs, discovery loops, tag derivation, PR-vs-publish gating, `turbo prune` | `references/pipeline-workflow.md` |
|
|
| Confirm where Dockerfiles and charts live, how image/chart names are derived, which Harbor project to push to | `references/conventions.md` |
|
|
| Drop a working pipeline into a repo | copy `assets/workflows/ci-cd.yaml` → `<repo>/.gitea/workflows/ci-cd.yaml` |
|
|
|
|
## The mental model
|
|
|
|
```text
|
|
git push ─▶ act_runner ─▶ [ discover apps ]
|
|
├─ applications-backend/<svc>/Dockerfile ─▶ docker build (repo root) ─▶ push harbor/<project>/<svc>:<tag>
|
|
├─ applications-frontend/<app>/Dockerfile ─▶ docker build (repo root) ─▶ push harbor/<project>/<app>:<tag>
|
|
└─ helm-charts/<chart>/Chart.yaml ─▶ helm package ─▶ push oci://harbor/<project>/charts
|
|
```
|
|
|
|
Everything the runner needs to authenticate comes from **one org secret + two global
|
|
variables** (below). Nothing sensitive is committed to the repo.
|
|
|
|
## 1. Registry authentication (the "docker thing")
|
|
|
|
Harbor push/pull uses a **project-scoped robot account** — a non-human account with a long
|
|
username and a generated secret. The pipeline logs in with it; it never uses a person's
|
|
credentials. Full model and how the robot is created (palantir `harbor:robot:*` tasks) →
|
|
`references/harbor-auth.md`.
|
|
|
|
The pipeline reads exactly three values:
|
|
|
|
| Kind | Name | Scope in Gitea | Example value |
|
|
|---|---|---|---|
|
|
| Variable | `HL_V1_HARBOR_ADDRESS` | **global** (Site Admin → Actions → Variables) | `harbor-v1.apps.lego-cloud.eu` |
|
|
| Variable | `HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_USERNAME` | **global** | `robot$gondor-v1+gitea-actions-v1` |
|
|
| Secret | `HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_SECRET` | **org** (per org, Settings → Actions → Secrets) | *(the robot's generated secret)* |
|
|
|
|
Why this split: the username and address are **not secret**, so they go in *variables* — and
|
|
variables have a **global** level, so you set them once for all organisations. Gitea has **no
|
|
global-secret level**, so the one genuinely sensitive value is an **org secret** — the single
|
|
per-org chore when you onboard a new org.
|
|
|
|
Login, in every job that touches the registry:
|
|
|
|
```bash
|
|
echo "${HARBOR_SECRET}" | docker login "${HARBOR_ADDRESS}" -u "${HARBOR_USERNAME}" --password-stdin
|
|
echo "${HARBOR_SECRET}" | helm registry login "${HARBOR_ADDRESS}" -u "${HARBOR_USERNAME}" --password-stdin
|
|
```
|
|
|
|
> **Alternative — bake the login into the runner.** If you would rather not attach a secret per
|
|
> org at all, pre-seed the runner's Docker `config.json` so every job is pre-authenticated. It
|
|
> trades central rotation for zero per-org setup. Steps in `references/harbor-auth.md` §4.
|
|
|
|
## 2. What gets built, and from where
|
|
|
|
Discovery is **convention-driven** — the pipeline finds work, you don't list it. Details and
|
|
the naming rules in `references/conventions.md`; the essentials:
|
|
|
|
- **Images**: one per `applications-backend/*/Dockerfile` and `applications-frontend/*/Dockerfile`.
|
|
The image repository is the **application directory name**. Build context is the
|
|
**repository root** (`docker build -f <path>/Dockerfile … .`) because the Dockerfile runs
|
|
`turbo prune` over the whole monorepo — building from the app directory **will fail**.
|
|
- **Charts**: one per `helm-charts/*/Chart.yaml`. Charts are **centralized under `helm-charts/`
|
|
at the repo root**, *not* co-located inside the app directory. Do not look for
|
|
`applications-frontend/<app>/helm` — that is not where charts live in this monorepo family.
|
|
- **Tag**: the git tag on a tag build, otherwise the short commit SHA. On the default branch the
|
|
image also gets a moving `latest`. See `references/pipeline-workflow.md` §Tagging.
|
|
|
|
## 3. Publish gating — build always, push selectively
|
|
|
|
| Trigger | Images | Charts |
|
|
|---|---|---|
|
|
| pull request / feature branch | build only (verify it compiles) | `helm lint` + `helm package` (verify) |
|
|
| push to default branch (`test`) or `main` | build **and push** | package **and push** |
|
|
| tag `v*` | build **and push** (tagged with the git tag) | package **and push** |
|
|
|
|
A PR must never publish — it proves the artifact builds, nothing more. The template computes a
|
|
`PUBLISH` flag from the event; do not remove it.
|
|
|
|
## 4. Installing the pipeline into a monorepo
|
|
|
|
1. Copy `assets/workflows/ci-cd.yaml` → `<repo>/.gitea/workflows/ci-cd.yaml`.
|
|
2. Set `runs-on:` to your runner's label (see Gotchas — it must be a label whose jobs provide
|
|
`docker`, `helm`, and `git`). Set `HL_V1_HARBOR_PROJECT` (default `gondor-v1`) to the Harbor
|
|
project the robot can **push** to.
|
|
3. Ensure the three Gitea values from §1 exist (2 global variables once; 1 org secret per org).
|
|
4. Commit, push a throwaway branch, and confirm the run **builds without pushing**.
|
|
5. Merge to the default branch and confirm images + charts appear in Harbor.
|
|
|
|
## Handoff
|
|
|
|
This skill stops at *published to Harbor*. To roll the new image/chart into the cluster, hand
|
|
the image reference and tag to **development-gitops-argo-cd**: the tag flows through the Copier
|
|
variables into the Helm values Argo CD syncs. Never wire a raw `kubectl apply` into this
|
|
pipeline — deployment is GitOps's responsibility, and a pipeline that also deploys bypasses the
|
|
one source of truth for cluster state.
|
|
|
|
## Gotchas
|
|
|
|
- **Build context is the repo root, always.** `docker build -f applications-backend/<svc>/Dockerfile … .`
|
|
The Dockerfile's `COPY . .` + `turbo prune` need the whole monorepo. Building from the app
|
|
directory fails with missing lockfile / workspace errors.
|
|
- **Charts are centralized, not co-located.** They live in `helm-charts/<chart>/`. An app can
|
|
ship an image with no chart, and a chart can exist with no matching app — discover the two
|
|
independently, do not assume a 1:1 pairing.
|
|
- **Gitea has no global secrets — only global variables.** The username/address are variables
|
|
(global, set once); the robot secret is an org secret (per org). Do not try to create a
|
|
"global secret"; the admin Actions page only offers global *variables*.
|
|
- **The robot secret is only shown once, at creation.** Harbor never reveals it again on GET.
|
|
It's cached under palantir `.harbor/<project>-<robot>.yml` (git-ignored). To rotate, re-run
|
|
the palantir robot task, then update the Gitea org secret. See `references/harbor-auth.md`.
|
|
- **`runs-on` maps to a container image — it must have `docker`, `helm`, `git`.** `turbo prune`
|
|
runs *inside* the image build, so the job itself needs Node **only if** you add pre-build
|
|
steps; the default template needs just docker + helm + git. A bare `node:alpine` label has no
|
|
docker CLI and the build step will fail with `docker: not found`.
|
|
- **Marketplace actions may not resolve.** Gitea proxies `actions/*` to github.com by default;
|
|
if the runner has no egress, `actions/checkout` fails. The template keeps action use to
|
|
`actions/checkout@v4` only and documents a `git clone` fallback in
|
|
`references/pipeline-workflow.md`.
|
|
- **Frontend build args are public.** A frontend image bakes its build-time env into the
|
|
bundle. Never pass a secret as a `--build-arg`; anything per-environment on the frontend is
|
|
fetched at runtime, not built in.
|
|
- **`helm push` needs an OCI-enabled Helm ≥ 3.8 and a prior `helm registry login`.** Without
|
|
the login step, `helm push` fails with `unauthorized` even though `docker login` succeeded —
|
|
they are separate credential stores.
|
|
- **Never `docker login` with `-p` on the command line.** Use `--password-stdin`; a `-p`
|
|
argument lands in the process list and the runner logs.
|