mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-13 04:00:12 -04:00
294 lines
12 KiB
YAML
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
|