Generate and maintain Gitea Actions CI/CD pipelines that build & publish Docker images (applications-backend/*, applications-frontend/*) and OCI Helm charts (helm-charts/*) from a pnpm + Turborepo monorepo to a Harbor registry, authenticated by a project robot account via Gitea org secret + global vars. - SKILL.md: auth model, convention-driven discovery, publish gating, gotchas - references/harbor-auth.md: docker/helm login, robot accounts, secret/var taxonomy, bake-into-runner alternative, troubleshooting - references/pipeline-workflow.md: jobs, discovery loops, tagging, turbo prune - references/conventions.md: Dockerfile/chart locations, image & chart naming - assets/workflows/ci-cd.yaml: ready-to-drop .gitea/workflows pipeline Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.2 KiB
Discovery conventions
Where the pipeline looks for work, and how names are derived. These conventions come from the
development-monorepo-pnpm skill (references/containers-and-helm.md) — this file restates
only what the pipeline depends on. If the monorepo skill and this file ever disagree, the
monorepo skill wins; update this one.
Repository shape the pipeline assumes
<repo>/
├── applications-backend/<svc>/Dockerfile # deployable services (NestJS by default)
├── applications-frontend/<app>/Dockerfile # deployable browser apps (React + Carbon)
├── helm-charts/<chart>/Chart.yaml # ALL charts — centralized, not per-app
├── pnpm-workspace.yaml
├── turbo.json
└── pnpm-lock.yaml
Two independent discovery sets:
| Artifact | Glob | Produced by |
|---|---|---|
| Image | applications-backend/*/Dockerfile, applications-frontend/*/Dockerfile |
docker build from repo root |
| Chart | helm-charts/*/Chart.yaml |
helm package |
Charts are centralized — do not look under the app directory
A frequent wrong assumption is that a chart lives at applications-frontend/<app>/helm/ or
.../chart/. It does not in this monorepo family. Charts are collected under
helm-charts/<chart>/ at the repo root. Practical consequences:
- Discover images and charts separately; there is no enforced 1:1 mapping.
- An app may have a
Dockerfileand no chart (deployed by a shared/umbrella chart, or not yet deployable). - A chart may exist with no same-named app (an umbrella chart, or a chart that deploys a third-party image).
- An app may have a
- Do not infer a chart's existence from an app's, or vice versa. Loop each glob on its own.
Naming
Image repository = application directory name
${HARBOR_ADDRESS}/${HARBOR_PROJECT}/<app-dir-name>:<tag>
# e.g. harbor-v1.apps.lego-cloud.eu/gondor-v1/user-service:9f3a1c2
- The directory name under
applications-backend/orapplications-frontend/is the image repository name. It is not derived from the package.jsonname/ scope — using the directory name keeps the image path stable and needs no Node in the job. - Backend and frontend images share the flat
<project>/<app>namespace. If two apps across the two roots ever share a directory name they would collide — keep directory names unique across both roots (they already must be, since pnpm workspace names must be unique).
Chart repository = chart name, under a charts subpath
oci://${HARBOR_ADDRESS}/${HARBOR_PROJECT}/charts # push target (base)
# a chart named "user-service" becomes:
# ${HARBOR_ADDRESS}/${HARBOR_PROJECT}/charts/user-service:<chart-version>
Charts are pushed under a charts subpath so OCI chart artifacts and image artifacts do not
share the project's top-level namespace. The chart name is the name: field in Chart.yaml
(which should match its directory name).
Harbor project
HARBOR_PROJECT (default gondor-v1) is the Harbor project everything is pushed into. It must
be a project the robot has push on. One project per monorepo is a clean default; if you host
several monorepos, either give each its own project (and a per-project push robot) or accept a
shared project with distinct <app> names.
Tag → deploy handoff
The pipeline publishes :<sha> (immutable) and, on the default branch, :latest (moving). The
immutable :<sha> is what a GitOps deployment should pin. This skill does not edit
deployment values — it hands the image reference and tag to development-gitops-argo-cd, where
the tag flows through Copier variables into the Helm values Argo CD syncs.
Quick audit of a repo before generating a pipeline
echo "images:"; ls -d applications-backend/*/ applications-frontend/*/ 2>/dev/null \
| while read d; do [ -f "$d/Dockerfile" ] && echo " $d"; done
echo "charts:"; ls -d helm-charts/*/ 2>/dev/null \
| while read d; do [ -f "$d/Chart.yaml" ] && echo " $d"; done
If either list is empty, confirm you are at the repo root and that the monorepo actually follows this layout before wiring the pipeline — an empty discovery set means the workflow will run green while building nothing.