diff --git a/.github/workflows/deploy-legacy.yml b/.github/workflows/deploy-legacy.yml new file mode 100644 index 00000000000..01c3cf002e2 --- /dev/null +++ b/.github/workflows/deploy-legacy.yml @@ -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