name: Deploy Landing to Cloudflare Pages on: push: branches: [main] paths: - "apps/landing/**" # Daily rebuild so the build-time GitHub stars / Docker Hub pull-count fetches # refresh even when no landing files changed. schedule: - cron: "0 7 * * *" workflow_dispatch: concurrency: group: deploy-landing cancel-in-progress: true permissions: contents: read jobs: deploy: name: Deploy runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/setup - name: Build Landing Page run: pnpm --filter @snapotter/landing build env: # Authenticated GitHub API (5,000 req/hr) so the build-time star-count # fetch isn't rate-limited on shared runner IPs (60 req/hr unauth). GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Astro's getRelativeLocaleUrl lowercases locale segments by default, so a # regression could re-emit /zh-cn/ etc. in hrefs and hreflang. Those 404 on # case-sensitive Cloudflare Pages while the built dirs + sitemap keep canonical # mixed case, and the mismatch is invisible on a case-insensitive dev machine. # Fail the deploy if any lowercased variant of a mixed-case locale directory # leaks into the emitted HTML. See #554. - name: Guard against lowercased locale URLs run: | set -uo pipefail dist=apps/landing/dist fail=0 for dir in "$dist"/*/; do name=$(basename "$dir") lower=$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]') [ "$name" = "$lower" ] && continue hits=$(grep -rIl --include='*.html' "/$lower/" "$dist" || true) if [ -n "$hits" ]; then echo "::error::Lowercased locale path /$lower/ found in built HTML (canonical is /$name/); it would 404 on Cloudflare Pages. See #554." printf '%s\n' "$hits" | head -5 fail=1 fi done if [ "$fail" -ne 0 ]; then echo "Deploy blocked: lowercased locale URLs would 404 on case-sensitive hosting." exit 1 fi echo "Locale URL casing OK: no lowercased locale paths in built output." - name: Deploy to Cloudflare Pages run: npx wrangler pages deploy apps/landing/dist --project-name snapotter-landing --branch main --commit-dirty=true env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}