Repository navigation
247 lines (221 loc) · 11.1 KB
/
Copy pathcloudflare-pages-deploy.yml
File metadata and controls
247 lines (221 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
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 }})