mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-30 12:05:39 -05:00
132 lines
4.8 KiB
YAML
132 lines
4.8 KiB
YAML
name: CD -- Deploy - API
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
fcc_api_log_level:
|
|
description: 'Log level for the API'
|
|
type: choice
|
|
options:
|
|
- debug
|
|
- info
|
|
- warn
|
|
default: info
|
|
push:
|
|
branches:
|
|
- prod-*
|
|
|
|
jobs:
|
|
static:
|
|
name: Set Static Data
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
site_tld: ${{ steps.static_data.outputs.site_tld }}
|
|
environment: ${{ steps.static_data.outputs.environment }}
|
|
fcc_api_log_level: ${{ steps.static_data.outputs.fcc_api_log_level }}
|
|
steps:
|
|
- name: Set Static Data
|
|
id: static_data
|
|
run: |
|
|
if [ "${{ github.ref }}" == "refs/heads/prod-staging" ]; then
|
|
echo "site_tld=dev" >> $GITHUB_OUTPUT
|
|
echo "environment=stg" >> $GITHUB_OUTPUT
|
|
echo "fcc_api_log_level=${{ inputs.fcc_api_log_level || 'info' }}" >> $GITHUB_OUTPUT
|
|
elif [ "${{ github.ref }}" == "refs/heads/prod-current" ]; then
|
|
echo "site_tld=org" >> $GITHUB_OUTPUT
|
|
echo "environment=prd" >> $GITHUB_OUTPUT
|
|
echo "fcc_api_log_level=${{ inputs.fcc_api_log_level || 'info' }}" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "site_tld=dev" >> $GITHUB_OUTPUT
|
|
echo "environment=stg" >> $GITHUB_OUTPUT
|
|
echo "fcc_api_log_level=${{ inputs.fcc_api_log_level || 'info' }}" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
build:
|
|
name: Build & Push
|
|
needs: static
|
|
uses: ./.github/workflows/docker-docr.yml
|
|
with:
|
|
site_tld: ${{ needs.static.outputs.site_tld }}
|
|
app: api
|
|
secrets: inherit
|
|
|
|
deploy:
|
|
name: Deploy to Docker Swarm -- ${{ needs.static.outputs.environment }}
|
|
runs-on: ubuntu-24.04
|
|
needs: [static, build]
|
|
env:
|
|
TS_USERNAME: ${{ secrets.TS_USERNAME }}
|
|
TS_MACHINE_NAME: ${{ secrets.TS_MACHINE_NAME }}
|
|
permissions:
|
|
deployments: write
|
|
environment:
|
|
name: ${{ needs.static.outputs.environment }}
|
|
url: https://api.freecodecamp.${{ needs.static.outputs.site_tld }}/status/ping?version=${{ needs.build.outputs.tagname }}
|
|
|
|
steps:
|
|
- name: Setup and connect to Tailscale network
|
|
uses: tailscale/github-action@v3
|
|
with:
|
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
|
tags: tag:ci
|
|
version: latest
|
|
|
|
- name: Configure SSH
|
|
# This is a workaround to avoid the SSH warning about known hosts & strict host key checking.
|
|
# It's not a problem for us, because we're using Tailscale to connect.
|
|
run: |
|
|
mkdir -p ~/.ssh
|
|
echo "Host *
|
|
UserKnownHostsFile=/dev/null
|
|
StrictHostKeyChecking no" > ~/.ssh/config
|
|
|
|
- name: Check connection
|
|
run: |
|
|
tailscale status | grep -q "$TS_MACHINE_NAME" || { echo "Error: Machine not found"; exit 1; }
|
|
ssh $TS_USERNAME@$TS_MACHINE_NAME "uptime"
|
|
|
|
- name: Deploy with Docker Stack
|
|
env:
|
|
# These are set in the "Environment" specifc secrets
|
|
AGE_ENCRYPTED_ASC_SECRETS: ${{ secrets.AGE_ENCRYPTED_ASC_SECRETS }}
|
|
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
|
|
# These are set in the static job above
|
|
STACK_NAME: ${{ needs.static.outputs.environment }}-api
|
|
DEPLOYMENT_VERSION: ${{ needs.build.outputs.tagname }}
|
|
FCC_API_LOG_LEVEL: ${{ needs.static.outputs.fcc_api_log_level }}
|
|
run: |
|
|
ssh $TS_USERNAME@$TS_MACHINE_NAME /bin/bash << EOF
|
|
set -e
|
|
cd /home/${TS_USERNAME}/docker-swarm-config/stacks/api || { echo "Error: Failed to change directory"; exit 1; }
|
|
which age > /dev/null || { echo "Error: age not installed"; exit 1; }
|
|
|
|
# Decrypt secrets
|
|
echo "${AGE_ENCRYPTED_ASC_SECRETS}" > secrets.age.asc
|
|
echo "${AGE_SECRET_KEY}" > age.key && chmod 600 age.key
|
|
age --identity age.key --decrypt secrets.age.asc > .env
|
|
rm -f age.key secrets.age.asc
|
|
|
|
# Add deployment variables
|
|
{
|
|
echo "DEPLOYMENT_VERSION=${DEPLOYMENT_VERSION}"
|
|
echo "FCC_API_LOG_LEVEL=${FCC_API_LOG_LEVEL}"
|
|
} >> .env
|
|
|
|
# Export environment variables with proper escaping
|
|
while IFS='=' read -r key value; do
|
|
if [[ -n \$key && ! \$key =~ ^# ]]; then
|
|
export "\${key}=\${value}"
|
|
fi
|
|
done < .env
|
|
rm -f .env
|
|
|
|
# Validate environment and config
|
|
env | grep -E 'DOMAIN|DEPLOYMENT' || { echo "Error: Required environment variables not found"; exit 1; }
|
|
docker stack config -c stack-api.yml > /dev/null || { echo "Error: Invalid stack configuration"; exit 1; }
|
|
|
|
# Deploy
|
|
docker stack deploy -c stack-api.yml --prune --with-registry-auth --detach=false ${STACK_NAME}
|
|
EOF
|
|
shell: bash
|