Files
devsecops-ci-cd-gitea-3darch/assets/workflows/ci-cd.yaml
T
Oleg LukasonokandClaude Opus 4.8 8f46c3faa5 feat: devsecops-ci-cd-gitea skill — Gitea Actions → Harbor pipelines
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>
2026-08-13 00:10:03 +03:00

168 lines
6.2 KiB
YAML

# Drop-in Gitea Actions pipeline for a pnpm + Turborepo monorepo.
# Copy to <repo>/.gitea/workflows/ci-cd.yaml.
#
# It discovers every app with a Dockerfile and every chart under helm-charts/, builds them,
# and — only on the default branch or a tag — pushes images and OCI Helm charts to Harbor.
#
# Prerequisites (see the devsecops-ci-cd-gitea skill):
# Gitea GLOBAL variables (Site Admin -> Actions -> Variables), set once for all orgs:
# HL_V1_HARBOR_ADDRESS e.g. harbor-v1.apps.lego-cloud.eu
# HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_USERNAME e.g. robot$gondor-v1+gitea-actions-v1
# HL_V1_HARBOR_PROJECT (optional; defaults to gondor-v1 below)
# Gitea ORG secret (per org, Settings -> Actions -> Secrets):
# HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_SECRET
#
# TODO before first run:
# - set `runs-on` to your runner's label (its jobs must provide docker, helm, git)
name: ci-cd
on:
push:
branches: [test, main]
tags: ["v*"]
pull_request:
branches: [test, main]
env:
HARBOR_ADDRESS: ${{ vars.HL_V1_HARBOR_ADDRESS }}
HARBOR_USERNAME: ${{ vars.HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_USERNAME }}
HARBOR_SECRET: ${{ secrets.HL_V1_HARBOR_ROBOT_GITEA_ACTIONS_V1_SECRET }}
HARBOR_PROJECT: ${{ vars.HL_V1_HARBOR_PROJECT || 'gondor-v1' }}
jobs:
build-and-push-images:
runs-on: [self-hosted] # TODO: set to your runner label (needs docker + git)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Compute tag and publish flag
id: meta
run: |
set -euo pipefail
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
TAG="${GITHUB_REF_NAME}"
else
TAG="$(git rev-parse --short HEAD)"
fi
if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
PUBLISH=false
elif [ "${GITHUB_REF_TYPE}" = "tag" ] \
|| [ "${GITHUB_REF_NAME}" = "test" ] \
|| [ "${GITHUB_REF_NAME}" = "main" ]; then
PUBLISH=true
else
PUBLISH=false
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "publish=${PUBLISH}" >> "$GITHUB_OUTPUT"
echo "Tag=${TAG} Publish=${PUBLISH}"
- name: Log in to Harbor (publish runs only)
if: steps.meta.outputs.publish == 'true'
run: |
set -euo pipefail
echo "${HARBOR_SECRET}" | docker login "${HARBOR_ADDRESS}" -u "${HARBOR_USERNAME}" --password-stdin
- name: Build and push images
env:
TAG: ${{ steps.meta.outputs.tag }}
PUBLISH: ${{ steps.meta.outputs.publish }}
run: |
set -euo pipefail
shopt -s nullglob
found=0
for df in applications-backend/*/Dockerfile applications-frontend/*/Dockerfile; do
found=1
app="$(basename "$(dirname "$df")")"
image="${HARBOR_ADDRESS}/${HARBOR_PROJECT}/${app}"
echo "::group::${app}"
# Build context is the REPOSITORY ROOT — the Dockerfile runs turbo prune / COPY . .
docker build -f "$df" -t "${image}:${TAG}" .
if [ "${PUBLISH}" = "true" ]; then
docker push "${image}:${TAG}"
# moving :latest on branch builds only (never on tags)
if [ "${GITHUB_REF_TYPE}" != "tag" ]; then
docker tag "${image}:${TAG}" "${image}:latest"
docker push "${image}:latest"
fi
else
echo "PUBLISH=false — built ${image}:${TAG}, not pushing"
fi
echo "::endgroup::"
done
[ "${found}" = "1" ] || echo "No Dockerfiles found under applications-backend/* or applications-frontend/*"
- name: Log out
if: always() && steps.meta.outputs.publish == 'true'
run: docker logout "${HARBOR_ADDRESS}" || true
package-and-push-charts:
runs-on: [self-hosted] # TODO: set to your runner label (needs helm + git)
needs: build-and-push-images
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Compute publish flag
id: meta
run: |
set -euo pipefail
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
TAG="${GITHUB_REF_NAME#v}"
else
TAG=""
fi
if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then
PUBLISH=false
elif [ "${GITHUB_REF_TYPE}" = "tag" ] \
|| [ "${GITHUB_REF_NAME}" = "test" ] \
|| [ "${GITHUB_REF_NAME}" = "main" ]; then
PUBLISH=true
else
PUBLISH=false
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "publish=${PUBLISH}" >> "$GITHUB_OUTPUT"
- name: Log in to Harbor (publish runs only)
if: steps.meta.outputs.publish == 'true'
run: |
set -euo pipefail
echo "${HARBOR_SECRET}" | helm registry login "${HARBOR_ADDRESS}" -u "${HARBOR_USERNAME}" --password-stdin
- name: Lint, package and push charts
env:
TAG: ${{ steps.meta.outputs.tag }}
PUBLISH: ${{ steps.meta.outputs.publish }}
run: |
set -euo pipefail
shopt -s nullglob
mkdir -p .cicd-charts
found=0
for chart in helm-charts/*/Chart.yaml; do
found=1
dir="$(dirname "$chart")"
echo "::group::$(basename "$dir")"
helm lint "$dir"
if [ -n "${TAG}" ]; then
# On a tag build, align chart + app version with the release tag.
helm package "$dir" -d .cicd-charts --version "${TAG}" --app-version "${TAG}"
else
helm package "$dir" -d .cicd-charts
fi
echo "::endgroup::"
done
[ "${found}" = "1" ] || { echo "No charts under helm-charts/*"; exit 0; }
if [ "${PUBLISH}" = "true" ]; then
for tgz in .cicd-charts/*.tgz; do
helm push "$tgz" "oci://${HARBOR_ADDRESS}/${HARBOR_PROJECT}/charts"
done
else
echo "PUBLISH=false — packaged charts, not pushing"
fi
- name: Log out
if: always() && steps.meta.outputs.publish == 'true'
run: helm registry logout "${HARBOR_ADDRESS}" || true