From 08cee0de0dc5a5ba1d8b644e37197f6ab7ecb6ee Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sun, 21 Jun 2026 15:46:34 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20plan=20=E2=80=94=20Gitea=20replaces=20G?= =?UTF-8?q?itLab=20(Phase=202B);=20SSH=20on=20both=20:22=20and=20:2222?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- .../plans/2026-06-21-gitea-replace-gitlab.md | 475 ++++++++++++++++++ .../2026-06-21-gitea-replace-gitlab-design.md | 9 +- 2 files changed, 481 insertions(+), 3 deletions(-) create mode 100644 docs/superpowers/plans/2026-06-21-gitea-replace-gitlab.md diff --git a/docs/superpowers/plans/2026-06-21-gitea-replace-gitlab.md b/docs/superpowers/plans/2026-06-21-gitea-replace-gitlab.md new file mode 100644 index 000000000..02f5ecf29 --- /dev/null +++ b/docs/superpowers/plans/2026-06-21-gitea-replace-gitlab.md @@ -0,0 +1,475 @@ +# Gitea Replaces GitLab + Cockpit Registry to Scaleway — Implementation Plan (Phase 2B) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (INLINE, with checkpoints). +> **Do NOT run subagent-driven** — this touches live git access, the container registry, and the cockpit +> deploy; each cutover step needs a human-confirmed gate. Steps use checkbox (`- [ ]`) syntax. + +**Goal:** Stand up Gitea (chart + existing Postgres) as the git host behind the unchanged `git.fxhnt.ai`, +migrate both repos, and move the cockpit image to Scaleway Container Registry — so GitLab can be removed in 2C. + +**Architecture:** Gitea comes up internal-only; repos migrate + validate over a port-forward; the cockpit +image moves to Scaleway registry and is build+rollout tested; THEN `git.fxhnt.ai` (HTTPS + SSH `:2222`) +cuts over to Gitea in one switch; finally a Gitea webhook → Argo Events auto-deploys the cockpit. GitLab +stays running (unaddressed) as the rollback net until 2C. + +**Tech Stack:** Gitea Helm chart (`gitea-charts/gitea`), existing in-cluster PostgreSQL, nginx+socat +Tailscale proxy, Scaleway Container Registry, Argo Events, kaniko. + +**Spec:** `docs/superpowers/specs/2026-06-21-gitea-replace-gitlab-design.md` + +**TWO REPOS:** infra changes → `foxhunt` (`~/Work/foxhunt`); cockpit build/deploy → `fxhnt` +(`~/Work/fxhnt`). Each gets its own branch + commits. + +--- + +## Shared env +```bash +export SCW_ACCESS_KEY=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.access-key}'|base64 -d) +export SCW_SECRET_KEY=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.secret-key}'|base64 -d) +export SCW_DEFAULT_PROJECT_ID=$(kubectl get secret scaleway-credentials -n foxhunt -o jsonpath='{.data.project-id}'|base64 -d) +export SCW_DEFAULT_REGION=fr-par SCW_DEFAULT_ZONE=fr-par-2 +``` + +--- + +## Task 0: Branches +- [ ] **Step 1** +```bash +cd /home/jgrusewski/Work/foxhunt && git checkout -b chore/gitea-replace-gitlab +cd /home/jgrusewski/Work/fxhnt && git checkout -b chore/gitea-cockpit-registry +``` + +--- + +## Task 1: Postgres `gitea` DB + secrets + +**Files:** none committed (cluster secrets). Record secret names only. + +- [ ] **Step 1: Read existing postgres superuser creds** +```bash +PGU=$(kubectl get deploy postgres -n foxhunt -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="POSTGRES_USER")].value}') +PGPOD=$(kubectl get pod -n foxhunt -l app=postgres -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || kubectl get pod -n foxhunt | grep '^postgres-' | grep -v backup | awk '{print $1}' | head -1) +echo "postgres user=$PGU pod=$PGPOD" +``` +Expected: a username + a running pod name. (If `POSTGRES_USER` is via secretRef not value, read it from the referenced secret instead.) + +- [ ] **Step 2: Generate a Gitea DB password + create role/db** +```bash +GITEA_DB_PW=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24) +kubectl exec -n foxhunt "$PGPOD" -- psql -U "$PGU" -v ON_ERROR_STOP=1 -c \ + "CREATE ROLE gitea LOGIN PASSWORD '$GITEA_DB_PW';" -c \ + "CREATE DATABASE gitea OWNER gitea ENCODING 'UTF8';" 2>&1 | grep -iE "CREATE|already exists|Error" +echo "$GITEA_DB_PW" # capture for the next step +``` +Expected: `CREATE ROLE` + `CREATE DATABASE` (or `already exists` if re-run). + +- [ ] **Step 3: Create the k8s secrets Gitea needs** (DB creds + admin) +```bash +GITEA_ADMIN_PW=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24) +kubectl create secret generic gitea-db -n foxhunt \ + --from-literal=password="$GITEA_DB_PW" --dry-run=client -o yaml | kubectl apply -f - +kubectl create secret generic gitea-admin -n foxhunt \ + --from-literal=username=gitadmin --from-literal=password="$GITEA_ADMIN_PW" \ + --from-literal=email=jeroen@bizworx.nl --dry-run=client -o yaml | kubectl apply -f - +echo "ADMIN PW (save in your password manager): $GITEA_ADMIN_PW" +``` +Expected: both secrets `created`/`configured`. **Save the admin password** — needed for Gitea login. + +--- + +## Task 2: Gitea Helm values + install (internal-only) + +**Files:** Create `infra/k8s/gitea/values.yaml` (foxhunt repo). + +- [ ] **Step 1: Add the Gitea Helm repo** +```bash +helm repo add gitea-charts https://dl.gitea.com/charts/ && helm repo update gitea-charts +helm search repo gitea-charts/gitea --versions | head -3 +``` +Expected: lists chart versions (use the latest stable in Step 3). + +- [ ] **Step 2: Write `infra/k8s/gitea/values.yaml`** +```yaml +# Gitea — lightweight git host replacing GitLab (Phase 2B). External Postgres (existing in-cluster +# `postgres`), no bundled DB/redis/memcached, Actions off (Argo does CI). Internal-only until cutover. +replicaCount: 1 +image: + rootless: true + +# Disable all bundled subcharts — reuse the existing in-cluster postgres +postgresql: + enabled: false +postgresql-ha: + enabled: false +redis-cluster: + enabled: false +redis: + enabled: false + +persistence: + enabled: true + size: 5Gi + storageClass: sbs-default-retain + +resources: + requests: { cpu: 100m, memory: 128Mi } + limits: { cpu: "1", memory: 512Mi } + +service: + http: { type: ClusterIP, port: 3000 } + ssh: { type: ClusterIP, port: 22 } + +actions: + enabled: false + +gitea: + admin: + existingSecret: gitea-admin + config: + server: + ROOT_URL: https://git.fxhnt.ai/ + DOMAIN: git.fxhnt.ai + SSH_DOMAIN: git.fxhnt.ai + SSH_PORT: "22" # advertise clean git@git.fxhnt.ai in NEW clone URLs; legacy :2222 still + # works via the proxy (Task 5). Port 22 is free — nothing host-SSHes the git node. + DISABLE_SSH: "false" + database: + DB_TYPE: postgres + HOST: postgres.foxhunt.svc.cluster.local:5432 + NAME: gitea + USER: gitea + service: + DISABLE_REGISTRATION: "true" + cache: + ADAPTER: memory + additionalConfigFromEnvs: + - name: GITEA__database__PASSWD + valueFrom: + secretKeyRef: { name: gitea-db, key: password } +``` + +- [ ] **Step 3: Install (internal-only — no public hostname yet)** +```bash +cd /home/jgrusewski/Work/foxhunt +helm install gitea gitea-charts/gitea -n foxhunt -f infra/k8s/gitea/values.yaml --wait --timeout 5m 2>&1 | tail -5 +``` +Expected: `STATUS: deployed`. (If `--wait` times out, continue; verify pod in Step 4.) + +- [ ] **Step 4: Verify Gitea is healthy** +```bash +kubectl get pods -n foxhunt | grep gitea +kubectl run gitea-probe --rm -i --restart=Never -n foxhunt --image=curlimages/curl -- \ + curl -s http://gitea-http.foxhunt.svc.cluster.local:3000/api/healthz +``` +Expected: a JSON health blob with `"status": "pass"`. Pod `Running`. + +- [ ] **Step 5: Commit values (foxhunt repo)** +```bash +cd /home/jgrusewski/Work/foxhunt && git add infra/k8s/gitea/values.yaml +git commit -m "feat(infra): Gitea Helm values (external postgres, internal-only) — Phase 2B" +``` + +--- + +## Task 3: Migrate both repos via port-forward + validate + +**Files:** none committed (git data operations). + +- [ ] **Step 1: Port-forward Gitea + create the repos via API** +```bash +kubectl port-forward -n foxhunt svc/gitea-http 3000:3000 >/tmp/gitea-pf.log 2>&1 & echo $! >/tmp/gitea-pf.pid +sleep 3 +GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d) +for r in fxhnt foxhunt; do + curl -s -u "$GA:$GP" -X POST http://localhost:3000/api/v1/user/repos \ + -H 'Content-Type: application/json' -d "{\"name\":\"$r\",\"private\":true}" -o /dev/null -w "$r: %{http_code}\n" +done +``` +Expected: `fxhnt: 201` and `foxhunt: 201`. + +- [ ] **Step 2: Mirror-push both repos from local clones** +```bash +GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d) +( cd /home/jgrusewski/Work/fxhnt && git push --mirror "http://$GA:$GP@localhost:3000/$GA/fxhnt.git" 2>&1 | tail -2 ) +( cd /home/jgrusewski/Work/foxhunt && git push --mirror "http://$GA:$GP@localhost:3000/$GA/foxhunt.git" 2>&1 | tail -2 ) +``` +Expected: both report refs pushed (no error). foxhunt is 876 MB — may take a minute. + +- [ ] **Step 3: VALIDATE — clone back + diff top SHAs vs GitLab** +```bash +GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d) +for r in fxhnt foxhunt; do + gitea_sha=$(git ls-remote "http://$GA:$GP@localhost:3000/$GA/$r.git" HEAD | awk '{print $1}') + gitlab_sha=$(cd /home/jgrusewski/Work/$r && git ls-remote origin HEAD | awk '{print $1}') + [ "$gitea_sha" = "$gitlab_sha" ] && echo "$r: MATCH ($gitea_sha)" || echo "$r: MISMATCH gitea=$gitea_sha gitlab=$gitlab_sha" +done +``` +Expected: `fxhnt: MATCH` and `foxhunt: MATCH`. **STOP if any MISMATCH.** + +- [ ] **Step 4: Mark foxhunt archived (read-only)** +```bash +GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d) +curl -s -u "$GA:$GP" -X PATCH "http://localhost:3000/api/v1/repos/$GA/foxhunt" \ + -H 'Content-Type: application/json' -d '{"archived":true}' -o /dev/null -w "archive foxhunt: %{http_code}\n" +kill $(cat /tmp/gitea-pf.pid) 2>/dev/null +``` +Expected: `archive foxhunt: 200`. + +--- + +## Task 4: Move cockpit image to Scaleway Container Registry + +**Files:** Modify `~/Work/fxhnt/infra/argo/cockpit-build-deploy.yaml`, +`~/Work/fxhnt/infra/k8s/orchestration/dagster.yaml` (+ dashboard manifest if separate). + +- [ ] **Step 1: Create the Scaleway registry pull/push secret** +```bash +kubectl create secret docker-registry scw-registry -n foxhunt \ + --docker-server=rg.fr-par.scw.cloud \ + --docker-username=nologin --docker-password="$SCW_SECRET_KEY" \ + --dry-run=client -o yaml | kubectl apply -f - +``` +Expected: secret `created`/`configured`. (Scaleway registry auth: username `nologin`, password = a +Scaleway secret key.) + +- [ ] **Step 2: Repoint kaniko build → Scaleway registry** + +In `~/Work/fxhnt/infra/argo/cockpit-build-deploy.yaml`, change the kaniko args: +``` + --destination=rg.fr-par.scw.cloud/bizworx/fxhnt-cockpit:latest \ + --cache-repo=rg.fr-par.scw.cloud/bizworx/cache \ +``` +Remove the `--insecure-registry=…gitlab-registry…:5000` and `--skip-tls-verify-registry=…` lines +(Scaleway is TLS). Mount the `scw-registry` dockerconfig for kaniko (replace the `gitlab-registry` +dockerconfig volume): set the kaniko `DOCKER_CONFIG`/volume to the `scw-registry` secret at +`/kaniko/.docker/config.json`. + +- [ ] **Step 3: Repoint the deploy image refs** + +In `~/Work/fxhnt/infra/k8s/orchestration/dagster.yaml` (and the dashboard manifest), change every +`image: gitlab-registry.foxhunt.svc.cluster.local:5000/root/foxhunt/fxhnt-cockpit:latest` → +`image: rg.fr-par.scw.cloud/bizworx/fxhnt-cockpit:latest`, and add under each pod spec: +```yaml + imagePullSecrets: + - name: scw-registry +``` + +- [ ] **Step 4: Run one cockpit build → Scaleway + verify the image exists** +```bash +cd /home/jgrusewski/Work/fxhnt && ./scripts/argo-deploy-cockpit.sh 2>&1 | tail -8 +# verify the pushed image +scw registry image list namespace-id=6561a90a-f4ba-4f44-b43f-be6e27cbdfca 2>/dev/null | grep -i fxhnt-cockpit || echo "(verify via Scaleway console)" +``` +Expected: workflow `Succeeded`; `fxhnt-cockpit` image listed in the `bizworx` namespace. + +- [ ] **Step 5: Verify a rollout pulls from Scaleway** +```bash +kubectl rollout restart deploy/dagster deploy/fxhnt-dashboard -n foxhunt 2>/dev/null +kubectl rollout status deploy/dagster -n foxhunt --timeout=180s +kubectl get pods -n foxhunt -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | grep -i "rg.fr-par.scw.cloud/bizworx/fxhnt-cockpit" | head +``` +Expected: rollout succeeds; pods now reference the `rg.fr-par.scw.cloud/bizworx/...` image (no `ImagePullBackOff`). + +- [ ] **Step 6: Commit (fxhnt repo)** +```bash +cd /home/jgrusewski/Work/fxhnt +git add infra/argo/cockpit-build-deploy.yaml infra/k8s/orchestration/dagster.yaml +git commit -m "feat(infra): cockpit image -> Scaleway Container Registry (Phase 2B)" +``` + +--- + +## Task 5: Cut `git.fxhnt.ai` + SSH `:2222` over to Gitea + +**Files:** Modify `infra/k8s/gitlab/tailscale-proxy.yaml` (foxhunt repo). **CHECKPOINT before applying.** + +- [ ] **Step 1: Repoint nginx `git.fxhnt.ai:443` → Gitea** + +In `infra/k8s/gitlab/tailscale-proxy.yaml`, in the `server_name git.fxhnt.ai;` (`listen 443`) block, +change: +``` + proxy_pass http://gitlab-webservice-default.foxhunt.svc.cluster.local:8181; +``` +to: +``` + proxy_pass http://gitea-http.foxhunt.svc.cluster.local:3000; +``` +Delete the entire `listen 5050 ssl; server_name git.fxhnt.ai;` registry block (Scaleway registry is +external — not proxied). + +- [ ] **Step 2: Repoint the socat SSH proxy → Gitea SSH, on BOTH `:22` and `:2222`** + +Nothing ever SSHes into the git node itself, so port `22` is free for git-SSH. Expose Gitea SSH on both +the standard `:22` (clean `git@git.fxhnt.ai` URLs) and the legacy `:2222` (existing remotes keep working — +no churn). In the `ssh-proxy` container args, change the existing `:2222` target to Gitea: +``` + - "TCP-LISTEN:2222,fork,reuseaddr,sndbuf=1048576,rcvbuf=1048576" + - "TCP:gitea-ssh.foxhunt.svc.cluster.local:22,sndbuf=1048576,rcvbuf=1048576" +``` +Then add a SECOND socat container in the same pod for `:22` (duplicate the `ssh-proxy` container, rename +`ssh-proxy-22`, listen on `22`, `containerPort: 22`): +```yaml + - name: ssh-proxy-22 + image: alpine/socat:latest + args: + - "TCP-LISTEN:22,fork,reuseaddr,sndbuf=1048576,rcvbuf=1048576" + - "TCP:gitea-ssh.foxhunt.svc.cluster.local:22,sndbuf=1048576,rcvbuf=1048576" + ports: + - containerPort: 22 +``` +Ensure the Tailscale node/proxy advertises both ports `22` and `2222` (the tailnet sidecar's exposed +ports / `serve` config — add `22` alongside the existing `2222`). + +- [ ] **Step 3: Apply + restart the proxy** +```bash +cd /home/jgrusewski/Work/foxhunt +kubectl apply -f infra/k8s/gitlab/tailscale-proxy.yaml +kubectl rollout restart deploy/tailscale-gitlab-proxy -n foxhunt +kubectl rollout status deploy/tailscale-gitlab-proxy -n foxhunt --timeout=120s +``` +Expected: rollout complete. + +- [ ] **Step 4: GATE — verify `git.fxhnt.ai` serves Gitea** +```bash +curl -sk https://git.fxhnt.ai/api/healthz | head -c 200; echo +echo "--- SSH on :2222 (legacy remotes) ---"; git ls-remote ssh://git@git.fxhnt.ai:2222/gitadmin/fxhnt.git HEAD 2>&1 | head -1 +echo "--- SSH on :22 (clean URLs) ---"; git ls-remote ssh://git@git.fxhnt.ai/gitadmin/fxhnt.git HEAD 2>&1 | head -1 +``` +Expected: Gitea healthz JSON; BOTH ssh ls-remotes (`:2222` and `:22`) return the HEAD sha (proves Gitea +web + SSH on both ports on the canonical name). **STOP + rollback (revert this file, re-apply) if any fail.** + +- [ ] **Step 5: Commit (foxhunt repo)** +```bash +cd /home/jgrusewski/Work/foxhunt && git add infra/k8s/gitlab/tailscale-proxy.yaml +git commit -m "feat(infra): cut git.fxhnt.ai + SSH :2222 over to Gitea; drop :5050 registry (Phase 2B)" +``` + +--- + +## Task 6: Wire Gitea push webhook → Argo Events auto-deploy + +**Files:** Create `infra/k8s/argo/events/gitea-push-eventsource.yaml` + +`infra/k8s/argo/events/gitea-deploy-sensor.yaml` (foxhunt repo). + +- [ ] **Step 1: Create the webhook eventsource** + +Create `infra/k8s/argo/events/gitea-push-eventsource.yaml`: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: EventSource +metadata: + name: gitea-push + namespace: foxhunt +spec: + service: + ports: + - port: 12000 + targetPort: 12000 + webhook: + fxhnt-push: + port: "12000" + endpoint: /push + method: POST +``` + +- [ ] **Step 2: Create the sensor that submits the cockpit workflow** + +Create `infra/k8s/argo/events/gitea-deploy-sensor.yaml`: +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Sensor +metadata: + name: gitea-deploy + namespace: foxhunt +spec: + dependencies: + - name: push + eventSourceName: gitea-push + eventName: fxhnt-push + filters: + data: + - path: body.ref + type: string + value: ["refs/heads/main", "refs/heads/master"] + triggers: + - template: + name: submit-cockpit + argoWorkflow: + operation: submit + source: + resource: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: fxhnt-cockpit- + namespace: foxhunt + spec: + workflowTemplateRef: + name: fxhnt-cockpit +``` + +- [ ] **Step 3: Apply + register the webhook in Gitea** +```bash +cd /home/jgrusewski/Work/foxhunt +kubectl apply -f infra/k8s/argo/events/gitea-push-eventsource.yaml -f infra/k8s/argo/events/gitea-deploy-sensor.yaml +kubectl get pods -n foxhunt | grep -iE "gitea-push|gitea-deploy" +# register webhook (in-cluster URL) on the fxhnt repo +GA=gitadmin; GP=$(kubectl get secret gitea-admin -n foxhunt -o jsonpath='{.data.password}'|base64 -d) +kubectl port-forward -n foxhunt svc/gitea-http 3000:3000 >/tmp/gitea-pf.log 2>&1 & echo $! >/tmp/gitea-pf.pid; sleep 3 +curl -s -u "$GA:$GP" -X POST "http://localhost:3000/api/v1/repos/$GA/fxhnt/hooks" -H 'Content-Type: application/json' -d '{ + "type":"gitea","active":true,"events":["push"], + "config":{"url":"http://gitea-push-eventsource-svc.foxhunt.svc.cluster.local:12000/push","content_type":"json"} +}' -o /dev/null -w "hook: %{http_code}\n" +kill $(cat /tmp/gitea-pf.pid) 2>/dev/null +``` +Expected: eventsource + sensor pods `Running`; `hook: 201`. + +- [ ] **Step 4: GATE — test push triggers a deploy** +```bash +cd /home/jgrusewski/Work/fxhnt +git commit --allow-empty -m "test: trigger Gitea->Argo cockpit deploy" +git push origin main # origin already = git.fxhnt.ai (now Gitea) +sleep 10 +kubectl get wf -n foxhunt --sort-by=.metadata.creationTimestamp 2>/dev/null | grep fxhnt-cockpit | tail -2 +``` +Expected: a new `fxhnt-cockpit-*` workflow appears (triggered by the push). **If none fires**, check the +eventsource pod logs; fallback is the manual `./scripts/argo-deploy-cockpit.sh` (still works). + +- [ ] **Step 5: Commit (foxhunt repo)** +```bash +cd /home/jgrusewski/Work/foxhunt && git add infra/k8s/argo/events/gitea-push-eventsource.yaml infra/k8s/argo/events/gitea-deploy-sensor.yaml +git commit -m "feat(infra): Gitea push webhook -> Argo Events cockpit auto-deploy (Phase 2B)" +``` + +--- + +## Task 7: Final verification + +- [ ] **Step 1: Footprint + health summary** +```bash +echo "=== Gitea (new) ==="; kubectl top pods -n foxhunt 2>/dev/null | grep -i gitea +echo "=== cockpit healthy on Scaleway image ==="; curl -sk -o /dev/null -w "dashboard.fxhnt.ai: %{http_code}\n" https://dashboard.fxhnt.ai +echo "=== git.fxhnt.ai serves Gitea ==="; curl -sk https://git.fxhnt.ai/api/healthz | head -c 80; echo +echo "=== GitLab still running (fallback for 2C) ==="; kubectl get pods -n foxhunt | grep -c gitlab | xargs echo "gitlab pods:" +``` +Expected: Gitea ≤~300 MiB; dashboard 200; git.fxhnt.ai healthz pass; GitLab pods still present. + +--- + +## Rollback +- **Cutover (Task 5):** `git checkout infra/k8s/gitlab/tailscale-proxy.yaml` + `kubectl apply` + rollout + restart → `git.fxhnt.ai`/SSH back to GitLab. +- **Registry (Task 4):** revert the fxhnt manifests + rollout restart → pods pull the GitLab image again + (still present until 2C). +- **Gitea:** `helm uninstall gitea -n foxhunt`; drop the `gitea` DB. GitLab was never touched. + +--- + +## Acceptance criteria (from spec) +- Gitea running on existing postgres, healthz pass, ≤~300 MiB. ✅ Task 2, Task 7 +- Both repos in Gitea, SHAs match GitLab, foxhunt archived. ✅ Task 3 +- Cockpit builds to + deploys from Scaleway registry; dashboard 200. ✅ Task 4, Task 7 +- `git.fxhnt.ai` (HTTPS + SSH :2222) serves Gitea; remotes unchanged. ✅ Task 5 +- Push to fxhnt main auto-triggers a cockpit deploy. ✅ Task 6 +- GitLab still running as fallback. ✅ Task 7 +``` diff --git a/docs/superpowers/specs/2026-06-21-gitea-replace-gitlab-design.md b/docs/superpowers/specs/2026-06-21-gitea-replace-gitlab-design.md index 92a16716f..c1a156b74 100644 --- a/docs/superpowers/specs/2026-06-21-gitea-replace-gitlab-design.md +++ b/docs/superpowers/specs/2026-06-21-gitea-replace-gitlab-design.md @@ -23,8 +23,10 @@ breaking the live cockpit deploy. - **Repos:** migrate **both** — `fxhnt` (active) and `foxhunt` (876 MB, flagged **archived/read-only**) — via `git push --mirror` from existing local clones. - **Canonical hostname stays `git.fxhnt.ai`** (no permanent `gitea.fxhnt.ai`, no remote-URL churn). - Gitea comes up internal-only; validate over a port-forward; then cut `git.fxhnt.ai` (HTTPS) + SSH - `:2222` over to Gitea in one switch. GitLab keeps running (unaddressed) as the 2C-removal fallback. + Gitea comes up internal-only; validate over a port-forward; then cut `git.fxhnt.ai` (HTTPS) + SSH over + to Gitea in one switch. **SSH exposed on BOTH `:22` and `:2222`** — nothing host-SSHes the git node so + `:22` is free; Gitea advertises the clean `git@git.fxhnt.ai` (`:22`) in new clone URLs while existing + `:2222` remotes keep working (no churn). GitLab keeps running (unaddressed) as the 2C-removal fallback. - **Container registry:** cockpit image → **Scaleway Container Registry** (`rg.fr-par.scw.cloud/bizworx/`, namespace exists). Retire the in-cluster GitLab registry. External/managed, no registry pod. - **Argo integration:** Gitea push webhook → Argo Events webhook eventsource → sensor → submit @@ -42,7 +44,8 @@ breaking the live cockpit deploy. 3. **Exposure:** - HTTPS: extend the nginx `*.fxhnt.ai` proxy (`infra/k8s/gitlab/tailscale-proxy.yaml`) — at cutover, repoint the `git.fxhnt.ai` server block `proxy_pass` → `gitea-http.foxhunt.svc:3000`. - - SSH: repoint the Tailscale-exposed `:2222` → Gitea SSH service at cutover. + - SSH: expose Gitea SSH on BOTH `:22` and `:2222` via the Tailscale proxy (two socat listeners) at + cutover; advertise `:22` in clone URLs, keep `:2222` for existing remotes. - Registry `:5050` server block: removed (Scaleway registry is external, not proxied here). 4. **Registry migration:** - `fxhnt/infra/argo/cockpit-build-deploy.yaml`: kaniko `--destination` + `--cache-repo` →