Going private hides the name everywhere, not just on the profile #25
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: CI | |
| on: | |
| push: | |
| branches: [main, 'dev/**'] | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build and gates | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| # The submodule is deliberately NOT checked out here. src/content points at | |
| # michaeljolley/content, which is private, and the default GITHUB_TOKEN is scoped to | |
| # this repository alone. Asking actions/checkout to do it fails inside git submodule | |
| # output with "Repository not found", which is how this job failed on every run of | |
| # its life while looking like an ordinary red cross. Doing it in a step below turns | |
| # that into an instruction. | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # A deploy key rather than a personal access token, which is decision 101. | |
| # | |
| # A fine grained PAT expires at twelve months at the most and dies with the account | |
| # that issued it, so both of those bring this exact failure back later and quietly, on | |
| # a timer. A deploy key does not expire, is scoped to one repository rather than to a | |
| # person, and needs no decision about who owns it. Netlify solved the same problem on | |
| # this same repository pair in 2024 with a read only deploy key on the content repo, | |
| # so the precedent is already live. | |
| # | |
| # It is also less machinery than the token was. .gitmodules already asks for | |
| # git@github.com:michaeljolley/content.git, so the old step had to rewrite SSH into | |
| # HTTPS to get a token into the URL. With a key the native URL is simply correct and | |
| # the rewrite goes away. | |
| # | |
| # The secret is named for what it holds. Calling an SSH private key CONTENT_TOKEN | |
| # would be the same defect this branch has now caught three times, where a name goes | |
| # on describing what something used to be. | |
| - name: Check out the content submodule | |
| env: | |
| CONTENT_DEPLOY_KEY: ${{ secrets.CONTENT_DEPLOY_KEY }} | |
| run: | | |
| if [ -z "$CONTENT_DEPLOY_KEY" ]; then | |
| echo "::error::CI cannot read src/content, so no collection would have any entries." | |
| echo "src/content is a git submodule pointing at michaeljolley/content, which is" | |
| echo "private. The default GITHUB_TOKEN is scoped to this repository only and" | |
| echo "cannot clone another one." | |
| echo "" | |
| echo "Fix, once, by hand:" | |
| echo " 1. ssh-keygen -t ed25519 -C 'baldbeardedbuilder.com CI' -f content-ci -N ''" | |
| echo " 2. On michaeljolley/content, Settings, Deploy keys, Add deploy key." | |
| echo " Paste content-ci.pub. Leave write access UNCHECKED, a build only reads." | |
| echo " 3. On this repository, save the private half, the whole content-ci file" | |
| echo " including its BEGIN and END lines, as the secret CONTENT_DEPLOY_KEY." | |
| echo " 4. Delete both local files. GitHub keeps the only copies that matter." | |
| echo "" | |
| echo "A deploy key rather than a token on purpose. A token expires and is tied to" | |
| echo "a person, so it brings this same failure back later without warning." | |
| echo "" | |
| echo "This job fails rather than building without content, because an empty" | |
| echo "collection set makes every gate below pass for the wrong reason." | |
| exit 1 | |
| fi | |
| mkdir -p ~/.ssh | |
| chmod 700 ~/.ssh | |
| # printf rather than echo, because a key is worthless if its final newline is lost. | |
| printf '%s\n' "$CONTENT_DEPLOY_KEY" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| ssh-keyscan -t rsa,ecdsa,ed25519 github.com >> ~/.ssh/known_hosts 2>/dev/null | |
| git submodule update --init --depth 1 src/content | |
| if [ ! -f src/content/content.config.ts ]; then | |
| echo "::error::src/content was cloned but content.config.ts is not in it." | |
| exit 1 | |
| fi | |
| echo "src/content present, $(find src/content -name '*.md' | wc -l) markdown files." | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| # Fails if a generated artifact was hand edited. themes.css, the font CSS, the | |
| # taxonomy map and _redirects are all outputs, never inputs. | |
| - name: Generated artifacts are in sync | |
| run: pnpm gen:check | |
| - name: Unit and redirect tests | |
| run: pnpm test | |
| # The baseline was trimmed to the two legacy tables v2 actually reads, so a | |
| # migration leaning on something that went would only fail on a db push against | |
| # a fresh project, which is the worst place to find out. | |
| - name: Migration chain is self contained | |
| run: pnpm check:migrations | |
| # Catches the class of mistake that a build will happily ship: a Supabase column | |
| # that changed shape under a query, a nullable view column read as if it were not. | |
| - name: Types | |
| run: pnpm check | |
| - name: Build | |
| run: pnpm build | |
| # The step above is the first thing in this job that produces dist, and four tests | |
| # in redirects.build.test.mjs need it. They were only in the run above, before the | |
| # build, so they skipped on every run and reported green while asserting nothing. | |
| # Run again here, where dist exists. REQUIRE_DIST turns the skip into a failure, so | |
| # this cannot quietly stop working again if the steps are ever reordered. | |
| - name: Redirect tests against the real build | |
| run: pnpm test | |
| env: | |
| REQUIRE_DIST: '1' | |
| # Reads the built output rather than the source, because the sitemap and the | |
| # Pagefind index are generated and so are never reviewed by a person. Catches a | |
| # parked route that still ships, and any page listed in the sitemap whose own | |
| # markup says noindex. | |
| - name: Shipped output | |
| run: pnpm check:dist | |
| # Both browser gates need dist, and a build is slow enough that handing it over | |
| # beats building it three times. | |
| - name: Upload dist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist | |
| retention-days: 3 | |
| a11y: | |
| name: Accessibility and layout | |
| runs-on: ubuntu-latest | |
| needs: build | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist | |
| - run: pnpm exec playwright install --with-deps chromium | |
| - run: pnpm a11y | |
| # Rides along in this job because it is the only one that pays for a browser, and | |
| # installing chromium twice to run a second thirty second check is not worth it. | |
| # Different question from accessibility, same requirement: a laid out page. | |
| - name: Layout geometry | |
| run: pnpm check:layout | |
| perf: | |
| name: Performance budget | |
| runs-on: ubuntu-latest | |
| needs: build | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist | |
| - run: pnpm exec playwright install --with-deps chromium | |
| - run: pnpm perf |