Skip to content

Deploy

Deploy #162

Workflow file for this run

name: Deploy
on:
workflow_dispatch:
inputs:
extension:
description: 'Deploy with extension'
required: false
type: choice
options:
- ''
- gala
- rally
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
env:
PRODUCTION: true
# Secrets (genuine credentials)
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
EMAIL_SMTP_PASSWORD: ${{ secrets.EMAIL_SMTP_PASSWORD }}
IDP_SECRET_KEY: ${{ secrets.IDP_SECRET_KEY }}
RECAPTCHA_SECRET_KEY: ${{ secrets.RECAPTCHA_SECRET_KEY }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }}
AUTHENTIK_TOKEN: ${{ secrets.AUTHENTIK_TOKEN }}
# Variables (public / non-sensitive config)
EMAIL_SENDER_ADDRESS: ${{ vars.EMAIL_SENDER_ADDRESS }}
EMAIL_SMTP_HOST: ${{ vars.EMAIL_SMTP_HOST }}
EMAIL_SMTP_USER: ${{ vars.EMAIL_SMTP_USER }}
R2_ENDPOINT_URL: ${{ vars.R2_ENDPOINT_URL }}
R2_BUCKET: ${{ vars.R2_BUCKET }}
R2_PUBLIC_BASE_URL: ${{ vars.R2_PUBLIC_BASE_URL }}
OIDC_ENABLED: ${{ vars.OIDC_ENABLED || 'true' }}
OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }}
ENABLED_EXTENSIONS: ${{ github.event.inputs.extension }}
steps:
- name: Fix permissions before checkout
run: |
set -euo pipefail
echo "Fixing permissions on root-owned files..."
# Only run find commands if the current directory exists and has content
if [ -d . ] && [ -n "$(ls -A . 2>/dev/null)" ]; then
# Remove __pycache__ directories (they'll be regenerated)
# Use -depth to process directories bottom-up to avoid errors with subdirectories
find . -depth -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
# Fix ownership of any remaining root-owned files (files and directories in single pass)
# Use numeric IDs to ensure correct group assignment
find . -user root \( -type f -o -type d \) -exec chown $(id -u):$(id -g) {} + 2>/dev/null || true
else
echo "Workspace is empty, skipping permission fixes (will be handled after checkout)"
fi
- uses: actions/checkout@v4
with:
submodules: true
submodules-recursive: false
fetch-depth: 0
token: ${{ secrets.GH_PAT }}
# Disable workspace cleaning to preserve permission fixes from previous runs
clean: false
- name: Update extension submodules to tracked branches
run: |
git submodule sync || true
# Initialize Rally submodule (required)
git submodule update --init extensions/rally || true
git -C extensions/rally fetch origin main --prune || true
git -C extensions/rally checkout main || true
git -C extensions/rally reset --hard origin/main || true
# Initialize Gala submodule
git submodule update --init extensions/gala
git -C extensions/gala fetch origin main --prune
git -C extensions/gala checkout main
git -C extensions/gala reset --hard origin/main
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Github Packages
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get compose files
id: compose-files
run: |
if [ "${{ github.event.inputs.extension }}" = "rally" ]; then
if [ ! -f "extensions/rally/compose.override.prod.yml" ]; then
echo "Error: rally extension selected but extensions/rally/compose.override.prod.yml not found (submodule not initialized?)" >&2
exit 1
fi
echo "COMPOSE_FILE=compose.prod.yml,extensions/rally/compose.override.prod.yml" >> "$GITHUB_OUTPUT"
elif [ "${{ github.event.inputs.extension }}" = "gala" ]; then
if [ ! -f "extensions/gala/compose.override.prod.yml" ]; then
echo "Error: gala extension selected but extensions/gala/compose.override.prod.yml not found (submodule not initialized?)" >&2
exit 1
fi
echo "COMPOSE_FILE=compose.prod.yml,extensions/gala/compose.override.prod.yml" >> "$GITHUB_OUTPUT"
else
echo "COMPOSE_FILE=compose.prod.yml" >> "$GITHUB_OUTPUT"
fi
- name: Clear stale Docker build cache
run: |
echo "Clearing Docker build cache to ensure fresh builds..."
docker builder prune -af --filter "until=1h" || true
echo "Cache cleared"
- name: Build and push
uses: docker/bake-action@v5
with:
push: true
files: ${{ steps.compose-files.outputs.COMPOSE_FILE }}
set: |
*.cache-to=type=gha,scope=cached-stage,mode=max
*.cache-from=type=gha,scope=cached-stage
deploy:
needs: build
runs-on: self-hosted
env:
PRODUCTION: true
# Secrets (genuine credentials)
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
EMAIL_SMTP_PASSWORD: ${{ secrets.EMAIL_SMTP_PASSWORD }}
IDP_SECRET_KEY: ${{ secrets.IDP_SECRET_KEY }}
RECAPTCHA_SECRET_KEY: ${{ secrets.RECAPTCHA_SECRET_KEY }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
TEAM_JWT_SECRET_KEY: ${{ secrets.TEAM_JWT_SECRET_KEY }}
OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }}
AUTHENTIK_TOKEN: ${{ secrets.AUTHENTIK_TOKEN }}
# Variables (public / non-sensitive config)
EMAIL_SENDER_ADDRESS: ${{ vars.EMAIL_SENDER_ADDRESS }}
EMAIL_SMTP_HOST: ${{ vars.EMAIL_SMTP_HOST }}
EMAIL_SMTP_USER: ${{ vars.EMAIL_SMTP_USER }}
R2_ENDPOINT_URL: ${{ vars.R2_ENDPOINT_URL }}
R2_BUCKET: ${{ vars.R2_BUCKET }}
R2_PUBLIC_BASE_URL: ${{ vars.R2_PUBLIC_BASE_URL }}
OIDC_ENABLED: ${{ vars.OIDC_ENABLED || 'true' }}
OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }}
ENABLED_EXTENSIONS: ${{ github.event.inputs.extension }}
steps:
- name: Fix permissions before checkout
run: |
set -euo pipefail
echo "Fixing permissions on root-owned files..."
# Only run find commands if the current directory exists and has content
if [ -d . ] && [ -n "$(ls -A . 2>/dev/null)" ]; then
# Remove __pycache__ directories (they'll be regenerated)
# Use -depth to process directories bottom-up to avoid errors with subdirectories
find . -depth -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
# Fix ownership of any remaining root-owned files (files and directories in single pass)
# Use numeric IDs to ensure correct group assignment
find . -user root \( -type f -o -type d \) -exec chown $(id -u):$(id -g) {} + 2>/dev/null || true
else
echo "Workspace is empty, skipping permission fixes (will be handled after checkout)"
fi
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
token: ${{ secrets.GH_PAT }}
# Disable workspace cleaning to preserve .env files and permission fixes from previous runs
clean: false
- name: Update extension submodules to tracked branches
run: |
git submodule sync || true
# Initialize Rally submodule (required)
git submodule update --init extensions/rally || true
git -C extensions/rally fetch origin main --prune || true
git -C extensions/rally checkout main || true
git -C extensions/rally reset --hard origin/main || true
# Initialize Gala submodule
git submodule update --init extensions/gala
git -C extensions/gala fetch origin main --prune
git -C extensions/gala checkout main
git -C extensions/gala reset --hard origin/main
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: neiaauav
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to Github Packages
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy with docker-compose
run: |
set -euo pipefail
if [ "${{ github.event.inputs.extension }}" = "rally" ]; then
COMPOSE_ARGS="-f compose.prod.yml -f extensions/rally/compose.override.prod.yml"
elif [ "${{ github.event.inputs.extension }}" = "gala" ]; then
COMPOSE_ARGS="-f compose.prod.yml -f extensions/gala/compose.override.prod.yml"
else
COMPOSE_ARGS="-f compose.prod.yml"
fi
# Pull images sequentially to avoid OOM on low-memory systems
for service in $(docker compose $COMPOSE_ARGS config --services); do
echo "Pulling $service..."
docker compose $COMPOSE_ARGS pull "$service" || echo "Warning: Failed to pull $service, continuing..."
done
docker compose $COMPOSE_ARGS up -d --remove-orphans
- name: Manage extensions and sync nginx
run: |
# Extension management script handles:
# - Starting/stopping extension containers
# - Database schema management
# - Dev proxy config generation
# - External nginx sync (Infrastructure/nginx)
ENABLED_EXTENSIONS="${{ github.event.inputs.extension }}" ./scripts/manage-extensions.sh
- name: Cleanup Docker resources
run: |
docker container prune -f
docker image prune -af
docker builder prune -af