feat(CD): deploy with GitHub actions

This commit is contained in:
Mrugesh Mohapatra
2025-03-13 12:20:10 +05:30
parent a87df1adc8
commit d97740a9a1
+244
View File
@@ -0,0 +1,244 @@
name: CD -- Deploy API (Legacy) & Clients
on:
workflow_dispatch:
jobs:
setup-jobs:
name: Setup Jobs
runs-on: ubuntu-24.04
outputs:
site_tld: ${{ steps.setup.outputs.site_tld }} # Used to define URLs like api.freecodecamp.org, forum.freecodecamp.dev, etc.
environment: ${{ steps.setup.outputs.environment }} # Used to define the environment (prd, stg, etc.)
deployment_env: ${{ steps.setup.outputs.deployment_env }} # Used to define the deployment environment (production, staging, etc.)
steps:
- name: Setup
id: setup
run: |
case "${{ github.ref }}" in
"refs/heads/prod-current")
echo "site_tld=org" >> $GITHUB_OUTPUT
echo "environment=prd" >> $GITHUB_OUTPUT
echo "deployment_env=production" >> $GITHUB_OUTPUT
;;
*)
echo "site_tld=dev" >> $GITHUB_OUTPUT
echo "environment=stg" >> $GITHUB_OUTPUT
echo "deployment_env=staging" >> $GITHUB_OUTPUT
;;
esac
api:
name: Build & Deploy API (Legacy)
needs: [setup-jobs]
runs-on: ubuntu-24.04
strategy:
matrix:
node-version: [20.x]
permissions:
deployments: write
contents: read
environment:
name: ${{ needs.setup-jobs.outputs.environment }}-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.environment}}-api-legacy-ci-${{ github.run_id }}
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: |
for i in {1..3}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-api-${i}
tailscale status | grep -q "$TS_MACHINE_NAME" || { echo "Error: Machine not found"; exit 1; }
ssh $TS_USERNAME@$TS_MACHINE_NAME "uptime"
done
# - name: Deploy API
# run: |
# export GIT_SOURCE_BRANCH=prod-${{ needs.setup-jobs.outputs.environment }}
# ssh $TS_USERNAME@$TS_MACHINE_NAME /bin/bash << EOF
# set -e
# cd /home/${TS_USERNAME}/freeCodeCamp || { echo "::error::Failed to change directory"; exit 1; }
# pm2 stop all
# git status || { echo "::error::Failed to check git status"; exit 1; }
# git clean -f || { echo "::error::Failed to clean git"; exit 1; }
# git fetch --all --prune || { echo "::error::Failed to fetch git"; exit 1; }
# git checkout -f $GIT_SOURCE_BRANCH || { echo "::error::Failed to checkout branch"; exit 1; }
# git reset --hard origin/${GIT_SOURCE_BRANCH} || { echo "::error::Failed to reset git"; exit 1; }
# git status || { echo "::error::Failed to check git status"; exit 1; }
# npm i -g pnpm@9 || { echo "::error::Failed to install pnpm"; exit 1; }
# pnpm clean:packages || { echo "::error::Failed to clean packages"; exit 1; }
# pnpm clean:server || { echo "::error::Failed to clean server"; exit 1; }
# pnpm install || { echo "::error::Failed to install dependencies"; exit 1; }
# pnpm prebuild || { echo "::error::Failed prebuild"; exit 1; }
# pnpm build:curriculum || { echo "::error::Failed to build curriculum"; exit 1; }
# pnpm build:server || { echo "::error::Failed to build server"; exit 1; }
# pnpm reload:server || { echo "::error::Failed to reload server"; exit 1; }
# pm2 ls || { echo "::error::Failed to list PM2 processes"; exit 1; }
# pm2 save || { echo "::error::Failed to save PM2 processes"; exit 1; }
# EOF
client:
name: Build Client
needs: [setup-jobs, api]
runs-on: ubuntu-24.04
strategy:
matrix:
node-version: [20.x]
lang:
- english
- chinese
- espanol
include:
- lang: english
short-name: eng
- lang: chinese
short-name: chn
- lang: espanol
short-name: esp
permissions:
deployments: write
contents: read
environment:
name: ${{ needs.setup-jobs.outputs.environment }}-clients
env:
API_LOCATION: 'https://api.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}'
ALGOLIA_APIKEY: ${{ secrets.ALGOLIA_APIKEY }}
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 }}
# The below is used in ecosystem.config.js file for the API -- to be removed later
DEPLOYMENT_ENV: ${{ needs.setup-jobs.outputs.deployment_env }}
# The above is used in ecosystem.config.js file for the API -- to be removed later
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
with:
version: 9
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- name: Prepare language specific ENV
run: |
if [ "${{ matrix.lang }}" = "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 }}" >> $GITHUB_ENV
echo "NEWS_LOCATION=https://www.freecodecamp.${{ needs.setup-jobs.outputs.site_tld }}/${{ matrix.lang }}/news" >> $GITHUB_ENV
fi
echo "CLIENT_LOCALE=${{ matrix.lang }}" >> $GITHUB_ENV
echo "CURRICULUM_LOCALE=${{ matrix.lang }}" >> $GITHUB_ENV
- name: Install and Build
run: |
# pnpm install
# pnpm run build
mkdir -p client/public
touch client/public/test-${{ matrix.short-name }}.txt
# We tar them for performance reasons - uploading a lot of files is slow.
- name: Tar Files
run: tar -czf client-${{ matrix.short-name }}.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.environment}}-clients-ci-${{ github.run_id }}
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: |
for i in {0..1}; do
TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-${{ matrix.short-name }}-${i}
tailscale status | grep -q "$TS_MACHINE_NAME" || { echo "Error: Machine not found"; exit 1; }
ssh $TS_USERNAME@$TS_MACHINE_NAME "uptime"
done
# - name: Upload and Deploy
# run: |
# for i in {0..1}; do
# TS_MACHINE_NAME=${TS_MACHINE_NAME_PREFIX}-${{ matrix.short-name }}-${i}
# scp client-${{ matrix.short-name }}.tar $TS_USERNAME@$TS_MACHINE_NAME:/tmp/${{ github.run_id }}-client-${{ matrix.short-name }}.tar || { echo "::error::Failed to upload client archive"; exit 1; }
# ssh $TS_USERNAME@$TS_MACHINE_NAME /bin/bash << EOF
# set -e
# export CURRENT_DATE=$(date +%Y%m%d)
# export CLIENT_BINARIES=gha-${{needs.setup-jobs.outputs.environment}}-release-${CURRENT_DATE}-${{ github.run_id }}
# mkdir -p /home/${TS_USERNAME}/client/releases/${CLIENT_BINARIES} || { echo "::error::Failed to create client release directory"; exit 1; }
# tar -xzf /tmp/${{ github.run_id }}-client-${{ matrix.short-name }}.tar -C /home/${TS_USERNAME}/client/releases/${CLIENT_BINARIES} || { echo "::error::Failed to extract client archive"; exit 1; }
# rm /tmp/${{ github.run_id }}-client-${{ matrix.short-name }}.tar
# cd /home/${TS_USERNAME}/client || { echo "::error::Failed to change to client directory"; exit 1; }
# npm install -g serve@13 || { echo "::error::Failed to install serve"; exit 1; }
# rm -f client-start-primary.sh
# echo "serve -c ../../serve.json releases/${CLIENT_BINARIES} -p 50505" >> client-start-primary.sh || { echo "::error::Failed to create primary script"; exit 1; }
# chmod +x client-start-primary.sh || { echo "::error::Failed to make primary script executable"; exit 1; }
# pm2 delete client-primary || true
# pm2 start ./client-start-primary.sh --name client-primary || { echo "::error::Failed to start primary client"; exit 1; }
# rm -f client-start-secondary.sh
# echo "serve -c ../../serve.json releases/${CLIENT_BINARIES} -p 52525" >> client-start-secondary.sh || { echo "::error::Failed to create secondary script"; exit 1; }
# chmod +x client-start-secondary.sh || { echo "::error::Failed to make secondary script executable"; exit 1; }
# pm2 delete client-secondary || true
# pm2 start ./client-start-secondary.sh --name client-secondary || { echo "::error::Failed to start secondary client"; exit 1; }
# EOF
# done