build #222
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 | |
| # This is the production build+deploy pipeline: it clones the archive, reads | |
| # ~16k per-style metadata files, and runs the full vite-ssg build. That is far | |
| # too heavy for a per-PR check (and can stall for hours when the archive is | |
| # mid-build), so it does NOT run on pull_request — fast PR validation lives in | |
| # pr-check.yml. This workflow only runs where a real deploy can happen: main, | |
| # manual dispatch, the archive [rebuild] dispatch, and the daily backstop. | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - README.md | |
| - CONTRIBUTING.md | |
| - LICENSE | |
| - .gitignore | |
| workflow_dispatch: | |
| repository_dispatch: | |
| types: [rebuild] | |
| # Backstop: the archive sync triggers a [rebuild] dispatch after it publishes, | |
| # but if that dispatch is ever dropped (network failure, missing token) the | |
| # site would otherwise serve stale data indefinitely. A daily rebuild picks up | |
| # any archive change the dispatch missed. It re-pins the current archive SHA, | |
| # so a no-op day just rebuilds identical output. | |
| schedule: | |
| - cron: '17 5 * * *' | |
| # Least privilege by default. `pages: write` / `id-token: write` are granted | |
| # only to the deploy job, so the build job cannot touch Pages even if the | |
| # deploy gate below were misconfigured. | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| # `pages: read` is what actions/configure-pages needs to read the Pages | |
| # config (it does not enable Pages here, and its outputs are unused — | |
| # astro.config.mjs hardcodes `site`). Deliberately not `write`: a PR build | |
| # must not be able to publish. | |
| permissions: | |
| contents: read | |
| pages: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| # Build-time metadata only: the two registry JSONs and the file manifest. | |
| # These are never served to a browser, so they can come from the private | |
| # archive — which matters because archive-public does not publish | |
| # font-metadata.json yet. blob:none + sparse-checkout pulls ~5 MB of blobs | |
| # instead of the repo's 6 GB, while still giving `git ls-tree` the full | |
| # tree for the manifest. | |
| # | |
| # Skipped when the secret is unavailable (fork PRs); fetch-data.sh then | |
| # falls back to archive-public and reports the missing artifact clearly. | |
| - name: Checkout archive-private (metadata only) | |
| id: meta | |
| continue-on-error: true | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: fontist/fontist-archive-private | |
| token: ${{ secrets.FONTIST_CI_PAT_TOKEN }} | |
| path: vendor/archive-private | |
| filter: blob:none | |
| sparse-checkout: | | |
| fonts.json | |
| metadata | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Pages | |
| id: pages | |
| uses: actions/configure-pages@v5 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| # Matches pr-check.yml (and local dev): keep the build and the PR | |
| # test run on the same Node so a PR can't pass on a version the | |
| # deploy build doesn't use. Node 20 is also deprecated on runners. | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| # Both cached artifacts are keyed on the upstream commit that produced | |
| # them, so a cache hit is only ever used when the source is unchanged. | |
| # fetch-data.sh and fetch-essenfont.mjs both skip their work when the | |
| # output is already present, so a hit turns into a genuine no-op. | |
| - name: Resolve upstream commits | |
| id: upstream | |
| run: | | |
| echo "formulas=$(git ls-remote https://github.com/fontist/formulas.git v5 | cut -f1)" >> "$GITHUB_OUTPUT" | |
| echo "essenfont=$(git ls-remote https://github.com/essenfont/essenfont.github.io.git HEAD | cut -f1)" >> "$GITHUB_OUTPUT" | |
| # Saves cloning formulas + `npm install` in docs/ + generate.js (~13s), | |
| # and means a formulas outage no longer breaks the site build. | |
| - name: Cache formulas data | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| public/formulas-data.json | |
| public/stats.json | |
| key: formulas-data-${{ steps.upstream.outputs.formulas }} | |
| # 209 per-block woff2 files (~97 MB), otherwise re-cloned every build. | |
| - name: Cache Essenfont woff2 | |
| uses: actions/cache@v4 | |
| with: | |
| path: public/fonts | |
| key: essenfont-${{ steps.upstream.outputs.essenfont }} | |
| - name: Build (fetch-data.sh clones the archive + formulas v5, then vite-ssg builds) | |
| # Only point at the private checkout if it actually succeeded, so a | |
| # fork PR falls back cleanly instead of pointing at an empty directory. | |
| env: | |
| ARCHIVE_META_PATH: ${{ steps.meta.outcome == 'success' && 'vendor/archive-private' || '' }} | |
| run: npm run build | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: dist | |
| deploy: | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| if: github.ref_name == github.event.repository.default_branch | |
| runs-on: ubuntu-latest | |
| needs: build | |
| # Pages write access lives here only — the build job above runs read-only. | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 |