Covers: Terragrunt node pools, object storage buckets, Postgres init, GitLab CE Helm deploy, Runner with K8s executor, .gitlab-ci.yml translation (Kaniko builds), manifest cutover, decommission steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1289 lines
36 KiB
Markdown
1289 lines
36 KiB
Markdown
# GitLab CE Migration Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Migrate from Gitea to GitLab CE running in Kapsule with K8s-native CI (scale-to-zero build pods), GitLab built-in registry, and external Postgres/Redis.
|
|
|
|
**Architecture:** GitLab CE deployed via Helm on a dedicated `gitlab` node pool (DEV1-L). GitLab Runner with Kubernetes executor spawns ephemeral build pods on a `ci-build` pool (GP1-XS, 0-2). External Postgres and Redis on existing always-on pool. Kaniko for unprivileged Docker builds. Tailscale-only access.
|
|
|
|
**Tech Stack:** Terraform/Terragrunt (Scaleway provider), Helm 3, GitLab CE Helm chart, GitLab Runner Helm chart, Kaniko, Scaleway Kapsule, Scaleway Object Storage (S3-compatible)
|
|
|
|
**Design doc:** `docs/plans/2026-02-24-gitlab-migration-design.md`
|
|
|
|
---
|
|
|
|
## Phase 1: Infrastructure (Terragrunt)
|
|
|
|
### Task 1: Add `gitlab` and `ci-build` node pools to Kapsule module
|
|
|
|
**Files:**
|
|
- Modify: `infra/modules/kapsule/variables.tf`
|
|
- Modify: `infra/modules/kapsule/main.tf`
|
|
- Modify: `infra/modules/kapsule/outputs.tf`
|
|
- Modify: `infra/live/production/kapsule/terragrunt.hcl`
|
|
|
|
**Step 1: Add variables for new pools**
|
|
|
|
Append to `infra/modules/kapsule/variables.tf`:
|
|
|
|
```hcl
|
|
variable "enable_gitlab_pool" {
|
|
description = "Create dedicated node pool for GitLab CE"
|
|
type = bool
|
|
default = false
|
|
}
|
|
|
|
variable "gitlab_type" {
|
|
description = "Instance type for the GitLab node pool"
|
|
type = string
|
|
default = "DEV1-L"
|
|
}
|
|
|
|
variable "enable_ci_build_pool" {
|
|
description = "Create dedicated node pool for CI build pods"
|
|
type = bool
|
|
default = false
|
|
}
|
|
|
|
variable "ci_build_type" {
|
|
description = "Instance type for the CI build node pool"
|
|
type = string
|
|
default = "GP1-XS"
|
|
}
|
|
|
|
variable "ci_build_max_size" {
|
|
description = "Maximum number of nodes in the CI build pool"
|
|
type = number
|
|
default = 2
|
|
}
|
|
```
|
|
|
|
**Step 2: Add node pool resources**
|
|
|
|
Append to `infra/modules/kapsule/main.tf` (after the `gpu_inference` resource):
|
|
|
|
```hcl
|
|
# GitLab CE node pool (dedicated DEV1-L for GitLab + Runner manager)
|
|
resource "scaleway_k8s_pool" "gitlab" {
|
|
count = var.enable_gitlab_pool ? 1 : 0
|
|
cluster_id = scaleway_k8s_cluster.foxhunt.id
|
|
name = "gitlab"
|
|
node_type = var.gitlab_type
|
|
size = 1
|
|
min_size = 1
|
|
max_size = 1
|
|
autoscaling = false
|
|
autohealing = true
|
|
region = var.region
|
|
}
|
|
|
|
# CI build pod pool (scale-to-zero for ephemeral GitLab Runner build pods)
|
|
resource "scaleway_k8s_pool" "ci_build" {
|
|
count = var.enable_ci_build_pool ? 1 : 0
|
|
cluster_id = scaleway_k8s_cluster.foxhunt.id
|
|
name = "ci-build"
|
|
node_type = var.ci_build_type
|
|
size = 0
|
|
min_size = 0
|
|
max_size = var.ci_build_max_size
|
|
autoscaling = true
|
|
autohealing = true
|
|
region = var.region
|
|
|
|
lifecycle {
|
|
ignore_changes = [size]
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 3: Add outputs**
|
|
|
|
Append to `infra/modules/kapsule/outputs.tf`:
|
|
|
|
```hcl
|
|
output "gitlab_pool_id" {
|
|
description = "ID of the GitLab node pool"
|
|
value = var.enable_gitlab_pool ? scaleway_k8s_pool.gitlab[0].id : ""
|
|
}
|
|
|
|
output "ci_build_pool_id" {
|
|
description = "ID of the CI build node pool"
|
|
value = var.enable_ci_build_pool ? scaleway_k8s_pool.ci_build[0].id : ""
|
|
}
|
|
```
|
|
|
|
**Step 4: Enable pools in terragrunt inputs**
|
|
|
|
Add to `infra/live/production/kapsule/terragrunt.hcl` inputs block:
|
|
|
|
```hcl
|
|
# GitLab CE pool (dedicated for GitLab + Runner manager)
|
|
enable_gitlab_pool = true
|
|
gitlab_type = "DEV1-L"
|
|
|
|
# CI build pool (ephemeral runner pods, scale-to-zero)
|
|
enable_ci_build_pool = true
|
|
ci_build_type = "GP1-XS"
|
|
ci_build_max_size = 2
|
|
```
|
|
|
|
**Step 5: Validate terraform plan**
|
|
|
|
Run: `cd infra/live/production/kapsule && terragrunt plan`
|
|
|
|
Expected: Plan shows 2 new resources (`scaleway_k8s_pool.gitlab[0]`, `scaleway_k8s_pool.ci_build[0]`), 0 destroyed.
|
|
|
|
**Step 6: Apply**
|
|
|
|
Run: `cd infra/live/production/kapsule && terragrunt apply`
|
|
|
|
Expected: 2 added. Verify with `kubectl get nodes` — new `gitlab` node should appear within ~3 minutes.
|
|
|
|
**Step 7: Commit**
|
|
|
|
```bash
|
|
git add infra/modules/kapsule/ infra/live/production/kapsule/terragrunt.hcl
|
|
git commit -m "infra(kapsule): add gitlab and ci-build node pools"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Add GitLab object storage buckets
|
|
|
|
**Files:**
|
|
- Modify: `infra/modules/object-storage/main.tf`
|
|
- Modify: `infra/modules/object-storage/variables.tf`
|
|
- Modify: `infra/modules/object-storage/outputs.tf` (create if missing)
|
|
|
|
**Step 1: Add bucket resources**
|
|
|
|
Append to `infra/modules/object-storage/main.tf`:
|
|
|
|
```hcl
|
|
resource "scaleway_object_bucket" "gitlab_registry" {
|
|
name = "${var.bucket_name_prefix}-gitlab-registry"
|
|
region = var.region
|
|
|
|
versioning {
|
|
enabled = false
|
|
}
|
|
}
|
|
|
|
resource "scaleway_object_bucket" "gitlab_artifacts" {
|
|
name = "${var.bucket_name_prefix}-gitlab-artifacts"
|
|
region = var.region
|
|
|
|
lifecycle_rule {
|
|
enabled = true
|
|
prefix = "tmp/"
|
|
|
|
expiration {
|
|
days = 7
|
|
}
|
|
}
|
|
|
|
versioning {
|
|
enabled = false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Add variable**
|
|
|
|
Append to `infra/modules/object-storage/variables.tf`:
|
|
|
|
```hcl
|
|
variable "bucket_name_prefix" {
|
|
description = "Prefix for additional buckets (e.g., foxhunt)"
|
|
type = string
|
|
default = "foxhunt"
|
|
}
|
|
```
|
|
|
|
**Step 3: Create outputs file**
|
|
|
|
Create `infra/modules/object-storage/outputs.tf`:
|
|
|
|
```hcl
|
|
output "artifacts_bucket_name" {
|
|
description = "Name of the main artifacts bucket"
|
|
value = scaleway_object_bucket.artifacts.name
|
|
}
|
|
|
|
output "gitlab_registry_bucket_name" {
|
|
description = "Name of the GitLab registry bucket"
|
|
value = scaleway_object_bucket.gitlab_registry.name
|
|
}
|
|
|
|
output "gitlab_artifacts_bucket_name" {
|
|
description = "Name of the GitLab artifacts bucket"
|
|
value = scaleway_object_bucket.gitlab_artifacts.name
|
|
}
|
|
```
|
|
|
|
**Step 4: Validate and apply**
|
|
|
|
Run: `cd infra/live/production/object-storage && terragrunt plan`
|
|
|
|
Expected: 2 new buckets, 0 changes to existing `artifacts` bucket.
|
|
|
|
Run: `cd infra/live/production/object-storage && terragrunt apply`
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add infra/modules/object-storage/
|
|
git commit -m "infra(storage): add GitLab registry and artifacts buckets"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Create GitLab database and user in Postgres
|
|
|
|
**Files:**
|
|
- Create: `infra/k8s/gitlab/postgres-init.yaml` (one-time Job)
|
|
|
|
**Step 1: Create the init job manifest**
|
|
|
|
Create `infra/k8s/gitlab/postgres-init.yaml`:
|
|
|
|
```yaml
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: gitlab-postgres-init
|
|
namespace: foxhunt
|
|
labels:
|
|
app.kubernetes.io/name: gitlab-postgres-init
|
|
app.kubernetes.io/part-of: foxhunt
|
|
spec:
|
|
ttlSecondsAfterFinished: 300
|
|
template:
|
|
spec:
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: always-on
|
|
restartPolicy: Never
|
|
containers:
|
|
- name: init
|
|
image: timescale/timescaledb:latest-pg16
|
|
command:
|
|
- bash
|
|
- -c
|
|
- |
|
|
set -euo pipefail
|
|
export PGPASSWORD="$POSTGRES_PASSWORD"
|
|
|
|
# Wait for postgres to be ready
|
|
until pg_isready -h postgres -U foxhunt; do
|
|
echo "Waiting for postgres..."
|
|
sleep 2
|
|
done
|
|
|
|
# Create gitlab database and user
|
|
psql -h postgres -U foxhunt -d foxhunt -c "
|
|
DO \$\$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'gitlab') THEN
|
|
CREATE ROLE gitlab WITH LOGIN PASSWORD '$GITLAB_DB_PASSWORD';
|
|
END IF;
|
|
END
|
|
\$\$;
|
|
"
|
|
psql -h postgres -U foxhunt -c "
|
|
SELECT 'CREATE DATABASE gitlab OWNER gitlab'
|
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'gitlab')
|
|
\gexec"
|
|
psql -h postgres -U foxhunt -d gitlab -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
|
psql -h postgres -U foxhunt -d gitlab -c "CREATE EXTENSION IF NOT EXISTS btree_gist;"
|
|
psql -h postgres -U foxhunt -d gitlab -c "GRANT ALL PRIVILEGES ON DATABASE gitlab TO gitlab;"
|
|
psql -h postgres -U foxhunt -d gitlab -c "GRANT ALL ON SCHEMA public TO gitlab;"
|
|
|
|
echo "GitLab database initialized successfully."
|
|
env:
|
|
- name: POSTGRES_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: foxhunt-secrets
|
|
key: db-password
|
|
- name: GITLAB_DB_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gitlab-secrets
|
|
key: db-password
|
|
```
|
|
|
|
**Step 2: Create GitLab secrets**
|
|
|
|
Generate a password and create the secret (run locally, NOT in a file):
|
|
|
|
```bash
|
|
GITLAB_DB_PASS=$(openssl rand -base64 32)
|
|
kubectl -n foxhunt create secret generic gitlab-secrets \
|
|
--from-literal=db-password="$GITLAB_DB_PASS" \
|
|
--from-literal=root-password="$(openssl rand -base64 24)" \
|
|
--from-literal=runner-token="" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
echo "GitLab DB password: $GITLAB_DB_PASS"
|
|
```
|
|
|
|
Save the `GITLAB_DB_PASS` value — needed for Helm values in Task 4.
|
|
|
|
**Step 3: Run the init job**
|
|
|
|
```bash
|
|
kubectl apply -f infra/k8s/gitlab/postgres-init.yaml
|
|
kubectl -n foxhunt wait --for=condition=complete job/gitlab-postgres-init --timeout=60s
|
|
kubectl -n foxhunt logs job/gitlab-postgres-init
|
|
```
|
|
|
|
Expected: "GitLab database initialized successfully."
|
|
|
|
**Step 4: Verify**
|
|
|
|
```bash
|
|
kubectl -n foxhunt exec deployment/postgres -- psql -U foxhunt -c "\l" | grep gitlab
|
|
```
|
|
|
|
Expected: Row showing `gitlab` database owned by `gitlab`.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add infra/k8s/gitlab/
|
|
git commit -m "infra(gitlab): postgres init job for GitLab database"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2: GitLab CE Deployment
|
|
|
|
### Task 4: Create GitLab CE Helm values and deploy
|
|
|
|
**Files:**
|
|
- Create: `infra/k8s/gitlab/values.yaml`
|
|
|
|
**Step 1: Add GitLab Helm repo**
|
|
|
|
```bash
|
|
helm repo add gitlab https://charts.gitlab.io/
|
|
helm repo update
|
|
```
|
|
|
|
**Step 2: Create Helm values file**
|
|
|
|
Create `infra/k8s/gitlab/values.yaml`:
|
|
|
|
```yaml
|
|
# GitLab CE - Foxhunt deployment
|
|
# Runs on dedicated 'gitlab' node pool (DEV1-L)
|
|
# External Postgres + Redis on always-on pool
|
|
|
|
global:
|
|
edition: ce
|
|
hosts:
|
|
domain: fxhnt.ai
|
|
gitlab:
|
|
name: gitlab.fxhnt.ai
|
|
registry:
|
|
name: gitlab.fxhnt.ai
|
|
ingress:
|
|
enabled: false
|
|
configureCertmanager: false
|
|
shell:
|
|
port: 2222
|
|
registry:
|
|
bucket: foxhunt-gitlab-registry
|
|
appConfig:
|
|
lfs:
|
|
bucket: foxhunt-gitlab-artifacts
|
|
connection:
|
|
secret: gitlab-s3-credentials
|
|
key: connection
|
|
artifacts:
|
|
bucket: foxhunt-gitlab-artifacts
|
|
connection:
|
|
secret: gitlab-s3-credentials
|
|
key: connection
|
|
uploads:
|
|
bucket: foxhunt-gitlab-artifacts
|
|
connection:
|
|
secret: gitlab-s3-credentials
|
|
key: connection
|
|
packages:
|
|
bucket: foxhunt-gitlab-artifacts
|
|
connection:
|
|
secret: gitlab-s3-credentials
|
|
key: connection
|
|
|
|
# Disable bundled databases — use external
|
|
postgresql:
|
|
install: false
|
|
redis:
|
|
install: false
|
|
|
|
# External Postgres (existing pod on always-on pool)
|
|
global.psql:
|
|
host: postgres.foxhunt.svc.cluster.local
|
|
port: 5432
|
|
database: gitlab
|
|
username: gitlab
|
|
password:
|
|
secret: gitlab-secrets
|
|
key: db-password
|
|
|
|
# External Redis (existing pod on always-on pool, DB index 8)
|
|
global.redis:
|
|
host: redis.foxhunt.svc.cluster.local
|
|
port: 6379
|
|
password:
|
|
enabled: false
|
|
# Use DB index 8 to avoid collision with application data
|
|
# Set via extraEnv on individual components
|
|
|
|
# Disable components we don't need
|
|
certmanager:
|
|
install: false
|
|
nginx-ingress:
|
|
enabled: false
|
|
prometheus:
|
|
install: false
|
|
grafana:
|
|
install: false
|
|
gitlab-runner:
|
|
install: false # We deploy runner separately with K8s executor config
|
|
minio:
|
|
install: false
|
|
|
|
# Registry — S3-backed
|
|
registry:
|
|
storage:
|
|
secret: gitlab-s3-credentials
|
|
key: registry
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
|
|
# GitLab core components — all on gitlab node pool
|
|
gitlab:
|
|
webservice:
|
|
replicaCount: 1
|
|
workerProcesses: 2
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
resources:
|
|
requests:
|
|
cpu: 500m
|
|
memory: 2Gi
|
|
limits:
|
|
cpu: 2000m
|
|
memory: 4Gi
|
|
extraEnv:
|
|
REDIS_URL: "redis://redis.foxhunt.svc.cluster.local:6379/8"
|
|
|
|
sidekiq:
|
|
replicas: 1
|
|
concurrency: 10
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
resources:
|
|
requests:
|
|
cpu: 250m
|
|
memory: 1Gi
|
|
limits:
|
|
cpu: 1000m
|
|
memory: 2Gi
|
|
extraEnv:
|
|
REDIS_URL: "redis://redis.foxhunt.svc.cluster.local:6379/8"
|
|
|
|
gitaly:
|
|
persistence:
|
|
enabled: true
|
|
size: 50Gi
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
resources:
|
|
requests:
|
|
cpu: 200m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: 1000m
|
|
memory: 2Gi
|
|
|
|
gitlab-shell:
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
service:
|
|
type: NodePort
|
|
nodePort: 32222
|
|
|
|
toolbox:
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
|
|
kas:
|
|
enabled: false
|
|
|
|
gitlab-exporter:
|
|
enabled: false
|
|
```
|
|
|
|
**Step 3: Create S3 credentials secret**
|
|
|
|
The GitLab Helm chart expects S3 credentials in a specific format. Create the secret (run locally):
|
|
|
|
```bash
|
|
# S3 connection config for GitLab (Scaleway Object Storage)
|
|
cat <<'CONN_EOF' > /tmp/gitlab-s3-connection.yaml
|
|
provider: AWS
|
|
aws_access_key_id: "<SCW_ACCESS_KEY>"
|
|
aws_secret_access_key: "<SCW_SECRET_KEY>"
|
|
region: fr-par
|
|
endpoint: "https://s3.fr-par.scw.cloud"
|
|
path_style: true
|
|
CONN_EOF
|
|
|
|
# Registry-specific S3 config
|
|
cat <<'REG_EOF' > /tmp/gitlab-s3-registry.yaml
|
|
s3:
|
|
accesskey: "<SCW_ACCESS_KEY>"
|
|
secretkey: "<SCW_SECRET_KEY>"
|
|
region: fr-par
|
|
regionendpoint: "https://s3.fr-par.scw.cloud"
|
|
bucket: foxhunt-gitlab-registry
|
|
REG_EOF
|
|
|
|
# Replace placeholders with real values from ~/.config/scw/config.yaml
|
|
# Then create the secret:
|
|
kubectl -n foxhunt create secret generic gitlab-s3-credentials \
|
|
--from-file=connection=/tmp/gitlab-s3-connection.yaml \
|
|
--from-file=registry=/tmp/gitlab-s3-registry.yaml \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
rm /tmp/gitlab-s3-connection.yaml /tmp/gitlab-s3-registry.yaml
|
|
```
|
|
|
|
**Step 4: Taint the gitlab node**
|
|
|
|
Once the gitlab node pool is running (from Task 1):
|
|
|
|
```bash
|
|
# Find the gitlab node name
|
|
GITLAB_NODE=$(kubectl get nodes -l k8s.scaleway.com/pool-name=gitlab -o jsonpath='{.items[0].metadata.name}')
|
|
kubectl taint nodes "$GITLAB_NODE" gitlab=true:NoSchedule --overwrite
|
|
```
|
|
|
|
**Step 5: Deploy GitLab CE**
|
|
|
|
```bash
|
|
helm upgrade --install gitlab gitlab/gitlab \
|
|
--namespace foxhunt \
|
|
--values infra/k8s/gitlab/values.yaml \
|
|
--timeout 10m \
|
|
--wait
|
|
```
|
|
|
|
Expected: All GitLab pods running on the gitlab node. This may take 5-8 minutes.
|
|
|
|
**Step 6: Verify**
|
|
|
|
```bash
|
|
kubectl -n foxhunt get pods -l app.kubernetes.io/part-of=gitlab -o wide
|
|
```
|
|
|
|
Expected: webservice, sidekiq, gitaly, gitlab-shell, toolbox pods all `Running` on the gitlab node.
|
|
|
|
**Step 7: Expose GitLab via Tailscale**
|
|
|
|
GitLab webservice is a ClusterIP service. Access it via the Tailscale subnet router (already advertising `10.42.0.0/16` pod CIDR):
|
|
|
|
```bash
|
|
# Get the webservice ClusterIP
|
|
kubectl -n foxhunt get svc -l app.kubernetes.io/name=webservice
|
|
```
|
|
|
|
Alternatively, create a NodePort service or use `kubectl port-forward` for initial testing:
|
|
|
|
```bash
|
|
kubectl -n foxhunt port-forward svc/gitlab-webservice-default 8443:8443
|
|
# Then access https://localhost:8443
|
|
```
|
|
|
|
**Step 8: Add DNS record**
|
|
|
|
```bash
|
|
# Get the gitlab node's Tailscale-routable IP (via subnet router)
|
|
# Or use the webservice ClusterIP if the subnet router is advertising pod CIDR
|
|
scw dns record add fxhnt.ai name=gitlab type=A data=<TAILSCALE_IP> ttl=300
|
|
```
|
|
|
|
**Step 9: Set root password and create project**
|
|
|
|
Access `https://gitlab.fxhnt.ai` and:
|
|
1. Log in with `root` / the password from `gitlab-secrets` `root-password`
|
|
2. Create group `foxhunt`
|
|
3. Create project `foxhunt/foxhunt`
|
|
4. Go to Settings → CI/CD → Runners and note the registration token (for Task 6)
|
|
|
|
**Step 10: Commit**
|
|
|
|
```bash
|
|
git add infra/k8s/gitlab/values.yaml
|
|
git commit -m "infra(gitlab): Helm values for GitLab CE deployment"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Push-mirror repository from Gitea to GitLab
|
|
|
|
**No files to modify — operational step.**
|
|
|
|
**Step 1: Add GitLab as a remote**
|
|
|
|
```bash
|
|
git remote add gitlab ssh://git@gitlab.fxhnt.ai:2222/foxhunt/foxhunt.git
|
|
```
|
|
|
|
**Step 2: Push mirror**
|
|
|
|
```bash
|
|
git push gitlab --mirror
|
|
```
|
|
|
|
Expected: All branches, tags, and refs pushed. Output shows `main` and all tags.
|
|
|
|
**Step 3: Verify on GitLab**
|
|
|
|
Navigate to `https://gitlab.fxhnt.ai/foxhunt/foxhunt` and verify:
|
|
- Full commit history present
|
|
- All branches visible
|
|
- File tree matches Gitea
|
|
|
|
---
|
|
|
|
### Task 6: Deploy GitLab Runner with Kubernetes executor
|
|
|
|
**Files:**
|
|
- Create: `infra/k8s/gitlab/runner-values.yaml`
|
|
|
|
**Step 1: Create runner Helm values**
|
|
|
|
Create `infra/k8s/gitlab/runner-values.yaml`:
|
|
|
|
```yaml
|
|
# GitLab Runner — Kubernetes executor targeting ci-build pool
|
|
# Runner manager pod lives on gitlab node pool
|
|
# Build pods spawn on ci-build pool (scale-to-zero)
|
|
|
|
gitlabUrl: https://gitlab.fxhnt.ai
|
|
# runnerToken set via --set at install time
|
|
|
|
replicas: 1
|
|
|
|
nodeSelector:
|
|
k8s.scaleway.com/pool-name: gitlab
|
|
tolerations:
|
|
- key: gitlab
|
|
operator: Equal
|
|
value: "true"
|
|
effect: NoSchedule
|
|
|
|
runners:
|
|
config: |
|
|
[[runners]]
|
|
[runners.kubernetes]
|
|
namespace = "foxhunt"
|
|
image = "rust:1.89-slim"
|
|
privileged = false
|
|
|
|
# Build pods on ci-build pool
|
|
[runners.kubernetes.node_selector]
|
|
"k8s.scaleway.com/pool-name" = "ci-build"
|
|
[[runners.kubernetes.node_tolerations]]
|
|
key = "ci-build"
|
|
operator = "Equal"
|
|
value = "true"
|
|
effect = "NoSchedule"
|
|
|
|
# Resource limits for build pods
|
|
cpu_request = "2000m"
|
|
cpu_limit = "6000m"
|
|
memory_request = "4Gi"
|
|
memory_limit = "12Gi"
|
|
|
|
# Helper container
|
|
helper_cpu_request = "100m"
|
|
helper_cpu_limit = "500m"
|
|
helper_memory_request = "128Mi"
|
|
helper_memory_limit = "512Mi"
|
|
|
|
# Service containers (for kaniko sidecar)
|
|
[runners.kubernetes.pod_labels]
|
|
"app.kubernetes.io/part-of" = "foxhunt-ci"
|
|
|
|
# Runner tags for job matching
|
|
tags: "kapsule,rust,docker"
|
|
|
|
# Concurrency
|
|
concurrent: 4
|
|
|
|
# RBAC for runner to spawn pods
|
|
rbac:
|
|
create: true
|
|
rules:
|
|
- apiGroups: [""]
|
|
resources: ["pods", "pods/exec", "pods/log", "secrets", "configmaps"]
|
|
verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
|
|
- apiGroups: [""]
|
|
resources: ["pods/attach"]
|
|
verbs: ["create", "get"]
|
|
|
|
serviceAccount:
|
|
create: true
|
|
name: gitlab-runner
|
|
```
|
|
|
|
**Step 2: Register and deploy runner**
|
|
|
|
Get the runner registration token from GitLab UI (Settings → CI/CD → Runners), then:
|
|
|
|
```bash
|
|
helm upgrade --install gitlab-runner gitlab/gitlab-runner \
|
|
--namespace foxhunt \
|
|
--values infra/k8s/gitlab/runner-values.yaml \
|
|
--set runnerToken="<RUNNER_TOKEN_FROM_GITLAB_UI>" \
|
|
--timeout 5m \
|
|
--wait
|
|
```
|
|
|
|
**Step 3: Verify runner is registered**
|
|
|
|
```bash
|
|
kubectl -n foxhunt get pods -l app=gitlab-runner
|
|
```
|
|
|
|
Expected: 1 runner manager pod `Running` on the gitlab node.
|
|
|
|
Also verify in GitLab UI: Settings → CI/CD → Runners → should show 1 runner online.
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git add infra/k8s/gitlab/runner-values.yaml
|
|
git commit -m "infra(gitlab): Runner Helm values with K8s executor for ci-build pool"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 3: CI Pipeline
|
|
|
|
### Task 7: Translate Gitea Actions workflow to `.gitlab-ci.yml`
|
|
|
|
**Files:**
|
|
- Create: `.gitlab-ci.yml`
|
|
- Reference: `.gitea/workflows/ci.yaml` (existing, for translation)
|
|
|
|
**Step 1: Create `.gitlab-ci.yml`**
|
|
|
|
Create `.gitlab-ci.yml` at the repo root:
|
|
|
|
```yaml
|
|
# GitLab CI/CD — Foxhunt
|
|
# Runs on ci-build pool (GP1-XS, scale-to-zero)
|
|
# Uses sccache for Rust build caching, Kaniko for Docker builds
|
|
|
|
stages:
|
|
- check
|
|
- test
|
|
- build
|
|
- deploy
|
|
|
|
variables:
|
|
SQLX_OFFLINE: "true"
|
|
CARGO_TERM_COLOR: always
|
|
SCCACHE_BUCKET: $SCCACHE_BUCKET
|
|
SCCACHE_ENDPOINT: $SCCACHE_ENDPOINT
|
|
SCCACHE_S3_USE_SSL: "true"
|
|
AWS_ACCESS_KEY_ID: $SCW_ACCESS_KEY
|
|
AWS_SECRET_ACCESS_KEY: $SCW_SECRET_KEY
|
|
RUSTC_WRAPPER: /usr/local/bin/sccache
|
|
REGISTRY: ${CI_REGISTRY_IMAGE}
|
|
|
|
# Base template for Rust jobs
|
|
.rust-base:
|
|
image: rust:1.89-slim
|
|
tags:
|
|
- kapsule
|
|
- rust
|
|
before_script:
|
|
- apt-get update && apt-get install -y protobuf-compiler curl pkg-config libssl-dev
|
|
- curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.8.1/sccache-v0.8.1-x86_64-unknown-linux-musl.tar.gz
|
|
| tar xz --strip-components=1 -C /usr/local/bin sccache-v0.8.1-x86_64-unknown-linux-musl/sccache
|
|
- chmod +x /usr/local/bin/sccache
|
|
- rustup component add clippy
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Stage 1: cargo check + clippy
|
|
# --------------------------------------------------------------------------
|
|
check:
|
|
extends: .rust-base
|
|
stage: check
|
|
script:
|
|
- cargo check --workspace
|
|
- cargo clippy --workspace -- -D warnings
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Stage 2: tests
|
|
# --------------------------------------------------------------------------
|
|
test:
|
|
extends: .rust-base
|
|
stage: test
|
|
needs: [check]
|
|
script:
|
|
- cargo test --workspace --lib
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Stage 3: Build + push images (main only, Kaniko)
|
|
# --------------------------------------------------------------------------
|
|
.kaniko-base:
|
|
stage: build
|
|
needs: [test]
|
|
image:
|
|
name: gcr.io/kaniko-project/executor:debug
|
|
entrypoint: [""]
|
|
tags:
|
|
- kapsule
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
|
before_script:
|
|
# Authenticate to GitLab container registry
|
|
- mkdir -p /kaniko/.docker
|
|
- echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf '%s:%s' "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
|
|
|
|
build-trading-service:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=trading_service
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/trading_service:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/trading_service:latest"
|
|
|
|
build-api-gateway:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=api_gateway
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/api_gateway:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/api_gateway:latest"
|
|
|
|
build-broker-gateway:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=broker_gateway_service
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/broker_gateway_service:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/broker_gateway_service:latest"
|
|
|
|
build-ml-training:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=ml_training_service
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/ml_training_service:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/ml_training_service:latest"
|
|
|
|
build-backtesting:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=backtesting_service
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/backtesting_service:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/backtesting_service:latest"
|
|
|
|
build-trading-agent:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.service"
|
|
--build-arg SERVICE=trading_agent_service
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/trading_agent_service:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/trading_agent_service:latest"
|
|
|
|
build-web-gateway:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.web-gateway"
|
|
--destination "${REGISTRY}/web-gateway:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/web-gateway:latest"
|
|
|
|
build-training:
|
|
extends: .kaniko-base
|
|
script:
|
|
- /kaniko/executor
|
|
--context "${CI_PROJECT_DIR}"
|
|
--dockerfile "${CI_PROJECT_DIR}/infra/docker/Dockerfile.training"
|
|
--build-arg SCCACHE_BUCKET=${SCCACHE_BUCKET}
|
|
--build-arg AWS_ACCESS_KEY_ID=${SCW_ACCESS_KEY}
|
|
--build-arg AWS_SECRET_ACCESS_KEY=${SCW_SECRET_KEY}
|
|
--build-arg SCCACHE_ENDPOINT=${SCCACHE_ENDPOINT}
|
|
--destination "${REGISTRY}/training:${CI_COMMIT_SHA}"
|
|
--destination "${REGISTRY}/training:latest"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Stage 4: Deploy to Kapsule (main only)
|
|
# --------------------------------------------------------------------------
|
|
deploy:
|
|
stage: deploy
|
|
image: bitnami/kubectl:latest
|
|
tags:
|
|
- kapsule
|
|
needs:
|
|
- build-trading-service
|
|
- build-api-gateway
|
|
- build-broker-gateway
|
|
- build-ml-training
|
|
- build-backtesting
|
|
- build-trading-agent
|
|
- build-web-gateway
|
|
- build-training
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
|
before_script:
|
|
- mkdir -p ~/.kube
|
|
- echo "$KUBECONFIG_DATA" | base64 -d > ~/.kube/config
|
|
- chmod 600 ~/.kube/config
|
|
script:
|
|
- |
|
|
for svc in trading-service api-gateway broker-gateway-service ml-training-service backtesting-service trading-agent-service; do
|
|
kubectl -n foxhunt set image deployment/${svc} ${svc}=${REGISTRY}/${svc}:${CI_COMMIT_SHA} || true
|
|
done
|
|
kubectl -n foxhunt set image deployment/web-gateway web-gateway=${REGISTRY}/web-gateway:${CI_COMMIT_SHA} || true
|
|
- |
|
|
for svc in trading-service api-gateway broker-gateway-service ml-training-service backtesting-service trading-agent-service web-gateway; do
|
|
kubectl -n foxhunt rollout status deployment/${svc} --timeout=300s || true
|
|
done
|
|
```
|
|
|
|
**Step 2: Configure CI/CD variables in GitLab UI**
|
|
|
|
Go to GitLab → foxhunt/foxhunt → Settings → CI/CD → Variables. Add:
|
|
|
|
| Key | Value | Type | Protected | Masked |
|
|
|-----|-------|------|-----------|--------|
|
|
| `SCW_ACCESS_KEY` | (from `~/.config/scw/config.yaml`) | Variable | Yes | Yes |
|
|
| `SCW_SECRET_KEY` | (from `~/.config/scw/config.yaml`) | Variable | Yes | Yes |
|
|
| `SCCACHE_BUCKET` | `foxhunt-artifacts` | Variable | No | No |
|
|
| `SCCACHE_ENDPOINT` | `https://s3.fr-par.scw.cloud` | Variable | No | No |
|
|
| `KUBECONFIG_DATA` | (base64 of `~/.kube/foxhunt-fr-par.yaml`) | Variable | Yes | Yes |
|
|
|
|
**Step 3: Push and test pipeline**
|
|
|
|
```bash
|
|
git add .gitlab-ci.yml
|
|
git commit -m "ci: add GitLab CI/CD pipeline (check, test, build, deploy)"
|
|
git push gitlab main
|
|
```
|
|
|
|
**Step 4: Monitor pipeline**
|
|
|
|
Go to GitLab → foxhunt/foxhunt → CI/CD → Pipelines. Verify:
|
|
- `check` job runs, passes (cargo check + clippy)
|
|
- `test` job runs, passes (cargo test --workspace --lib)
|
|
- `build-*` jobs run in parallel (8 Kaniko builds)
|
|
- `deploy` job updates K8s deployments
|
|
|
|
Watch for ci-build node autoscaling:
|
|
```bash
|
|
kubectl get nodes -w # Should see ci-build node appear, then disappear after ~10min idle
|
|
```
|
|
|
|
**Step 5: Commit is already done in Step 3.**
|
|
|
|
---
|
|
|
|
## Phase 4: Cutover
|
|
|
|
### Task 8: Update K8s manifests for GitLab registry
|
|
|
|
**Files:**
|
|
- Modify: `infra/k8s/services/api-gateway.yaml`
|
|
- Modify: `infra/k8s/services/trading-service.yaml`
|
|
- Modify: `infra/k8s/services/trading-service-gpu.yaml`
|
|
- Modify: `infra/k8s/services/ml-training-service.yaml`
|
|
- Modify: `infra/k8s/services/ml-training-service-gpu.yaml`
|
|
- Modify: `infra/k8s/services/broker-gateway.yaml`
|
|
- Modify: `infra/k8s/services/backtesting-service.yaml`
|
|
- Modify: `infra/k8s/services/trading-agent-service.yaml`
|
|
- Modify: `infra/k8s/services/web-gateway.yaml`
|
|
|
|
**Step 1: Create GitLab registry pull secret**
|
|
|
|
Create a GitLab deploy token first (GitLab UI → Settings → Repository → Deploy Tokens):
|
|
- Name: `k8s-deploy`
|
|
- Scopes: `read_registry`
|
|
|
|
Then create the K8s secret:
|
|
|
|
```bash
|
|
kubectl -n foxhunt create secret docker-registry gitlab-registry \
|
|
--docker-server=gitlab.fxhnt.ai:5050 \
|
|
--docker-username=<deploy-token-username> \
|
|
--docker-password=<deploy-token-password> \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
```
|
|
|
|
**Step 2: Update all service manifests**
|
|
|
|
In every service YAML file, make two changes:
|
|
|
|
1. Replace `imagePullSecrets`:
|
|
```yaml
|
|
# OLD
|
|
imagePullSecrets:
|
|
- name: scw-registry
|
|
|
|
# NEW
|
|
imagePullSecrets:
|
|
- name: gitlab-registry
|
|
```
|
|
|
|
2. Replace image references:
|
|
```yaml
|
|
# OLD
|
|
image: rg.fr-par.scw.cloud/foxhunt/<service_name>:latest
|
|
|
|
# NEW
|
|
image: gitlab.fxhnt.ai:5050/foxhunt/foxhunt/<service_name>:latest
|
|
```
|
|
|
|
Service name mappings (container image name → manifest):
|
|
| Manifest | Old image | New image |
|
|
|----------|-----------|-----------|
|
|
| api-gateway.yaml | `rg.fr-par.scw.cloud/foxhunt/api_gateway` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/api_gateway` |
|
|
| trading-service.yaml | `rg.fr-par.scw.cloud/foxhunt/trading_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/trading_service` |
|
|
| trading-service-gpu.yaml | `rg.fr-par.scw.cloud/foxhunt/trading_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/trading_service` |
|
|
| ml-training-service.yaml | `rg.fr-par.scw.cloud/foxhunt/ml_training_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/ml_training_service` |
|
|
| ml-training-service-gpu.yaml | `rg.fr-par.scw.cloud/foxhunt/ml_training_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/ml_training_service` |
|
|
| broker-gateway.yaml | `rg.fr-par.scw.cloud/foxhunt/broker_gateway_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/broker_gateway_service` |
|
|
| backtesting-service.yaml | `rg.fr-par.scw.cloud/foxhunt/backtesting_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/backtesting_service` |
|
|
| trading-agent-service.yaml | `rg.fr-par.scw.cloud/foxhunt/trading_agent_service` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/trading_agent_service` |
|
|
| web-gateway.yaml | `rg.fr-par.scw.cloud/foxhunt/web-gateway` | `gitlab.fxhnt.ai:5050/foxhunt/foxhunt/web-gateway` |
|
|
|
|
**Step 3: Apply updated manifests**
|
|
|
|
```bash
|
|
kubectl apply -f infra/k8s/services/
|
|
```
|
|
|
|
**Step 4: Verify all pods can pull from GitLab registry**
|
|
|
|
```bash
|
|
kubectl -n foxhunt get pods
|
|
# All pods should be Running, no ImagePullBackOff errors
|
|
```
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add infra/k8s/services/
|
|
git commit -m "infra(k8s): switch image refs from Scaleway registry to GitLab registry"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 9: Switch git remote to GitLab
|
|
|
|
**No files to create — operational step.**
|
|
|
|
**Step 1: Update origin remote**
|
|
|
|
```bash
|
|
git remote set-url origin ssh://git@gitlab.fxhnt.ai:2222/foxhunt/foxhunt.git
|
|
git remote remove gitlab # Remove the temporary mirror remote
|
|
```
|
|
|
|
**Step 2: Verify push works**
|
|
|
|
```bash
|
|
git push origin main
|
|
```
|
|
|
|
Expected: Push succeeds, GitLab shows new commit.
|
|
|
|
**Step 3: Verify full CI cycle**
|
|
|
|
Push triggers pipeline → check → test → build → deploy. Verify services update with new images.
|
|
|
|
---
|
|
|
|
## Phase 5: Decommission
|
|
|
|
### Task 10: Destroy CI runner infrastructure
|
|
|
|
**Step 1: Destroy the CI runner VM**
|
|
|
|
```bash
|
|
cd infra/live/production/ci-runner && terragrunt destroy
|
|
```
|
|
|
|
Expected: GP1-S instance and associated resources destroyed.
|
|
|
|
**Step 2: Remove ci-runner module from repo**
|
|
|
|
```bash
|
|
rm -rf infra/modules/ci-runner/
|
|
rm -rf infra/live/production/ci-runner/
|
|
```
|
|
|
|
**Step 3: Remove Gitea workflow**
|
|
|
|
```bash
|
|
rm -rf .gitea/
|
|
```
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "infra: decommission Gitea CI runner and remove Gitea Actions workflow"
|
|
git push origin main
|
|
```
|
|
|
|
---
|
|
|
|
### Task 11: Decommission Gitea VM
|
|
|
|
**Operational step — verify everything works on GitLab first.**
|
|
|
|
**Step 1: Final verification checklist**
|
|
|
|
- [ ] GitLab UI accessible at `https://gitlab.fxhnt.ai`
|
|
- [ ] Git push/pull works via SSH
|
|
- [ ] CI pipeline runs: check → test → build → deploy
|
|
- [ ] Build pods scale up on ci-build pool, then scale to zero
|
|
- [ ] Images pushed to GitLab registry
|
|
- [ ] K8s pods pull images from GitLab registry
|
|
- [ ] Deploy stage updates K8s deployments successfully
|
|
|
|
**Step 2: Decommission Gitea (once all checks pass)**
|
|
|
|
```bash
|
|
# Stop Gitea service
|
|
ssh root@vm-fxhnt-git "systemctl stop gitea"
|
|
|
|
# Remove auto-start watcher (was deployed by ci-runner terraform)
|
|
ssh root@vm-fxhnt-git "systemctl stop ci-watcher.timer && systemctl disable ci-watcher.timer"
|
|
|
|
# Delete Scaleway instance (via CLI, since it wasn't Terragrunt-managed)
|
|
scw instance server terminate 8e8f0ac7-3745-412e-9db2-f288e2938035 with-ip=true with-block=local
|
|
```
|
|
|
|
**Step 3: Remove DNS record**
|
|
|
|
```bash
|
|
scw dns record delete fxhnt.ai name=git type=A data=100.92.231.92
|
|
```
|
|
|
|
**Step 4: Optionally delete Scaleway container registry**
|
|
|
|
```bash
|
|
# Only after confirming no running pods reference old images
|
|
scw registry namespace delete e8b31a6b-0dba-442f-8b34-23be2b3a3e52
|
|
```
|
|
|
|
---
|
|
|
|
### Task 12: Update documentation and memory
|
|
|
|
**Files:**
|
|
- Modify: `CLAUDE.md` (update Git remote, CI references)
|
|
- Modify: Memory files
|
|
|
|
**Step 1: Update CLAUDE.md**
|
|
|
|
Find and replace:
|
|
- `git.fxhnt.ai` → `gitlab.fxhnt.ai` where referencing git remote
|
|
- Remove references to Gitea, act_runner, ci-runner VM
|
|
- Update CI pipeline description from Gitea Actions to GitLab CI
|
|
|
|
**Step 2: Update memory files**
|
|
|
|
Update `/home/jgrusewski/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/infrastructure.md`:
|
|
- Replace Gitea section with GitLab section
|
|
- Update CI runner section (K8s executor instead of VM)
|
|
- Update registry info (GitLab instead of Scaleway)
|
|
- Add gitlab node pool to Kapsule section
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add CLAUDE.md
|
|
git commit -m "docs: update references for GitLab CE migration"
|
|
git push origin main
|
|
```
|