fix(toolbar): make the top menu bar horizontally scrollable on tablets #467
Workflow file for this run
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: Cloudflare preview | |
| # Builds the docs site + web demo for every pull request and uploads it to a | |
| # Cloudflare Pages project as a *preview* deployment, then comments the URL on | |
| # the PR. This replaces the old Netlify Deploy Previews. | |
| # | |
| # The build mirrors .github/workflows/pages.yml (the GitHub Pages production | |
| # deploy) so previews match what ships: | |
| # docs deps -> jupyterlite deps -> npm ci -> web demo build -> docs build -> | |
| # bundle demo into site/demo. | |
| # | |
| # NOTE on fork PRs: the `pull_request` event does NOT expose repository secrets | |
| # to PRs opened from forks, so previews only run for branches pushed to this | |
| # repo (maintainers and collaborators). External fork PRs are skipped — the job | |
| # guard below short-circuits them instead of failing with a missing token. | |
| # | |
| # Required repository secrets (already present for the Workers deploys): | |
| # CLOUDFLARE_API_TOKEN - needs the "Cloudflare Pages: Edit" permission | |
| # CLOUDFLARE_ACCOUNT_ID | |
| # Optional (baked into the client bundle, same as pages.yml): | |
| # VITE_GEE_OAUTH_CLIENT_ID, VITE_PROTOMAPS_API_KEY | |
| on: | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| concurrency: | |
| group: cf-preview-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| preview: | |
| name: Build and deploy preview | |
| runs-on: ubuntu-latest | |
| # Skip fork PRs: secrets are unavailable there, so the deploy would fail. | |
| if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| # Match ci.yml: don't leave the GITHUB_TOKEN in .git/config where an | |
| # npm lifecycle script during `npm ci`/`npm run build` could read it. | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements-docs.txt | |
| apps/geolibre-desktop/jupyterlite/requirements.txt | |
| - name: Install documentation dependencies | |
| run: python -m pip install -r requirements-docs.txt | |
| - name: Install JupyterLite build dependencies | |
| run: python -m pip install -r apps/geolibre-desktop/jupyterlite/requirements.txt | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: lts/* | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install frontend dependencies | |
| run: npm ci | |
| - name: Build web demo | |
| run: npm run build -w geolibre-desktop | |
| env: | |
| GEOLIBRE_APP_BASE: ./ | |
| VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} | |
| VITE_PROTOMAPS_API_KEY: ${{ secrets.VITE_PROTOMAPS_API_KEY }} | |
| VITE_GEOLIBRE_COLLAB_URL: wss://collab.geolibre.app | |
| - name: Build documentation | |
| run: zensical build --strict | |
| - name: Add web demo to site | |
| run: | | |
| mkdir -p site/demo | |
| cp -R apps/geolibre-desktop/dist/. site/demo/ | |
| test -f site/demo/index.html | |
| test -f site/demo/jupyterlite/lab/index.html | |
| - name: Offload oversized DuckDB WASM to CDN | |
| # Cloudflare Pages rejects any single file > 25 MiB. The bundled DuckDB | |
| # WASM modules (duckdb-eh ~35 MiB, duckdb-mvp ~40 MiB) exceed that, so we | |
| # drop the local copies and redirect their hashed URLs to jsDelivr at the | |
| # exact pinned version (byte-identical to the bundled file). This only | |
| # affects the Cloudflare preview; GitHub Pages still serves them locally. | |
| # Any OTHER oversized file is a hard error so we never ship a broken site. | |
| run: | | |
| set -euo pipefail | |
| version=$(node -e "console.log(require('./node_modules/@duckdb/duckdb-wasm/package.json').version)") | |
| echo "duckdb-wasm version: $version" | |
| redirects=site/_redirects | |
| touch "$redirects" | |
| moved=0 | |
| # 26214400 bytes = 25 MiB, Cloudflare Pages' exact per-file limit. | |
| while IFS= read -r -d '' f; do | |
| rel=${f#site} | |
| base=$(basename "$f") | |
| case "$base" in | |
| duckdb-eh-*) cdn=duckdb-eh.wasm ;; | |
| duckdb-mvp-*) cdn=duckdb-mvp.wasm ;; | |
| duckdb-coi-*) cdn=duckdb-coi.wasm ;; | |
| *) | |
| echo "::error::Oversized file not handled by CDN redirect: $rel ($(du -h "$f" | cut -f1)). Cloudflare Pages rejects files > 25 MiB." | |
| exit 1 ;; | |
| esac | |
| echo "$rel https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@${version}/dist/${cdn} 302" >> "$redirects" | |
| rm "$f" | |
| echo "redirect: $rel -> $cdn" | |
| moved=$((moved + 1)) | |
| done < <(find site -type f -size +26214400c -print0) | |
| echo "offloaded $moved oversized file(s)" | |
| echo "----- site/_redirects -----" | |
| cat "$redirects" | |
| - name: Deploy to Cloudflare Pages | |
| id: deploy | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| # --branch is anything other than the project's production branch, so | |
| # every run here is a preview deployment with its own URL. | |
| command: >- | |
| pages deploy site | |
| --project-name=geolibre-preview | |
| --branch="${{ github.head_ref || github.ref_name }}" | |
| --commit-dirty=true | |
| - name: Comment preview URL on PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| env: | |
| DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }} | |
| with: | |
| script: | | |
| const url = process.env.DEPLOY_URL; | |
| if (!url) return; | |
| const marker = '<!-- cloudflare-preview -->'; | |
| const body = `${marker}\n### ⚡ Cloudflare Pages preview\n\n| Item | Value |\n| --- | --- |\n| Preview | ${url} |\n| Demo app | ${url}/demo/ |\n| Commit | \`${context.sha.slice(0, 7)}\` |`; | |
| const { owner, repo } = context.repo; | |
| const issue_number = context.issue.number; | |
| const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }); | |
| const existing = comments.find((c) => c.body && c.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number, body }); | |
| } |