name: Frontend - Build and Deploy to Cloudflare Pages on: workflow_call: inputs: mode: description: "Build mode: 'prod' or 'pr'" required: true type: string pr_number: description: 'PR number (required when mode=pr)' required: false type: string project_name: description: 'Cloudflare Pages project name (overrides default)' required: false type: string secrets: CLOUDFLARE_API_TOKEN: required: true CLOUDFLARE_ACCOUNT_ID: required: true outputs: deployed_url: description: 'URL of deployed frontend' value: ${{ jobs.deploy.outputs.deployed_url }} # Required repository secrets: # CLOUDFLARE_API_TOKEN - API token with Pages:Edit (or Account:Workers Scripts:Edit) permissions # CLOUDFLARE_ACCOUNT_ID - Your Cloudflare account ID # Optional repository variables: # CF_PAGES_PROJECT_NAME - Default Cloudflare Pages project name # PROD_DOMAIN - App domain for prod releases (e.g., api.example.com or https://api.example.com) # BACKEND_URL_PR_TEMPLATE - Template for PR backend URL. Use {PR} placeholder for PR number (e.g., https://api-pr-{PR}.example.com) jobs: build: name: Build frontend runs-on: ubuntu-latest defaults: run: working-directory: 7project/frontend steps: - name: Checkout uses: actions/checkout@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: 7project/frontend/package-lock.json - name: Install dependencies run: npm ci - name: Compute backend URL for Vite id: be env: EVENT_NAME: ${{ github.event_name }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} PR_TEMPLATE: ${{ vars.BACKEND_URL_PR_TEMPLATE }} PROD_DOMAIN: ${{ vars.PROD_DOMAIN }} MODE: ${{ inputs.mode }} run: | set -euo pipefail URL="" if [ -n "${PROD_DOMAIN:-}" ]; then if echo "$PROD_DOMAIN" | grep -Eiq '^https?://'; then URL="$PROD_DOMAIN" else URL="https://${PROD_DOMAIN}" fi fi if [ "${MODE:-}" = "pr" ] || [ "${EVENT_NAME}" = "pull_request" ]; then if [ -n "${PR_TEMPLATE:-}" ] && [ -n "${PR_NUMBER:-}" ] ; then URL="${PR_TEMPLATE//\{PR\}/${PR_NUMBER}}" fi fi echo "Using backend URL: ${URL:-}" echo "VITE_BACKEND_URL=${URL}" >> $GITHUB_ENV - name: Build run: npm run build - name: Upload build artifact uses: actions/upload-artifact@v4 with: name: frontend-dist path: 7project/frontend/dist deploy: name: Deploy to Cloudflare Pages needs: build runs-on: ubuntu-latest outputs: deployed_url: ${{ steps.out.outputs.deployed_url }} steps: - name: Checkout uses: actions/checkout@v4 - name: Download build artifact uses: actions/download-artifact@v4 with: name: frontend-dist path: dist - name: Determine project name and branch id: pname env: INPUT_MODE: ${{ inputs.mode }} INPUT_PR: ${{ inputs.pr_number }} run: | set -euo pipefail # Prefer manual input, then repo variable, fallback to repo-name INPUT_NAME='${{ inputs.project_name }}' VAR_NAME='${{ vars.CF_PAGES_PROJECT_NAME }}' if [ -n "$INPUT_NAME" ]; then PNAME="$INPUT_NAME"; elif [ -n "$VAR_NAME" ]; then PNAME="$VAR_NAME"; else PNAME="${GITHUB_REPOSITORY##*/}-frontend"; fi # Determine branch for Pages if [ "${INPUT_MODE}" = "pr" ]; then if [ -z "${INPUT_PR}" ]; then echo "pr_number is required when mode=pr"; exit 1; fi PBRANCH="pr-${INPUT_PR}" else PBRANCH="main" fi echo "project_name=$PNAME" >> $GITHUB_OUTPUT echo "branch=$PBRANCH" >> $GITHUB_OUTPUT - name: Deploy using Cloudflare Wrangler uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} command: pages deploy dist --project-name=${{ steps.pname.outputs.project_name }} --branch=${{ steps.pname.outputs.branch }} - name: Compute deployed URL id: out env: PNAME: ${{ steps.pname.outputs.project_name }} PBRANCH: ${{ steps.pname.outputs.branch }} run: | set -euo pipefail if [ "$PBRANCH" = "main" ]; then URL="https://${PNAME}.pages.dev" else URL="https://${PBRANCH}.${PNAME}.pages.dev" fi echo "deployed_url=$URL" >> $GITHUB_OUTPUT