deploy via Gitea CI registry; provision GCP infra with Terraform
- Terraform: e2-micro VM (us-east1-b, free tier), static IP, SSH/web
firewall rules, IAM bindings for Firestore + GCS; imports existing
drb-calls bucket and c2-server Firestore database into state
- Gitea CI: build c2-core, discord-bot, frontend images and push to
git.vpn.cusano.net registry; SSH deploy pulls pre-built images (no
build on VM)
- Ansible: first-time setup only — git clone, env files from vault,
Caddyfile, docker login + compose pull + up; no rsync or on-VM builds
- docker-compose: add image: ${REGISTRY}/name:latest alongside build:
so local dev and CI registry both work
- gitignore: add Terraform state, lock, tfvars, ansible secrets
This commit is contained in:
+58
-15
@@ -1,16 +1,61 @@
|
||||
name: Deploy
|
||||
name: Build & Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
SERVER_IP: ${{ secrets.SERVER_IP }}
|
||||
SSH_USER: drb
|
||||
# REGISTRY secret = "git.vpn.cusano.net/logan" (full image prefix)
|
||||
REGISTRY: ${{ secrets.REGISTRY }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build & push images
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Gitea registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.vpn.cusano.net
|
||||
username: ${{ secrets.REGISTRY_USER }}
|
||||
password: ${{ secrets.BUILD_TOKEN }}
|
||||
|
||||
- name: Build & push c2-core
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./drb-c2-core
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/c2-core:latest
|
||||
${{ env.REGISTRY }}/c2-core:${{ gitea.sha }}
|
||||
|
||||
- name: Build & push discord-bot
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./drb-server-discord-bot
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/discord-bot:latest
|
||||
${{ env.REGISTRY }}/discord-bot:${{ gitea.sha }}
|
||||
|
||||
- name: Build & push frontend
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./drb-frontend
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/frontend:latest
|
||||
${{ env.REGISTRY }}/frontend:${{ gitea.sha }}
|
||||
|
||||
deploy:
|
||||
name: Deploy to VM
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -21,26 +66,24 @@ jobs:
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key \
|
||||
${{ env.SSH_USER }}@${{ env.SERVER_IP }} << 'ENDSSH'
|
||||
ssh -o StrictHostKeyChecking=no \
|
||||
-o HostKeyAlgorithms=ssh-ed25519,rsa-sha2-256,rsa-sha2-512 \
|
||||
-i /tmp/deploy_key \
|
||||
drb@${{ secrets.SERVER_IP }} << 'ENDSSH'
|
||||
set -e
|
||||
cd /opt/drb
|
||||
|
||||
# Pull latest code
|
||||
# Update compose files + mosquitto config
|
||||
git pull origin main
|
||||
|
||||
# Rebuild and restart changed services
|
||||
docker compose up -d --build --remove-orphans
|
||||
|
||||
# Reload Caddy if Caddyfile changed
|
||||
sudo systemctl reload caddy
|
||||
|
||||
# Clean up old images
|
||||
# Pull pre-built images and restart (no build on the VM)
|
||||
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --remove-orphans
|
||||
docker image prune -f
|
||||
ENDSSH
|
||||
|
||||
- name: Verify health
|
||||
- name: Health check
|
||||
run: |
|
||||
sleep 15
|
||||
sleep 20
|
||||
curl -f https://api.${{ secrets.DRB_DOMAIN }}/health || \
|
||||
(echo "Health check failed" && exit 1)
|
||||
|
||||
+3
-2
@@ -17,17 +17,17 @@ services:
|
||||
- mosquitto_data:/mosquitto/data
|
||||
|
||||
c2-core:
|
||||
image: ${REGISTRY}/c2-core:${TAG:-latest}
|
||||
build: ./drb-c2-core
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8888:8000"
|
||||
env_file: ./drb-c2-core/.env
|
||||
volumes:
|
||||
- ./drb-c2-core/gcp-key.json:/app/gcp-key.json:ro
|
||||
depends_on:
|
||||
- mosquitto
|
||||
|
||||
discord-bot:
|
||||
image: ${REGISTRY}/discord-bot:${TAG:-latest}
|
||||
build: ./drb-server-discord-bot
|
||||
restart: unless-stopped
|
||||
env_file: ./drb-server-discord-bot/.env
|
||||
@@ -35,6 +35,7 @@ services:
|
||||
- c2-core
|
||||
|
||||
frontend:
|
||||
image: ${REGISTRY}/frontend:${TAG:-latest}
|
||||
build: ./drb-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
---
|
||||
# Sync code, write secrets, bring the stack up.
|
||||
# First-time setup: clone repo, write secrets, pull pre-built images and start stack.
|
||||
# Images are built and pushed by Gitea CI — this role never builds on the VM.
|
||||
|
||||
- name: Sync app code from local machine
|
||||
synchronize:
|
||||
src: "{{ local_repo_path }}"
|
||||
dest: "{{ app_dir }}/"
|
||||
delete: true
|
||||
recursive: true
|
||||
rsync_opts:
|
||||
- "--exclude=.git"
|
||||
- "--exclude=**/__pycache__"
|
||||
- "--exclude=**/.env"
|
||||
- "--exclude=**/gcp-key.json"
|
||||
- "--exclude=**/node_modules"
|
||||
- "--exclude=drb-c2-core/gcp-key.json"
|
||||
- "--exclude=infra/"
|
||||
become: false # rsync runs as the SSH user, not root
|
||||
- name: Clone repo (skipped if already present)
|
||||
git:
|
||||
repo: "{{ repo_url }}"
|
||||
dest: "{{ app_dir }}"
|
||||
version: main
|
||||
update: false
|
||||
become: false
|
||||
|
||||
- name: Set ownership of app directory
|
||||
file:
|
||||
@@ -25,10 +18,7 @@
|
||||
group: "{{ ssh_user }}"
|
||||
recurse: true
|
||||
|
||||
# No gcp-key.json needed — the VM authenticates to GCS/Firestore via ADC
|
||||
# (GCE metadata server). IAM roles are granted by Terraform.
|
||||
|
||||
- name: Template top-level .env (docker-compose MQTT creds)
|
||||
- name: Template top-level .env (docker-compose MQTT creds + registry)
|
||||
template:
|
||||
src: root.env.j2
|
||||
dest: "{{ app_dir }}/.env"
|
||||
@@ -69,18 +59,19 @@
|
||||
mode: "0644"
|
||||
notify: Reload Caddy
|
||||
|
||||
- name: Bring the stack up (builds images if changed)
|
||||
- name: Log in to container registry
|
||||
command: >
|
||||
docker login {{ vault_registry_host }}
|
||||
-u {{ vault_registry_user }}
|
||||
-p {{ vault_registry_token }}
|
||||
no_log: true
|
||||
|
||||
- name: Pull pre-built images and start stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_dir }}"
|
||||
build: always
|
||||
files:
|
||||
- docker-compose.yml
|
||||
- docker-compose.prod.yml
|
||||
pull: always
|
||||
build: never
|
||||
state: present
|
||||
pull: never
|
||||
become: true
|
||||
environment:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
|
||||
- name: Prune unused Docker images
|
||||
community.docker.docker_prune:
|
||||
images: true
|
||||
images_filters:
|
||||
dangling: true
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# Top-level docker-compose environment — MQTT credentials for the broker container.
|
||||
# Top-level docker-compose environment — MQTT credentials and registry prefix.
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
|
||||
MQTT_C2_USER={{ vault_mqtt_c2_user }}
|
||||
MQTT_C2_PASS={{ vault_mqtt_c2_pass }}
|
||||
MQTT_NODE_USER={{ vault_mqtt_node_user }}
|
||||
MQTT_NODE_PASS={{ vault_mqtt_node_pass }}
|
||||
|
||||
# Container registry prefix — docker compose uses this for image: ${REGISTRY}/name:latest
|
||||
REGISTRY={{ vault_registry }}
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
- vault.yml
|
||||
|
||||
pre_tasks:
|
||||
- name: Install rsync
|
||||
apt:
|
||||
name: rsync
|
||||
state: present
|
||||
update_cache: false
|
||||
|
||||
- name: Wait for Docker (startup.sh runs async on first boot)
|
||||
command: docker info
|
||||
register: _docker
|
||||
|
||||
@@ -19,6 +19,12 @@ vault_gemini_api_key: ""
|
||||
vault_gcs_bucket: "your-gcs-bucket-name"
|
||||
vault_firestore_database: "c2-server"
|
||||
|
||||
# ── Gitea Container Registry ──────────────────────────────────────────────────
|
||||
vault_registry_host: "git.vpn.cusano.net"
|
||||
vault_registry_user: "logan"
|
||||
vault_registry_token: "" # Gitea access token with package:write scope
|
||||
vault_registry: "git.vpn.cusano.net/logan" # full image prefix
|
||||
|
||||
# ── Discord Bot ───────────────────────────────────────────────────────────────
|
||||
vault_discord_token: ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user