Build and Deploy Site #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Deploy Site | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Environment to deploy to' | |
| required: true | |
| type: choice | |
| options: | |
| - dev | |
| - prod | |
| target: | |
| description: 'What to deploy' | |
| required: true | |
| type: choice | |
| options: | |
| - both | |
| - web | |
| - api | |
| default: both | |
| env: | |
| NODE_VERSION: '20' | |
| jobs: | |
| deploy-web: | |
| name: Build and Deploy Web (${{ inputs.environment }}) | |
| if: ${{ inputs.target == 'both' || inputs.target == 'web' }} | |
| runs-on: ubuntu-latest | |
| environment: ${{ inputs.environment }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| lfs: true | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Create web build .env file | |
| uses: SpicyPizza/create-envfile@v2 | |
| with: | |
| envkey_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| envkey_API_URL: ${{ secrets.API_URL }} | |
| envkey_VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }} | |
| envkey_VITE_SENTRY_ORG: ${{ vars.VITE_SENTRY_ORG }} | |
| envkey_VITE_SENTRY_PROJECT: ${{ vars.VITE_SENTRY_PROJECT }} | |
| envkey_VITE_API_URL: ${{ vars.VITE_API_URL }} | |
| directory: 'apps/web/' | |
| file_name: '.env' | |
| fail_on_empty: false | |
| sort_keys: false | |
| - name: Build web application | |
| run: pnpm build --filter=@bt/web | |
| - name: Create web server .env file | |
| uses: SpicyPizza/create-envfile@v2 | |
| with: | |
| envkey_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| envkey_API_URL: ${{ secrets.API_URL }} | |
| directory: 'apps/web/.output/server' | |
| file_name: '.env' | |
| fail_on_empty: false | |
| sort_keys: false | |
| - name: Remove .htaccess if configured | |
| if: ${{ vars.REMOVE_HTACCESS == 'true' }} | |
| run: rm -f apps/web/.output/public/.htaccess | |
| - name: Zip web artifact | |
| working-directory: apps/web/.output | |
| run: zip -r ../../../bedrock_tweaks_site_${{ github.run_id }}_${{ github.run_attempt }}.zip . | |
| - name: Upload web artifact to server | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| source: 'bedrock_tweaks_site_${{ github.run_id }}_${{ github.run_attempt }}.zip' | |
| target: '/tmp' | |
| - name: Deploy web on server | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| script: | | |
| set -euo pipefail | |
| DEPLOY_DIR="${{ vars.WEB_DEPLOY_DIR }}" | |
| # Safety guard: ensure DEPLOY_DIR is not empty and is under /var/www | |
| if [ -z "$DEPLOY_DIR" ] || [ "${DEPLOY_DIR#/var/www}" = "$DEPLOY_DIR" ]; then | |
| echo "Refusing to deploy web to unsafe directory: '$DEPLOY_DIR'" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "$DEPLOY_DIR" | |
| rm -rf "$DEPLOY_DIR"/* | |
| unzip -o /tmp/bedrock_tweaks_site_${{ github.run_id }}_${{ github.run_attempt }}.zip -d "$DEPLOY_DIR" | |
| rm -f /tmp/bedrock_tweaks_site_${{ github.run_id }}_${{ github.run_attempt }}.zip | |
| - name: Restart web service | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| script: sudo systemctl restart ${{ vars.WEB_SERVICE_NAME }} | |
| - name: Output job status | |
| id: job-status | |
| if: always() | |
| run: echo "status=${{ job.status }}" >> $GITHUB_OUTPUT | |
| deploy-api: | |
| name: Build and Deploy API (${{ inputs.environment }}) | |
| if: ${{ inputs.target == 'both' || inputs.target == 'api' }} | |
| runs-on: ubuntu-latest | |
| environment: ${{ inputs.environment }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| lfs: true | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build API | |
| run: pnpm build --filter=@bt/api | |
| - name: Zip API artifact | |
| working-directory: apps/api | |
| run: zip -r ../../bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.zip .output assets | |
| - name: Upload API artifact to server | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| source: 'bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.zip' | |
| target: '/tmp' | |
| - name: Create API .env file | |
| uses: SpicyPizza/create-envfile@v2 | |
| with: | |
| envkey_NODE_PORT: ${{ vars.API_NODE_PORT }} | |
| envkey_PRODUCTION: ${{ vars.API_PRODUCTION }} | |
| envkey_STORAGE_URL: ${{ vars.API_STORAGE_URL }} | |
| envkey_METADATA_AUTHORS: ${{ vars.API_METADATA_AUTHORS }} | |
| envkey_CACHE_DIR: ${{ vars.API_CACHE_DIR }} | |
| envkey_DOWNLOADS_DIR: ${{ vars.API_DOWNLOADS_DIR }} | |
| envkey_CACHE_MAX_BYTES: ${{ vars.API_CACHE_MAX_BYTES }} | |
| envkey_DOWNLOADS_MAX_BYTES: ${{ vars.API_DOWNLOADS_MAX_BYTES }} | |
| directory: '.' | |
| file_name: 'bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.env' | |
| fail_on_empty: false | |
| sort_keys: false | |
| - name: Upload API .env to server | |
| uses: appleboy/scp-action@v0.1.7 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| source: 'bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.env' | |
| target: '/tmp' | |
| - name: Deploy API on server | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| script: | | |
| set -euo pipefail | |
| DEPLOY_DIR="${{ vars.API_DEPLOY_DIR }}" | |
| CACHE_DIR="${{ vars.API_CACHE_DIR }}" | |
| DOWNLOADS_DIR="${{ vars.API_DOWNLOADS_DIR }}" | |
| # Safety guard: ensure DEPLOY_DIR is not empty and is under /opt/bedrock_tweaks | |
| if [ -z "$DEPLOY_DIR" ] || [ "${DEPLOY_DIR#/opt/bedrock_tweaks}" = "$DEPLOY_DIR" ]; then | |
| echo "Refusing to deploy API to unsafe directory: '$DEPLOY_DIR'" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "$DEPLOY_DIR" | |
| rm -rf "$DEPLOY_DIR"/* | |
| unzip -o /tmp/bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.zip -d "$DEPLOY_DIR" | |
| rm -f /tmp/bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.zip | |
| # Place runtime .env | |
| cp /tmp/bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.env "$DEPLOY_DIR/.env" | |
| rm -f /tmp/bedrock_tweaks_api_${{ github.run_id }}_${{ github.run_attempt }}.env | |
| # Ensure cache/download directories exist and are writable by the service user | |
| mkdir -p "$CACHE_DIR" "$DOWNLOADS_DIR" | |
| chown -R bedrock_tweaks:bedrock_tweaks "$CACHE_DIR" "$DOWNLOADS_DIR" | |
| - name: Restart API service | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.SERVER_ADDRESS }} | |
| username: ${{ secrets.SERVER_USER }} | |
| password: ${{ secrets.SERVER_PASSWORD }} | |
| port: 22 | |
| script: sudo systemctl restart ${{ vars.API_SERVICE_NAME }} | |
| - name: Output job status | |
| id: job-status | |
| if: always() | |
| run: echo "status=${{ job.status }}" >> $GITHUB_OUTPUT |