From 9983b873f9ff2fc4b61285c047a6dd491b4355a1 Mon Sep 17 00:00:00 2001 From: funman300 Date: Wed, 13 May 2026 13:53:09 -0700 Subject: [PATCH] feat(ops): add k3s + ArgoCD GitOps pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile: copy web/ and assets/ to runtime stage so ServeDir routes work - .gitea/workflows/docker-build.yml: build/push image on master push, pin SHA tag back into deploy/kustomization.yaml so ArgoCD sees a real manifest change - deploy/: Kustomize manifests — Namespace, PVC, Deployment (Recreate for SQLite), Service, Traefik Ingress at klondike.aleshym.co - argocd/application.yaml: auto-sync Application watching deploy/ on Gitea Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/docker-build.yml | 65 +++++++++++++++++++++++++++++++ argocd/application.yaml | 20 ++++++++++ deploy/deployment.yaml | 62 +++++++++++++++++++++++++++++ deploy/ingress.yaml | 27 +++++++++++++ deploy/kustomization.yaml | 16 ++++++++ deploy/namespace.yaml | 4 ++ deploy/pvc.yaml | 11 ++++++ deploy/service.yaml | 12 ++++++ solitaire_server/Dockerfile | 8 +++- 9 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/docker-build.yml create mode 100644 argocd/application.yaml create mode 100644 deploy/deployment.yaml create mode 100644 deploy/ingress.yaml create mode 100644 deploy/kustomization.yaml create mode 100644 deploy/namespace.yaml create mode 100644 deploy/pvc.yaml create mode 100644 deploy/service.yaml diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml new file mode 100644 index 0000000..156accf --- /dev/null +++ b/.gitea/workflows/docker-build.yml @@ -0,0 +1,65 @@ +name: Build and Deploy + +on: + push: + branches: [master] + # Only run when server code changes, not when CI itself updates deploy/. + paths-ignore: + - 'deploy/**' + - 'argocd/**' + - '**.md' + +env: + REGISTRY: git.aleshym.co + IMAGE: git.aleshym.co/funman300/solitaire-server + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # Need full history so we can push the tag-update commit back. + fetch-depth: 0 + token: ${{ secrets.CI_TOKEN }} + + - name: Set image tag + id: meta + run: echo "sha=${GITHUB_SHA::8}" >> "$GITHUB_OUTPUT" + + - name: Log in to Gitea registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.CI_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: solitaire_server/Dockerfile + push: true + tags: | + ${{ env.IMAGE }}:${{ steps.meta.outputs.sha }} + ${{ env.IMAGE }}:latest + + - name: Install kustomize + run: | + curl -sL "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + sudo mv kustomize /usr/local/bin/kustomize + + - name: Pin image tag in deploy manifests + run: | + cd deploy + kustomize edit set image solitaire-server=${{ env.IMAGE }}:${{ steps.meta.outputs.sha }} + + - name: Commit and push updated kustomization + run: | + git config user.email "ci@gitea.local" + git config user.name "Gitea CI" + git add deploy/kustomization.yaml + git commit -m "chore(deploy): bump image to ${{ steps.meta.outputs.sha }} [skip ci]" || true + git push diff --git a/argocd/application.yaml b/argocd/application.yaml new file mode 100644 index 0000000..1be6aea --- /dev/null +++ b/argocd/application.yaml @@ -0,0 +1,20 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: solitaire-server + namespace: argocd +spec: + project: default + source: + repoURL: http://10.10.0.64:3000/funman300/Rusty_Solitare.git + targetRevision: master + path: deploy + destination: + server: https://kubernetes.default.svc + namespace: solitaire + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/deploy/deployment.yaml b/deploy/deployment.yaml new file mode 100644 index 0000000..24124dc --- /dev/null +++ b/deploy/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: solitaire-server + namespace: solitaire +spec: + replicas: 1 + selector: + matchLabels: + app: solitaire-server + # SQLite is single-writer; Recreate avoids two pods owning the PVC at once. + strategy: + type: Recreate + template: + metadata: + labels: + app: solitaire-server + spec: + imagePullSecrets: + - name: gitea-registry + containers: + - name: server + image: solitaire-server + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: DATABASE_URL + value: sqlite:///data/sol.db + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: solitaire-secrets + key: jwt-secret + - name: SERVER_PORT + value: "8080" + volumeMounts: + - name: db-data + mountPath: /data + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 10 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 256Mi + volumes: + - name: db-data + persistentVolumeClaim: + claimName: solitaire-db diff --git a/deploy/ingress.yaml b/deploy/ingress.yaml new file mode 100644 index 0000000..f1de49b --- /dev/null +++ b/deploy/ingress.yaml @@ -0,0 +1,27 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: solitaire-server + namespace: solitaire + annotations: + # Remove the next two lines if you are not using cert-manager. + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + ingressClassName: traefik + rules: + - host: klondike.aleshym.co + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: solitaire-server + port: + name: http + # Remove the tls block if you are not using cert-manager. + tls: + - hosts: + - klondike.aleshym.co + secretName: solitaire-tls diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml new file mode 100644 index 0000000..6aa2358 --- /dev/null +++ b/deploy/kustomization.yaml @@ -0,0 +1,16 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - namespace.yaml + - pvc.yaml + - deployment.yaml + - service.yaml + - ingress.yaml + +# CI updates this block automatically via `kustomize edit set image`. +# The image name here matches the `image: solitaire-server` stub in deployment.yaml. +images: + - name: solitaire-server + newName: git.aleshym.co/funman300/solitaire-server + newTag: latest diff --git a/deploy/namespace.yaml b/deploy/namespace.yaml new file mode 100644 index 0000000..6d4ccee --- /dev/null +++ b/deploy/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: solitaire diff --git a/deploy/pvc.yaml b/deploy/pvc.yaml new file mode 100644 index 0000000..ac4a941 --- /dev/null +++ b/deploy/pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: solitaire-db + namespace: solitaire +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/deploy/service.yaml b/deploy/service.yaml new file mode 100644 index 0000000..e4e9631 --- /dev/null +++ b/deploy/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: solitaire-server + namespace: solitaire +spec: + selector: + app: solitaire-server + ports: + - name: http + port: 80 + targetPort: 8080 diff --git a/solitaire_server/Dockerfile b/solitaire_server/Dockerfile index 33d3078..0375a35 100644 --- a/solitaire_server/Dockerfile +++ b/solitaire_server/Dockerfile @@ -48,8 +48,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /app COPY --from=builder /build/target/release/solitaire_server ./solitaire_server -# Migrations are embedded via sqlx::migrate!("./migrations") relative to the -# crate root at compile time — they do not need to be copied here. +# Migrations are embedded via sqlx::migrate!("./migrations") at compile time. +# Static web assets are served via ServeDir at runtime from these paths: +# /app/solitaire_server/web → /web route +# /app/assets → /assets route +COPY solitaire_server/web ./solitaire_server/web +COPY assets ./assets ENV SERVER_PORT=8080 EXPOSE 8080