Refactor to git-based deployment: simplify CI/CD and supervisor
Addresses GitLab PR comments: 1. Remove hardcoded secrets from Pulumi.prod.yaml, use ESC environment 2. Simplify deployment by using git pull instead of R2 artifacts 3. Add bootstrap script for one-time supervisor setup Major changes: - **Pulumi config**: Use ESC environment (beanflows/prod) for all secrets - **Supervisor script**: Git-based deployment (git pull every 15 min) * No more artifact downloads from R2 * Runs code directly via `uv run materia` * Self-updating from master branch - **Bootstrap script**: New infra/bootstrap_supervisor.sh for initial setup * One-time script to clone repo and setup systemd service * Idempotent and simple - **CI/CD simplification**: Remove build and R2 deployment stages * Eliminated build:extract, build:transform, build:cli jobs * Eliminated deploy:r2 job * Simplified deploy:supervisor to just check bootstrap status * Reduced from 4 stages to 3 stages (Lint → Test → Deploy) - **Documentation**: Updated CLAUDE.md with new architecture * Git-based deployment flow * Bootstrap instructions * Simplified execution model Benefits: - ✅ No hardcoded secrets in config files - ✅ Simpler deployment (no artifact builds) - ✅ Easy to test locally (just git clone + uv sync) - ✅ Auto-updates every 15 minutes - ✅ Fewer CI/CD jobs (faster pipelines) - ✅ Cleaner separation of concerns Inspired by TigerBeetle's CFO supervisor pattern. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
143
.gitlab-ci.yml
143
.gitlab-ci.yml
@@ -3,7 +3,6 @@ image: python:3.13
|
||||
stages:
|
||||
- lint
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
@@ -54,83 +53,6 @@ test:sqlmesh:
|
||||
- uv sync
|
||||
- cd transform/sqlmesh_materia && uv run sqlmesh test
|
||||
|
||||
build:extract:
|
||||
stage: build
|
||||
before_script:
|
||||
- *uv_setup
|
||||
script:
|
||||
- uv sync
|
||||
- mkdir -p dist
|
||||
- uv build --package psdonline --out-dir dist/extract
|
||||
- cd dist/extract && tar -czf ../materia-extract-latest.tar.gz .
|
||||
artifacts:
|
||||
paths:
|
||||
- dist/materia-extract-latest.tar.gz
|
||||
expire_in: 1 week
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
build:transform:
|
||||
stage: build
|
||||
before_script:
|
||||
- *uv_setup
|
||||
script:
|
||||
- uv sync
|
||||
- mkdir -p dist
|
||||
- uv build --package sqlmesh_materia --out-dir dist/transform
|
||||
- cd dist/transform && tar -czf ../materia-transform-latest.tar.gz .
|
||||
artifacts:
|
||||
paths:
|
||||
- dist/materia-transform-latest.tar.gz
|
||||
expire_in: 1 week
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
build:cli:
|
||||
stage: build
|
||||
before_script:
|
||||
- *uv_setup
|
||||
script:
|
||||
- uv sync
|
||||
- mkdir -p dist
|
||||
- uv build --out-dir dist/cli
|
||||
- cd dist/cli && tar -czf ../materia-cli-latest.tar.gz .
|
||||
artifacts:
|
||||
paths:
|
||||
- dist/materia-cli-latest.tar.gz
|
||||
expire_in: 1 week
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
deploy:r2:
|
||||
stage: deploy
|
||||
image: rclone/rclone:latest
|
||||
before_script:
|
||||
- apk add --no-cache curl unzip
|
||||
- curl -fsSL https://get.pulumi.com/esc/install.sh | sh
|
||||
- export PATH="$HOME/.pulumi/bin:$PATH"
|
||||
- esc login --token ${PULUMI_ACCESS_TOKEN}
|
||||
- eval $(esc env open beanflows/prod --format shell)
|
||||
- |
|
||||
mkdir -p ~/.config/rclone
|
||||
cat > ~/.config/rclone/rclone.conf <<EOF
|
||||
[r2]
|
||||
type = s3
|
||||
provider = Cloudflare
|
||||
access_key_id = ${R2_ACCESS_KEY_ID}
|
||||
secret_access_key = ${R2_SECRET_ACCESS_KEY}
|
||||
endpoint = https://${R2_ENDPOINT}
|
||||
acl = private
|
||||
EOF
|
||||
script:
|
||||
- rclone copy dist/*.tar.gz r2:${R2_ARTIFACTS_BUCKET}/ -v
|
||||
dependencies:
|
||||
- build:extract
|
||||
- build:transform
|
||||
- build:cli
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
deploy:infra:
|
||||
stage: deploy
|
||||
image: pulumi/pulumi:latest
|
||||
@@ -152,20 +74,26 @@ deploy:supervisor:
|
||||
- export PATH="$HOME/.pulumi/bin:$PATH"
|
||||
- esc login --token ${PULUMI_ACCESS_TOKEN}
|
||||
- eval $(esc env open beanflows/prod --format shell)
|
||||
script:
|
||||
# Install Pulumi CLI to get stack outputs
|
||||
- |
|
||||
# Install pulumi CLI to get stack outputs
|
||||
apk add --no-cache pulumi-bin || {
|
||||
curl -fsSL https://get.pulumi.com/install.sh | sh
|
||||
export PATH="$HOME/.pulumi/bin:$PATH"
|
||||
}
|
||||
pulumi login --token ${PULUMI_ACCESS_TOKEN}
|
||||
|
||||
- pulumi login --token ${PULUMI_ACCESS_TOKEN}
|
||||
script:
|
||||
- |
|
||||
# Get supervisor IP from Pulumi
|
||||
cd infra
|
||||
SUPERVISOR_IP=$(pulumi stack output supervisor_ip -s prod)
|
||||
cd ..
|
||||
|
||||
# Check if supervisor exists
|
||||
if [ -z "$SUPERVISOR_IP" ] || [ "$SUPERVISOR_IP" = "null" ]; then
|
||||
echo "No supervisor instance found. Run 'pulumi up' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploying to supervisor at ${SUPERVISOR_IP}..."
|
||||
|
||||
# Setup SSH
|
||||
@@ -174,48 +102,17 @@ deploy:supervisor:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H $SUPERVISOR_IP >> ~/.ssh/known_hosts
|
||||
|
||||
# Deploy supervisor script and service
|
||||
scp infra/supervisor/supervisor.sh root@${SUPERVISOR_IP}:/opt/materia/supervisor.sh
|
||||
scp infra/supervisor/materia-supervisor.service root@${SUPERVISOR_IP}:/etc/systemd/system/materia-supervisor.service
|
||||
|
||||
# Deploy to supervisor
|
||||
ssh root@${SUPERVISOR_IP} bash <<'ENDSSH'
|
||||
set -e
|
||||
cd /opt/materia
|
||||
|
||||
# Create environment file with secrets
|
||||
cat > .env <<EOF
|
||||
R2_ENDPOINT=${R2_ENDPOINT}
|
||||
R2_ARTIFACTS_BUCKET=${R2_ARTIFACTS_BUCKET}
|
||||
PULUMI_ACCESS_TOKEN=${PULUMI_ACCESS_TOKEN}
|
||||
EOF
|
||||
|
||||
# Download and install CLI
|
||||
curl -fsSL -o materia-cli-latest.tar.gz \
|
||||
https://${R2_ENDPOINT}/${R2_ARTIFACTS_BUCKET}/materia-cli-latest.tar.gz
|
||||
|
||||
rm -rf cli && mkdir -p cli
|
||||
tar -xzf materia-cli-latest.tar.gz -C cli/
|
||||
pip3 install --break-system-packages --force-reinstall cli/*.whl
|
||||
|
||||
# Configure Pulumi ESC
|
||||
export PATH="$HOME/.pulumi/bin:$PATH"
|
||||
esc login --token ${PULUMI_ACCESS_TOKEN}
|
||||
|
||||
# Make supervisor script executable
|
||||
chmod +x /opt/materia/supervisor.sh
|
||||
|
||||
# Reload systemd and restart service
|
||||
systemctl daemon-reload
|
||||
systemctl enable materia-supervisor
|
||||
systemctl restart materia-supervisor
|
||||
|
||||
# Show status
|
||||
echo "Supervisor service status:"
|
||||
systemctl status materia-supervisor --no-pager
|
||||
ENDSSH
|
||||
# Check if supervisor is bootstrapped
|
||||
if ssh -o ConnectTimeout=10 root@${SUPERVISOR_IP} "test -d /opt/materia/repo/.git"; then
|
||||
echo "Supervisor already bootstrapped, triggering update..."
|
||||
# Just signal supervisor to pull latest - it will do so on next check cycle
|
||||
ssh root@${SUPERVISOR_IP} "systemctl is-active materia-supervisor || echo 'Service not running, may need bootstrap'"
|
||||
else
|
||||
echo "Supervisor not bootstrapped yet. Run bootstrap script:"
|
||||
echo " export PULUMI_ACCESS_TOKEN=\${PULUMI_ACCESS_TOKEN}"
|
||||
echo " ssh root@${SUPERVISOR_IP} 'bash -s' < infra/bootstrap_supervisor.sh"
|
||||
fi
|
||||
dependencies:
|
||||
- deploy:r2
|
||||
- deploy:infra
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
|
||||
Reference in New Issue
Block a user