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 backend_url_scheme: description: 'The full scheme URL for the backend (e.g., https://api.example.com)' required: true 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 }} jobs: build: name: Build frontend runs-on: ubuntu-latest defaults: run: working-directory: 7project/src/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/src/frontend/package-lock.json - name: Install dependencies run: npm ci - name: Set backend URL from workflow input run: | echo "VITE_BACKEND_URL=${{ inputs.backend_url_scheme }}" >> $GITHUB_ENV - name: Build run: npm run build - name: Upload build artifact uses: actions/upload-artifact@v4 with: name: frontend-dist path: 7project/src/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_RAW="$INPUT_NAME"; elif [ -n "$VAR_NAME" ]; then PNAME_RAW="$VAR_NAME"; else PNAME_RAW="${GITHUB_REPOSITORY##*/}-frontend"; fi # Normalize project name to lowercase to satisfy Cloudflare Pages naming PNAME="${PNAME_RAW,,}" # 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: Ensure Cloudflare Pages project exists env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} PNAME: ${{ steps.pname.outputs.project_name }} run: | set -euo pipefail npx wrangler pages project create "$PNAME" --production-branch=main || true - 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