Files
freeCodeCamp/.github/workflows/deploy-legacy.yml
2025-03-16 06:24:32 +05:30

294 lines
12 KiB
YAML

name: CD -- Deploy API (Legacy) & Clients
on:
workflow_dispatch:
jobs:
setup-jobs:
name: Setup Jobs
runs-on: ubuntu-22.04
outputs:
site_tld: ${{ steps.setup.outputs.site_tld }} # org, dev
tgt_env_short: ${{ steps.setup.outputs.tgt_env_short }} # prd, stg
tgt_env_long: ${{ steps.setup.outputs.tgt_env_long }} # production, staging
steps:
- name: Setup
id: setup
run: |
case "${{ github.ref }}" in
"refs/heads/prod-current")
echo "site_tld=org" >> $GITHUB_OUTPUT
echo "tgt_env_short=prd" >> $GITHUB_OUTPUT
echo "tgt_env_long=production" >> $GITHUB_OUTPUT
;;
*)
echo "site_tld=dev" >> $GITHUB_OUTPUT
echo "tgt_env_short=stg" >> $GITHUB_OUTPUT
echo "tgt_env_long=staging" >> $GITHUB_OUTPUT
;;
esac
api:
name: API (Legacy) - [${{ needs.setup-jobs.outputs.tgt_env_short }}]
needs: [setup-jobs]
runs-on: ubuntu-22.04
permissions:
deployments: write
contents: read
environment:
name: ${{ needs.setup-jobs.outputs.tgt_env_short }}-api-legacy
env:
TS_USERNAME: ${{ secrets.TS_USERNAME }}
TS_MACHINE_NAME_PREFIX: ${{ secrets.TS_MACHINE_NAME_PREFIX }}
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.setup-jobs.outputs.tgt_env_short}}-api-legacy-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
if [ $? -ne 0 ]; then echo "::error::Failed to configure SSH"; exit 1; fi
for i in {1..3}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-api-${i}
tailscale status | grep -q "$TS_MACHINE_NAME" && \
ssh $TS_USERNAME@$TS_MACHINE_NAME "uptime"
if [ $? -ne 0 ]; then echo "::error::Failed to check connection"; exit 1; fi
done
- name: Deploy API
run: |
export GIT_SOURCE_BRANCH=prod-${{ needs.setup-jobs.outputs.tgt_env_long }}
for i in {1..3}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-api-${i}
ssh $TS_USERNAME@$TS_MACHINE_NAME /bin/bash << EOF
set -e
echo "Deploying API (Legacy) to $TS_MACHINE_NAME"
cd /home/$TS_USERNAME/freeCodeCamp
if [ $? -ne 0 ]; then echo "::error::Failed to change to working directory"; exit 1; fi
# Environment setup
export NVM_DIR=\$HOME/.nvm && [ -s "\$NVM_DIR/nvm.sh" ] && source "\$NVM_DIR/nvm.sh" && \
nvm ls | grep 'default' && \
echo "Node.js version:" && node --version
if [ $? -ne 0 ]; then echo "::error::Failed during environment setup"; exit 1; fi
# Stop all PM2 services
pm2 stop all
if [ $? -ne 0 ]; then echo "::error::Failed to stop PM2 services"; exit 1; fi
# Git operations
git status && \
git clean -f && \
git fetch --all --prune && \
git checkout -f $GIT_SOURCE_BRANCH && \
git reset --hard origin/$GIT_SOURCE_BRANCH && \
git status
if [ $? -ne 0 ]; then echo "::error::Failed during git operations"; exit 1; fi
# Build
npm i -g pnpm@9 && \
pnpm clean:packages && \
pnpm clean:server && \
pnpm install && \
pnpm prebuild && \
pnpm build:curriculum && \
pnpm build:server
if [ $? -ne 0 ]; then echo "::error::Failed during build process"; exit 1; fi
# Server reload
pnpm reload:server && \
pm2 ls && \
pm2 save
if [ $? -ne 0 ]; then echo "::error::Failed during server reload"; exit 1; fi
EOF
done
client:
name: Clients - [${{ needs.setup-jobs.outputs.tgt_env_short }}] [${{ matrix.lang-name-short }}]
needs: [setup-jobs, api]
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [20]
lang-name-full:
- english
- chinese
- espanol
- chinese-traditional
- italian
- portuguese
- ukrainian
- japanese
- german
- swahili
include:
- lang-name-full: english
lang-name-short: eng
- lang-name-full: chinese
lang-name-short: chn
- lang-name-full: espanol
lang-name-short: esp
- lang-name-full: chinese-traditional
lang-name-short: cnt
- lang-name-full: italian
lang-name-short: ita
- lang-name-full: portuguese
lang-name-short: por
- lang-name-full: ukrainian
lang-name-short: ukr
- lang-name-full: japanese
lang-name-short: jpn
- lang-name-full: german
lang-name-short: ger
- lang-name-full: swahili
lang-name-short: swa
permissions:
deployments: write
contents: read
environment:
name: ${{ needs.setup-jobs.outputs.tgt_env_short }}-clients
env:
TS_USERNAME: ${{ secrets.TS_USERNAME }}
TS_MACHINE_NAME_PREFIX: ${{ secrets.TS_MACHINE_NAME_PREFIX }}
steps:
- name: Checkout Source Files
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'recursive'
- name: Setup pnpm
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d #v3.0.0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- name: Language specific ENV - [${{ matrix.lang-name-full }}]
run: |
if [ "${{ matrix.lang-name-full }}" = "english" ]; then
echo "HOME_LOCATION=https://www.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}" >> $GITHUB_ENV
echo "NEWS_LOCATION=https://www.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}/news" >> $GITHUB_ENV
else
echo "HOME_LOCATION=https://www.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}/${{ matrix.lang-name-full }}" >> $GITHUB_ENV
echo "NEWS_LOCATION=https://www.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}/${{ matrix.lang-name-full }}/news" >> $GITHUB_ENV
fi
echo "CLIENT_LOCALE=${{ matrix.lang-name-full }}" >> $GITHUB_ENV
echo "CURRICULUM_LOCALE=${{ matrix.lang-name-full }}" >> $GITHUB_ENV
- name: Install and Build
env:
API_LOCATION: 'https://api.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}'
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
GROWTHBOOK_URI: ${{ secrets.GROWTHBOOK_URI }}
FORUM_LOCATION: 'https://forum.freecodecamp.org'
PATREON_CLIENT_ID: ${{ secrets.PATREON_CLIENT_ID }}
PAYPAL_CLIENT_ID: ${{ secrets.PAYPAL_CLIENT_ID }}
STRIPE_PUBLIC_KEY: ${{ secrets.STRIPE_PUBLIC_KEY }}
SHOW_UPCOMING_CHANGES: ${{ vars.SHOW_UPCOMING_CHANGES }}
FREECODECAMP_NODE_ENV: production
# The below is used in ecosystem.config.js file for the API -- to be removed later
DEPLOYMENT_ENV: ${{ needs.setup-jobs.outputs.tgt_env_long }}
# The above is used in ecosystem.config.js file for the API -- to be removed later
run: |
pnpm install
pnpm run build
- name: Tar Files
run: tar -czf client-${{ matrix.lang-name-short }}.tar client/public
- 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.setup-jobs.outputs.tgt_env_short}}-clients-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
if [ $? -ne 0 ]; then echo "::error::Failed to configure SSH"; exit 1; fi
for i in {0..1}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-${{ matrix.lang-name-short }}-${i}
tailscale status | grep -q "$TS_MACHINE_NAME" && \
ssh $TS_USERNAME@$TS_MACHINE_NAME "uptime"
if [ $? -ne 0 ]; then echo "::error::Failed to check connection"; exit 1; fi
done
- name: Upload and Deploy
run: |
for i in {0..1}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-${{ matrix.lang-name-short }}-${i}
CURRENT_DATE=$(date +%Y%m%d)
CLIENT_SRC=client-${{ matrix.lang-name-short }}.tar
CLIENT_DST=/tmp/client-${{ matrix.lang-name-short }}-${CURRENT_DATE}-${{ github.run_id }}.tar
CLIENT_BINARIES=${{needs.setup-jobs.outputs.tgt_env_short}}-release-$CURRENT_DATE-${{ github.run_id }}
# Upload client archive
scp $CLIENT_SRC $TS_USERNAME@$TS_MACHINE_NAME:$CLIENT_DST
if [ $? -ne 0 ]; then echo "::error::Failed to upload client archive"; exit 1; fi
ssh $TS_USERNAME@$TS_MACHINE_NAME /bin/bash << EOF
set -e
# Extract client archive
mkdir -p /home/$TS_USERNAME/client/releases/$CLIENT_BINARIES && \
tar -xzf $CLIENT_DST -C /home/$TS_USERNAME/client/releases/$CLIENT_BINARIES --strip-components=2 && \
rm $CLIENT_DST && \
du -sh /home/$TS_USERNAME/client/releases/$CLIENT_BINARIES
if [ $? -ne 0 ]; then echo "::error::Failed to extract client archive"; exit 1; fi
cd /home/$TS_USERNAME/client
if [ $? -ne 0 ]; then echo "::error::Failed to change to working directory"; exit 1; fi
# Environment setup
export NVM_DIR=\$HOME/.nvm && [ -s "\$NVM_DIR/nvm.sh" ] && source "\$NVM_DIR/nvm.sh" && \
nvm ls | grep 'default' && \
echo "Node.js version:" && node --version
if [ $? -ne 0 ]; then echo "::error::Failed during environment setup"; exit 1; fi
npm install -g serve@13
if [ $? -ne 0 ]; then echo "::error::Failed to install serve"; exit 1; fi
# Primary client setup
rm -f client-start-primary.sh && \
echo "serve -c ../../serve.json releases/$CLIENT_BINARIES -p 50505" >> client-start-primary.sh && \
chmod +x client-start-primary.sh && \
pm2 delete client-primary || true && \
pm2 start ./client-start-primary.sh --name client-primary
if [ $? -ne 0 ]; then echo "::error::Failed to setup primary client"; exit 1; fi
# Secondary client setup
rm -f client-start-secondary.sh && \
echo "serve -c ../../serve.json releases/$CLIENT_BINARIES -p 52525" >> client-start-secondary.sh && \
chmod +x client-start-secondary.sh && \
pm2 delete client-secondary || true && \
pm2 start ./client-start-secondary.sh --name client-secondary
if [ $? -ne 0 ]; then echo "::error::Failed to setup secondary client"; exit 1; fi
EOF
done