Compare commits
3 Commits
1f9934349b
...
d261a9f3fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d261a9f3fe | ||
|
|
09b3f25211 | ||
|
|
a8d04242d9 |
186
.gitea/workflows/cd.yml
Normal file
186
.gitea/workflows/cd.yml
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
name: CD
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ["v*"]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
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
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push to local registry
|
||||||
|
run: |
|
||||||
|
REGISTRY="localhost:5000"
|
||||||
|
REF="${REGISTRY}/${{ env.IMAGE }}:${{ steps.meta.outputs.sha-tag }}"
|
||||||
|
buildah build \
|
||||||
|
--label "org.opencontainers.image.revision=${{ github.sha }}" \
|
||||||
|
--label "org.opencontainers.image.source=${{ github.repositoryUrl }}" \
|
||||||
|
-t ${REF} \
|
||||||
|
-t ${REGISTRY}/${{ env.IMAGE }}:latest \
|
||||||
|
.
|
||||||
|
buildah push --tls-verify=false ${REF}
|
||||||
|
buildah push --tls-verify=false ${REGISTRY}/${{ env.IMAGE }}:latest
|
||||||
|
[[ -n "${{ steps.meta.outputs.version-tag }}" ]] && \
|
||||||
|
buildah push --tls-verify=false \
|
||||||
|
${REF} \
|
||||||
|
${REGISTRY}/${{ env.IMAGE }}:${{ steps.meta.outputs.version-tag }} || true
|
||||||
|
echo "✓ Image pushed to ${REF}"
|
||||||
|
|
||||||
|
- name: Smoke test
|
||||||
|
run: |
|
||||||
|
REGISTRY="localhost:5000"
|
||||||
|
REF="${REGISTRY}/${{ env.IMAGE }}:${{ steps.meta.outputs.sha-tag }}"
|
||||||
|
CNAME="smoke-${{ steps.meta.outputs.sha-tag }}"
|
||||||
|
sudo k3s ctr images pull --plain-http ${REF}
|
||||||
|
OUTPUT=$(timeout 5 sudo k3s ctr run --rm ${REF} ${CNAME} /gitea-mcp 2>&1 || true)
|
||||||
|
sudo k3s ctr containers delete ${CNAME} 2>/dev/null || true
|
||||||
|
echo "$OUTPUT" | grep -q "gitea-mcp" \
|
||||||
|
&& echo "✓ Smoke test passed" \
|
||||||
|
|| echo "⚠ Smoke test inconclusive: $OUTPUT"
|
||||||
|
|
||||||
|
# ── 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; }
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM golang:1.26-alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/gitea-mcp ./cmd/gitea-mcp
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/static-debian12:nonroot
|
||||||
|
COPY --from=build /out/gitea-mcp /gitea-mcp
|
||||||
|
USER nonroot:nonroot
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["/gitea-mcp"]
|
||||||
@@ -14,3 +14,9 @@ tasks:
|
|||||||
lint:
|
lint:
|
||||||
desc: Run golangci-lint
|
desc: Run golangci-lint
|
||||||
cmds: [golangci-lint run ./...]
|
cmds: [golangci-lint run ./...]
|
||||||
|
check:
|
||||||
|
desc: Lint, vet, and test (used by CI)
|
||||||
|
cmds:
|
||||||
|
- golangci-lint run ./...
|
||||||
|
- go vet ./...
|
||||||
|
- go test ./... -race -count=1
|
||||||
|
|||||||
Reference in New Issue
Block a user