Files
freeCodeCamp/.github/workflows/deploy.yml
2025-04-14 22:59:13 +05:30

133 lines
5.1 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
jobs:
static:
name: Set Static Data
runs-on: ubuntu-22.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-22.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 }}
hostname: gha-${{needs.static.outputs.environment}}-api-ci-${{ github.run_id }}
tags: tag:ci
version: latest
- name: Configure SSH & Check Connection
run: |
mkdir -p ~/.ssh
echo "Host *
UserKnownHostsFile=/dev/null
StrictHostKeyChecking no" > ~/.ssh/config
chmod 644 ~/.ssh/config
sleep 10
tailscale status | grep -q "$TS_MACHINE_NAME" || { echo "Error: Machine not found"; exit 1; }
sleep 1
MACHINE_IP=$(tailscale ip -4 $TS_MACHINE_NAME)
echo -e "\nLOG:Checking connection to $TS_MACHINE_NAME..."
ssh $TS_USERNAME@$MACHINE_IP "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: |
REMOTE_SCRIPT="
set -e
echo -e '\nLOG:Deploying API to \$TS_MACHINE_NAME...'
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; }
echo -e '\nLOG:Decrypting 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
echo -e '\nLOG:Adding deployment variables...'
{
echo \"DEPLOYMENT_VERSION=\${DEPLOYMENT_VERSION}\"
echo \"FCC_API_LOG_LEVEL=\${FCC_API_LOG_LEVEL}\"
} >> .env
echo -e '\nLOG:Exporting environment variables...'
while IFS='=' read -r key value; do
if [[ -n \$key && ! \$key =~ ^# ]]; then
export \"\${key}=\${value}\"
fi
done < .env
rm -f .env
echo -e '\nLOG:Validating 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; }
echo -e '\nLOG:Deploying stack...'
docker stack deploy -c stack-api.yml --prune --with-registry-auth --detach=false \${STACK_NAME}
echo -e '\nLOG:Finished deployment.'
"
MACHINE_IP=$(tailscale ip -4 $TS_MACHINE_NAME)
ssh $TS_USERNAME@$MACHINE_IP "$REMOTE_SCRIPT"