Files
gitea-mcp/.gitea/workflows/cd.yml
T
mathiasandClaude Opus 5 417ab60707
CD / Lint / Test / Vet (push) Successful in 9s
CD / Build & Import (push) Successful in 6s
CD / Deploy via GitOps (push) Failing after 1m2s
ci: call the shared build-image script instead of an inline copy (infra#309)
Wave 2. The buildctl/skopeo step here was one of nineteen near-identical
copies; the implementation now lives in .gitea/build-image.sh,
byte-identical to mathias/infra's ci-templates/build-image.sh and held
there by a drift gate. Labels, tags and build-args are inputs.

Behaviour unchanged: same registry, same :sha/:latest/version tags, same
revision and source labels.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011PL5XcG68SvQqz3vGVbWbf
2026-08-25 12:35:52 +02:00

186 lines
7.3 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CD
"on":
push:
branches: [main]
tags: ["v*"]
# Docs-only pushes don't change the image — skip the build/deploy roll.
# (Only applies to branch pushes; tag pushes always trigger.)
paths-ignore:
- 'docs/**'
env:
IMAGE: gitea-mcp
jobs:
# ── 1. Quality gate ─────────────────────────────────────────────────────────
check:
name: Lint / Test / Vet
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false # self-hosted runner: Go cache persists on disk between runs
- name: Verify toolchain
run: |
go version
task --version
govulncheck -version 2>&1 || true
- name: Install golangci-lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh \
| sh -s -- -b "$(go env GOPATH)/bin" v2.11.4
golangci-lint --version
- name: Run checks
run: task check
# ── 2. Build image ──────────────────────────────────────────────────────────
build:
name: Build & Import
needs: check
runs-on: self-hosted
outputs:
image-tag: ${{ steps.meta.outputs.sha-tag }}
steps:
- uses: actions/checkout@v4
- name: Derive image tags
id: meta
run: |
SHA=$(git rev-parse --short HEAD)
echo "sha-tag=${SHA}" >> "$GITHUB_OUTPUT"
REF="${{ github.ref }}"
if [[ "$REF" == refs/tags/v* ]]; then
echo "version-tag=${REF#refs/tags/}" >> "$GITHUB_OUTPUT"
echo "version=${REF#refs/tags/}" >> "$GITHUB_OUTPUT"
else
# The ldflags version stamp: git tag on a v* build, else short sha.
# Computed here in shell, not as a `${{ a || b }}` expression, so it
# cannot depend on how the runner treats an empty step output -- a
# wrong VERSION would be baked into the binary silently.
echo "version=${SHA}" >> "$GITHUB_OUTPUT"
fi
- name: Build and push to local registry
# infra#309: implementation lives in .gitea/build-image.sh, a
# byte-identical copy of mathias/infra's ci-templates/build-image.sh.
# Do not edit it here -- edit the canonical file, run `task ci:sync`,
# and infra's drift gate names any repo still carrying an old copy.
env:
SHA_TAG: ${{ steps.meta.outputs.sha-tag }}
VERSION_TAG: ${{ steps.meta.outputs.version-tag }}
GIT_SHA: ${{ github.sha }}
IMAGE_SOURCE: ${{ github.repositoryUrl }}
BUILD_ARGS: VERSION=${{ steps.meta.outputs.version }}
run: bash .gitea/build-image.sh
# No smoke test here. The old one ran `sudo k3s ctr run` against the host's
# containerd: written for the Arch host runner, impossible in the
# containerized runner (infra#233) which has no sudo, no ctr and no
# containerd socket -- it failed with `sudo: command not found` the moment
# infra#241's buildctl migration let the build reach it. The deploy job's
# `Verify rollout` + `Confirm pod running new image` cover the same ground
# more strongly, which is why tapir dropped its smoke test too (infra#241).
# ── 3. Deploy via infra repo + Flux ─────────────────────────────────────────
deploy:
name: Deploy via GitOps
needs: build
runs-on: self-hosted
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: staging
steps:
- name: Update image tag in infra repo
env:
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
DEPLOY_KEY: ${{ secrets.INFRA_DEPLOY_KEY }}
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_infra
chmod 600 ~/.ssh/id_infra
ssh-keyscan -p 30022 10.0.1.20 >> ~/.ssh/known_hosts 2>/dev/null
export GIT_SSH_COMMAND="ssh -i ~/.ssh/id_infra -o IdentitiesOnly=yes"
rm -rf /tmp/infra
git clone -b main ssh://git@10.0.1.20:30022/mathias/infra.git /tmp/infra
cd /tmp/infra
DEPLOYMENT="k3s/apps/gitea-mcp/deployment.yaml"
sed -i "s|image: localhost:5000/gitea-mcp:.*|image: localhost:5000/gitea-mcp:${IMAGE_TAG}|" "$DEPLOYMENT"
grep -q "localhost:5000/gitea-mcp:${IMAGE_TAG}" "$DEPLOYMENT" \
|| { echo "✗ image tag patch failed"; exit 1; }
if git diff --quiet "$DEPLOYMENT"; then
echo "ℹ image tag unchanged — skipping push"
else
git -c user.name="gitea-mcp CI" \
-c user.email="ci@gitea-mcp.local" \
commit -m "chore(deploy): gitea-mcp → ${IMAGE_TAG}" "$DEPLOYMENT"
git push origin main
echo "✓ pushed to infra repo"
fi
shred -u ~/.ssh/id_infra
- name: Trigger Flux reconcile (immediate)
run: |
kubectl -n flux-system annotate gitrepository flux-system \
reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
kubectl -n flux-system annotate kustomization apps \
reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
- name: Wait for Flux to apply new image
env:
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
run: |
EXPECTED="localhost:5000/gitea-mcp:${IMAGE_TAG}"
for i in $(seq 1 60); do
CURRENT=$(kubectl get deploy gitea-mcp -n gitea-mcp \
-o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null || echo "")
if [ "$CURRENT" = "$EXPECTED" ]; then
echo "✓ Flux applied new image after ${i}s"
break
fi
sleep 1
done
kubectl get deploy gitea-mcp -n gitea-mcp \
-o jsonpath='{.spec.template.spec.containers[0].image}' \
| grep -qx "$EXPECTED" \
|| { echo "✗ Flux did not apply new image within 60s"; exit 1; }
- name: Verify rollout
run: |
kubectl rollout status deployment/gitea-mcp \
--namespace gitea-mcp \
--timeout=120s \
|| {
echo "── pod status ──"
kubectl get pods -n gitea-mcp -o wide
echo "── events ──"
kubectl get events -n gitea-mcp --sort-by='.lastTimestamp' | tail -20
echo "── describe ──"
kubectl describe pods -n gitea-mcp -l app=gitea-mcp | tail -40
exit 1
}
- name: Confirm pod running new image
env:
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
run: |
kubectl get pods -n gitea-mcp \
-l app=gitea-mcp \
--field-selector=status.phase=Running \
-o jsonpath='{.items[*].spec.containers[0].image}' \
| grep -q "localhost:5000/gitea-mcp:${IMAGE_TAG}" \
&& echo "✓ pod running new image" \
|| { echo "✗ pod image mismatch"; exit 1; }