mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
refactor(GHA): update workflow to be consistent with legacy
This commit is contained in:
+126
-56
@@ -3,7 +3,7 @@ name: CD - Deploy - API
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
fcc_api_log_level:
|
||||
api_log_lvl:
|
||||
description: 'Log level for the API'
|
||||
type: choice
|
||||
options:
|
||||
@@ -11,54 +11,74 @@ on:
|
||||
- info
|
||||
- warn
|
||||
default: info
|
||||
workflow_run:
|
||||
workflows: [CI - Node.js]
|
||||
types:
|
||||
- completed
|
||||
branches:
|
||||
- prod-*
|
||||
|
||||
jobs:
|
||||
static:
|
||||
name: Set Static Data
|
||||
setup-jobs:
|
||||
name: Setup Jobs
|
||||
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 }}
|
||||
site_tld: ${{ steps.setup.outputs.site_tld }}
|
||||
tgt_env_short: ${{ steps.setup.outputs.tgt_env_short }}
|
||||
tgt_env_long: ${{ steps.setup.outputs.tgt_env_long }}
|
||||
api_log_lvl: ${{ steps.setup.outputs.api_log_lvl }}
|
||||
steps:
|
||||
- name: Set Static Data
|
||||
id: static_data
|
||||
- name: Setup
|
||||
id: setup
|
||||
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
|
||||
if [[ "${{ github.event_name }}" == "workflow_run" && "${{ github.event.workflow_run.conclusion }}" != "success" ]]; then
|
||||
echo "Node.js tests failed in the triggering workflow run. Check logs in its workflow run. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${{ github.event_name }}" == "workflow_run" ]]; then
|
||||
BRANCH="${{ github.event.workflow_run.head_branch }}"
|
||||
else
|
||||
BRANCH="${{ github.ref_name }}"
|
||||
fi
|
||||
|
||||
echo "Current branch: $BRANCH"
|
||||
|
||||
case "$BRANCH" in
|
||||
"prod-current")
|
||||
echo "site_tld=org" >> $GITHUB_OUTPUT
|
||||
echo "tgt_env_short=prd" >> $GITHUB_OUTPUT
|
||||
echo "tgt_env_long=production" >> $GITHUB_OUTPUT
|
||||
echo "api_log_lvl=${{ inputs.api_log_lvl || 'info' }}" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
*)
|
||||
echo "site_tld=dev" >> $GITHUB_OUTPUT
|
||||
echo "tgt_env_short=stg" >> $GITHUB_OUTPUT
|
||||
echo "tgt_env_long=staging" >> $GITHUB_OUTPUT
|
||||
echo "api_log_lvl=${{ inputs.api_log_lvl || 'info' }}" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
|
||||
build:
|
||||
name: Build & Push
|
||||
needs: static
|
||||
needs: setup-jobs
|
||||
uses: ./.github/workflows/docker-docr.yml
|
||||
with:
|
||||
site_tld: ${{ needs.static.outputs.site_tld }}
|
||||
site_tld: ${{ needs.setup-jobs.outputs.site_tld }}
|
||||
app: api
|
||||
secrets: inherit
|
||||
|
||||
deploy:
|
||||
name: Deploy to Docker Swarm -- ${{ needs.static.outputs.environment }}
|
||||
name: Deploy to Docker Swarm -- ${{ needs.setup-jobs.outputs.tgt_env_short }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [static, build]
|
||||
needs: [setup-jobs, 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 }}
|
||||
name: ${{ needs.setup-jobs.outputs.tgt_env_short }}-api
|
||||
|
||||
steps:
|
||||
- name: Setup and connect to Tailscale network
|
||||
@@ -66,7 +86,7 @@ jobs:
|
||||
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 }}
|
||||
hostname: gha-${{needs.setup-jobs.outputs.tgt_env_short}}-api-ci-${{ github.run_id }}
|
||||
tags: tag:ci
|
||||
version: latest
|
||||
|
||||
@@ -86,47 +106,97 @@ jobs:
|
||||
|
||||
- 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 "Environment" specifc secrets
|
||||
# DOCKER_REGISTRY
|
||||
# MONGOHQ_URL
|
||||
# SENTRY_DSN
|
||||
# SENTRY_ENVIRONMENT
|
||||
# AUTH0_CLIENT_ID
|
||||
# AUTH0_CLIENT_SECRET
|
||||
# AUTH0_DOMAIN
|
||||
# JWT_SECRET
|
||||
# COOKIE_SECRET
|
||||
# COOKIE_DOMAIN
|
||||
# SES_ID
|
||||
# SES_SECRET
|
||||
# GROWTHBOOK_FASTIFY_API_HOST
|
||||
# GROWTHBOOK_FASTIFY_CLIENT_KEY
|
||||
# HOME_LOCATION
|
||||
# API_LOCATION
|
||||
# STRIPE_SECRET_KEY
|
||||
# These are set in the static job above
|
||||
STACK_NAME: ${{ needs.static.outputs.environment }}-api
|
||||
STACK_NAME: ${{ needs.setup-jobs.outputs.tgt_env_short }}-api
|
||||
DEPLOYMENT_VERSION: ${{ needs.build.outputs.tagname }}
|
||||
FCC_API_LOG_LEVEL: ${{ needs.static.outputs.fcc_api_log_level }}
|
||||
DEPLOYMENT_ENV: ${{ needs.setup-jobs.outputs.site_tld }}
|
||||
FCC_API_LOG_LEVEL: ${{ needs.setup-jobs.outputs.api_log_lvl }}
|
||||
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; }
|
||||
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: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:Cleaning up .env file...'
|
||||
touch .env.tmp
|
||||
while IFS= read -r line; do
|
||||
if [[ \$line =~ ^[A-Za-z0-9_]+=.*$ ]]; then
|
||||
# Extract the key (part before the first =)
|
||||
key=\${line%%=*}
|
||||
# Remove any previous line with this key
|
||||
sed -i \"/^\${key}=/d\" .env.tmp
|
||||
fi
|
||||
# Append the current line
|
||||
echo \"\$line\" >> .env.tmp
|
||||
done < .env
|
||||
mv .env.tmp .env
|
||||
|
||||
echo -e '\nLOG:Exporting environment variables...'
|
||||
while IFS='=' read -r key value; do
|
||||
if [[ -n \$key && ! \$key =~ ^# ]]; then
|
||||
export \"\${key}=\${value}\"
|
||||
echo -e '\nLOG:Adding deployment variables...'
|
||||
{
|
||||
echo \"DEPLOYMENT_VERSION=$DEPLOYMENT_VERSION\"
|
||||
echo \"DEPLOYMENT_ENV=$DEPLOYMENT_ENV\"
|
||||
echo \"FCC_API_LOG_LEVEL=$FCC_API_LOG_LEVEL\"
|
||||
} >> .env
|
||||
|
||||
echo -e '\nLOG:Sourcing environment...'
|
||||
while IFS='=' read -r key value; do
|
||||
if [[ -n \"\$key\" && ! \"\$key\" =~ ^# ]]; then
|
||||
export \"\${key}=\${value}\"
|
||||
fi
|
||||
done < .env
|
||||
rm -rf .env
|
||||
|
||||
echo -e '\nLOG:Validating environment...'
|
||||
if [[ -z \"\$DOCKER_REGISTRY\" || -z \"\$DEPLOYMENT_ENV\" || -z \"\$DEPLOYMENT_VERSION\" || -z \"\$MONGOHQ_URL\" || -z \"\$FCC_API_LOG_LEVEL\" ]]; then
|
||||
echo \"Error: Missing required environment variables\"
|
||||
exit 1
|
||||
fi
|
||||
done < .env
|
||||
rm -f .env
|
||||
if [[ \"\$DEPLOYMENT_VERSION\" != \"$DEPLOYMENT_VERSION\" ]]; then
|
||||
echo \"Error: Version mismatch. Expected: $DEPLOYMENT_VERSION, Got: \$DEPLOYMENT_VERSION\"
|
||||
exit 1
|
||||
fi
|
||||
env | grep -E 'DOMAIN|DEPLOYMENT' || { echo \"Error: Required environment variables not found\"; exit 1; }
|
||||
|
||||
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:Checking stack configuration...'
|
||||
CONFIG_OUTPUT="/dev/null"
|
||||
if [[ \"\$FCC_API_LOG_LEVEL\" == \"debug\" ]]; then
|
||||
CONFIG_FILENAME="debug-docker-stack-config-\${DEPLOYMENT_VERSION}.yml"
|
||||
echo -e '\nLOG:Saving stack configuration to $CONFIG_FILENAME for debugging...'
|
||||
CONFIG_OUTPUT="\$CONFIG_FILENAME"
|
||||
fi
|
||||
docker stack config -c stack-api.yml > "$CONFIG_OUTPUT" || { 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.'
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user