Repository navigation
build(deps-dev): bump @types/node from 26.5.1 to 26.6.3 #462
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 Pages Deploy | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main"] | |
| permissions: {} | |
| env: | |
| ARTIFACT_NAME: roadmap-dsa-artifact | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| name: Build Pages | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| # Histórico completo, e não o clone raso (profundidade 1) do padrão. | |
| # O motivo é medido: num clone raso o `git log <caminho>` NÃO falha, | |
| # ele devolve a data do HEAD para qualquer arquivo. Quem deriva data | |
| # do histórico no build (o `lastmod` do sitemap) recebe a mesma data | |
| # para as 40 URLs e não tem como perceber. O repositório tem 269 | |
| # commits e 18 MB de `.git`: o custo do histórico inteiro cabe no | |
| # ruído do passo de checkout. | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - name: Restore cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 | |
| with: | |
| path: | | |
| .next/cache | |
| key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx', '**.md', '**.mdx') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build with Next.js | |
| run: npm run build | |
| env: | |
| # Apoiadores vêm da APOIA.se no build. A chave vai no header | |
| # `x-api-key` e o segredo no `Authorization: Bearer` (doc v0.1 da | |
| # APOIA.se); ver `src/app/apoie/apoiadores.ts`. | |
| # | |
| # Estes três nomes substituem `APOIASE_TOKEN`/`APOIASE_CAMPAIGN_ID`, | |
| # que o workflow injetava e que NUNCA existiram como secret: o build | |
| # caía calado na lista de plano B, e era por isso que a página | |
| # publicada mostrava 3 apoiadores com 5 apoios na campanha. Sem os | |
| # secrets o comportamento continua o mesmo, com uma diferença que é o | |
| # ponto: agora o log do build diz que caiu. | |
| APOIASE_KEY: ${{ secrets.APOIASE_KEY }} | |
| APOIASE_SECRET: ${{ secrets.APOIASE_SECRET }} | |
| APOIASE_CAMPAIGN: ${{ secrets.APOIASE_CAMPAIGN }} | |
| # Google Analytics só no build da `main`. Este mesmo job roda em PR, | |
| # e o deploy de preview do Cloudflare é um site público de verdade: | |
| # sem este recorte, cada PR mandaria pageview para a propriedade de | |
| # produção. String vazia = o componente devolve null e o gtag.js nem | |
| # é pedido. O ID não é segredo (sai no HTML), por isso `vars`; o | |
| # fallback em `secrets` é só para não quebrar se alguém cadastrar no | |
| # lugar errado. | |
| NEXT_PUBLIC_GA_ID: ${{ github.ref == 'refs/heads/main' && (vars.NEXT_PUBLIC_GA_ID || secrets.NEXT_PUBLIC_GA_ID) || '' }} | |
| # Só usada se a verificação do Search Console for pela tag HTML em vez | |
| # do TXT no DNS. Vale em qualquer branch: é uma meta tag inerte. | |
| NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION: ${{ vars.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION }} | |
| # Prova de que o que se testa é o que se publica. O mesmo comando roda nos | |
| # três jobs; se os três hashes baterem, ninguém reconstruiu no meio do | |
| # caminho. É a diferença entre reaproveitar o artefato e dizer que | |
| # reaproveitou. | |
| - name: Impressão digital do out/ | |
| run: | | |
| arquivos=$(find out -type f | wc -l | tr -d " ") | |
| hash=$(find out -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | cut -d" " -f1) | |
| echo "out/: $arquivos arquivos, sha256 do conjunto = $hash" | |
| echo "\`build\`: **$arquivos** arquivos, sha256 do conjunto \`$hash\`" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload artifact | |
| if: github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| retention-days: 3 | |
| path: ./out | |
| - name: Skip | |
| if: github.event.pull_request.head.repo.fork == true || github.actor == 'dependabot[bot]' | |
| run: echo "Skipping artifact upload for forked or Dependabot PR" | |
| # O portão que faltava. Antes, `tests.yml` e este workflow disparavam os DOIS | |
| # em `push: main`, em paralelo e sem nenhum `needs` entre eles: medido no push | |
| # do merge do #44, o deploy terminou às 03:13:17 e a suíte só às 03:17:37. | |
| # São 4m20 de produção no ar sem ninguém ter olhado — o CI avisava, não | |
| # barrava. E não havia via indireta: `branches/main/protection` responde | |
| # "Branch not protected". | |
| # | |
| # Este job roda ENTRE o build e o deploy, e testa o artefato JÁ CONSTRUÍDO em | |
| # vez de reconstruir. Os dois motivos: | |
| # · o que se testa passa a ser byte a byte o que se publica (a `impressao | |
| # digital` abaixo prova isso, comparando o hash nos três jobs); | |
| # · o `npm run build` deixa de rodar duas vezes por push. | |
| test: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| name: Playwright (no artefato que vai ao ar) | |
| permissions: | |
| contents: read | |
| # Só fora de PR. Em PR quem testa é o `tests.yml`, que constrói por conta | |
| # própria — e precisa continuar assim, porque PR de fork não recebe | |
| # artefato (o `Upload artifact` acima é bloqueado para fork) e porque o | |
| # preview do revisor não deve esperar 5 minutos de suíte para aparecer. | |
| if: github.event_name != 'pull_request' | |
| steps: | |
| # Sem `fetch-depth: 0` de propósito: este job não constrói e não lê o | |
| # histórico. Ele só precisa de `tests/`, do `playwright.config.ts` e das | |
| # dependências. | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install Playwright (chromium) | |
| run: npx playwright install --with-deps chromium | |
| - name: Download a Build Artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| path: ./out | |
| - name: Impressão digital do out/ | |
| run: | | |
| arquivos=$(find out -type f | wc -l | tr -d " ") | |
| hash=$(find out -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | cut -d" " -f1) | |
| echo "out/: $arquivos arquivos, sha256 do conjunto = $hash" | |
| echo "\`test\`: **$arquivos** arquivos, sha256 do conjunto \`$hash\`" >> "$GITHUB_STEP_SUMMARY" | |
| # `npm test`, NUNCA `npm run test:build`. O `test:build` reconstrói, e | |
| # reconstruir aqui desfaz a razão de este job existir: a suíte passaria a | |
| # exercitar um build diferente do que o `deploy` publica. | |
| - name: Run tests | |
| run: npm test | |
| env: | |
| # A credencial entra AQUI, num job que não constrói nada, por um motivo | |
| # só: `tests/segredo-nao-vai-para-o-cliente.spec.ts` tem duas metades, e | |
| # a segunda procura o VALOR literal do segredo dentro do `out/`. Sem as | |
| # variáveis ela se declara `skip`, e a metade mais conclusiva da | |
| # varredura nunca rodaria em lugar nenhum — só na máquina de quem tem | |
| # `.env.local`, que é justamente quem não precisa dela. | |
| # | |
| # Com elas, o artefato que ACABOU de ser construído com a credencial de | |
| # verdade é conferido contra a credencial de verdade, no caminho entre o | |
| # build e o deploy. É a diferença entre "nenhuma forma conhecida de | |
| # segredo aparece" e "este segredo não aparece". | |
| # | |
| # O teste nunca imprime o valor (a mensagem de falha cita o NOME da | |
| # variável e o arquivo), e o Actions ainda mascara secret no log por | |
| # cima disso. Este job não roda em pull_request, então PR de fork | |
| # continua sem ver nada. | |
| APOIASE_KEY: ${{ secrets.APOIASE_KEY }} | |
| APOIASE_SECRET: ${{ secrets.APOIASE_SECRET }} | |
| APOIASE_CAMPAIGN: ${{ secrets.APOIASE_CAMPAIGN }} | |
| - name: Upload report on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: playwright-report-deploy | |
| path: playwright-report/ | |
| retention-days: 5 | |
| deploy: | |
| runs-on: ubuntu-latest | |
| needs: [build, test] | |
| # `needs` com job pulado pula o dependente junto, e o `test` é pulado em PR | |
| # de propósito. `!failure() && !cancelled()` é o idioma para dependência | |
| # opcional: roda quando nenhum dos dois REPROVOU. Se o `test` reprovar, | |
| # `failure()` é verdadeiro e o deploy não acontece — que é o ponto. | |
| if: ${{ !failure() && !cancelled() && (github.event_name != 'pull_request' || (github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]')) }} | |
| name: Deploy to Cloudflare Pages | |
| permissions: | |
| contents: read | |
| deployments: write | |
| pull-requests: write # To create a comment in the PR | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| persist-credentials: false | |
| - name: Download a Build Artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: ${{ env.ARTIFACT_NAME }} | |
| path: ./out | |
| - name: Impressão digital do out/ | |
| run: | | |
| arquivos=$(find out -type f | wc -l | tr -d " ") | |
| hash=$(find out -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | cut -d" " -f1) | |
| echo "out/: $arquivos arquivos, sha256 do conjunto = $hash" | |
| echo "\`deploy\`: **$arquivos** arquivos, sha256 do conjunto \`$hash\`" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Deploy | |
| id: deploy | |
| uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages deploy out --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} --branch ${{ github.ref_name }} | |
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Add Preview Url in PR Comment | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3 | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false | |
| with: | |
| header: pr-preview-url | |
| hide_and_recreate: true | |
| hide_classify: "OUTDATED" | |
| message: | | |
| ### 🚀 **Preview Url** 🚀 | |
| [${{ steps.deploy.outputs.deployment-url }}](${{ steps.deploy.outputs.deployment-url }}) |