Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
# Fails on regressions against baselines/ and on new divergences.
# How it all fits together, for people: site/content/docs/contributing/pipeline.mdx.
#
# Publishing: the site goes to GitHub Pages from main when vars.PUBLISH_SITE == 'true'.
# vars.SITE_URL is its public URL, path included (default: https://<owner>.github.io/<repo>);
# Publishing: the site goes to Cloudflare Pages (project brazilian-utils-docs) from main when
# vars.PUBLISH_SITE == 'true', with the secrets CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.
# vars.SITE_URL is its public URL, path included (default: https://brazilian-utils.com.br);
# the issues link to it and the README badges point at it.
name: Conformance

Expand Down Expand Up @@ -55,7 +56,7 @@ jobs:
- name: Site URL
env:
CONFIGURED: ${{ vars.SITE_URL }}
run: echo "SITE_URL=${CONFIGURED:-https://${GITHUB_REPOSITORY_OWNER}.github.io/${GITHUB_REPOSITORY#*/}}" >> "$GITHUB_ENV"
run: echo "SITE_URL=${CONFIGURED:-https://brazilian-utils.com.br}" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the PR step compares the contract with the base branch
Expand Down Expand Up @@ -187,10 +188,11 @@ jobs:
echo "ok=true" >> "$GITHUB_OUTPUT"
env:
PUBLISH_SITE: ${{ vars.PUBLISH_SITE }}
- uses: actions/upload-pages-artifact@v3
- uses: actions/upload-artifact@v4
id: pages
if: ${{ !cancelled() && steps.complete.outputs.ok == 'true' }}
with:
name: site
path: site/out/
- id: site
if: ${{ !cancelled() && steps.pages.outcome == 'success' }}
Expand All @@ -216,7 +218,7 @@ jobs:
if: env.HAS_LIBS_TOKEN == 'true'
env:
CONFIGURED: ${{ vars.SITE_URL }}
run: echo "SITE_URL=${CONFIGURED:-https://${GITHUB_REPOSITORY_OWNER}.github.io/${GITHUB_REPOSITORY#*/}}" >> "$GITHUB_ENV"
run: echo "SITE_URL=${CONFIGURED:-https://brazilian-utils.com.br}" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
if: env.HAS_LIBS_TOKEN == 'true'
with:
Expand Down Expand Up @@ -307,14 +309,24 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
concurrency:
group: github-pages
group: cloudflare-pages
cancel-in-progress: false
permissions:
pages: write
id-token: write
contents: read
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
name: production
url: ${{ vars.SITE_URL || 'https://brazilian-utils.com.br' }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
- uses: actions/download-artifact@v4
with:
name: site
path: out/
# The production branch of the Cloudflare Pages project is main: this deploy is the site.
- name: Deploy to Cloudflare Pages
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# The first deploy creates the project; "already exists" afterwards is expected.
run: |
npx --yes wrangler@4.149.0 pages project create brazilian-utils-docs --production-branch main > /dev/null 2>&1 || true
npx --yes wrangler@4.149.0 pages deploy out --project-name brazilian-utils-docs --branch main --commit-hash "$GITHUB_SHA" --commit-dirty=true
36 changes: 34 additions & 2 deletions .github/workflows/site-check.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Pull request checks for the docs site: production dependencies audited, both languages present, lint and
# types, the site builds (usage and guides cloned from the libraries, the status of the last
# published run), then accessibility and design checks. The full pipeline (conformance.yml)
# builds and publishes it with the results of its own run.
# published run) and goes to a Cloudflare Pages preview linked on the PR, then accessibility and
# design checks. The full pipeline (conformance.yml) builds and publishes it with the results of
# its own run.
name: site / check

on:
Expand All @@ -14,6 +15,7 @@ on:

permissions:
contents: read
pull-requests: write # the preview link comment

concurrency:
group: site-check-${{ github.ref }}
Expand Down Expand Up @@ -51,6 +53,36 @@ jobs:
run: npm run build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SITE_PREVIEW: 'true' # served at the root of the preview's own domain, noindex
# The preview: a Cloudflare Pages deployment on the branch pr-<number>, at
# https://pr-<number>.<project>.pages.dev, linked in one PR comment that each push updates. Pull requests from forks have no secrets and get no preview. wrangler runs outside
# site/, so the PR's own node_modules cannot stand in for it.
- name: Preview on Cloudflare Pages
if: github.event.pull_request.head.repo.full_name == github.repository
working-directory: ${{ runner.temp }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -o pipefail
# The first deploy creates the project; "already exists" afterwards is expected.
npx --yes wrangler@4.149.0 pages project create brazilian-utils-docs --production-branch main > /dev/null 2>&1 || true
npx --yes wrangler@4.149.0 pages deploy "$GITHUB_WORKSPACE/site/out" --project-name brazilian-utils-docs --branch "pr-$PR" --commit-hash "$SHA" --commit-dirty=true | tee deploy.log
# The last pages.dev address wrangler prints is the branch alias, which stays the same for the PR.
url=$(grep -o 'https://[^ ]*\.pages\.dev' deploy.log | tail -1)
[ -n "$url" ] || { echo "::error::the deploy printed no pages.dev address"; exit 1; }
marker='<!-- site-preview -->'
body="$marker
**Preview:** $url (\`${SHA:0:7}\`)"
id=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -1)
if [ -n "$id" ]; then
gh api -X PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$id" -f body="$body" > /dev/null
else
gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" -f body="$body" > /dev/null
fi
# After the build: it writes next-env.d.ts and the route types the check reads.
- name: Types
run: npm run typecheck
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ The pipeline publishes the docs site (`vars.PUBLISH_SITE`), with a status page p
Add this badge to the README of each library:

```markdown
[![API contract](https://brazilian-utils.github.io/docs/badges/python.svg)](https://brazilian-utils.github.io/docs/libs/python/)
[![API contract](https://brazilian-utils.com.br/badges/python.svg)](https://brazilian-utils.com.br/libs/python/)
```

Each library keeps the usage examples for the site (`docs/usage/<util>.md`, one
Expand Down Expand Up @@ -206,7 +206,6 @@ test/ unit + integration tests, fixtures per language
action.yml GitHub Action for the libs' CI
templates/ workflows and test harnesses to copy into each lib repo
site/ the docs site (Next.js + Fumadocs), built from all of the above
vercel.json review deployments of the site on Vercel
```

## Development
Expand Down
2 changes: 1 addition & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ is a number, not an opinion.
| Step | Who | How |
|---|---|---|
| Merge the PR of this repository | maintainers | CI runs typecheck, the tests with the language toolchains, contract lint |
| Publish the docs site | org admin | Enable GitHub Pages (source: GitHub Actions). Set the repository variable `PUBLISH_SITE=true` (and `SITE_URL` when a custom domain serves the site) |
| Publish the docs site | org admin | Set up Cloudflare Pages and the repository variable `PUBLISH_SITE=true`, as in [pipeline.mdx](../site/content/docs/contributing/pipeline.mdx#secrets-and-variables) |
| Turn on issue and test sync | org admin | Save a fine-grained token (or GitHub App) with `issues`, `contents` and `pull_requests: write` on every library repo as the secret `LIBS_TOKEN`. Do not give it the `workflows` permission |
| Optional: release notification | org admin | Save a token that can send `repository_dispatch` to the docs repository (`contents: write`) as the secret `DOCS_DISPATCH_TOKEN` in each library, and add the step in [usage-files.mdx](../site/content/docs/contributing/usage-files.mdx#update-the-site-on-each-release) to its release workflow. A release then refreshes the site at once, not at the next nightly |
| Add the Action to every library | one PR per library | Copy `templates/lib-ci/<lang>.yml` to `.github/workflows/api-contract.yml`. Add the badge to the README |
Expand Down
7 changes: 3 additions & 4 deletions site/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
GITHUB_TOKEN=

# Optional. The public URL, path included; its path becomes the base path of every link.
# Defaults to https://brazilian-utils.github.io/docs.
# Defaults to https://brazilian-utils.com.br.
SITE_URL=

# Optional. Set to "fixtures" to skip GitHub entirely and build from fixtures/usage/ (offline mode).
Expand All @@ -14,6 +14,5 @@ USAGE_SOURCE=
SITE_DATA_URL=
SITE_DATA=

# Optional. Vercel builds are noindex; true lifts the meta tag (the X-Robots-Tag header lives in
# vercel.json).
SITE_INDEXABLE=
# Optional. true builds a pull request preview: served at the root of its own domain, noindex.
SITE_PREVIEW=
47 changes: 25 additions & 22 deletions site/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mora no repositório docs. As páginas saem do contrato (`../contract`), das bib
`DESIGN.md` explica o visual.

O site é um app [Next.js](https://nextjs.org) com [Fumadocs](https://fumadocs.dev), exportado
como HTML estático (`output: 'export'`). Não há servidor: o GitHub Pages serve a pasta `out/`.
como HTML estático (`output: 'export'`). Não há servidor: o Cloudflare Pages serve a pasta `out/`.

## Rodar localmente

Expand All @@ -17,7 +17,7 @@ Requer **Node 22.12 ou mais novo** e npm 10.9+.
npm ci # instalação reproduzível
npm run dev # busca os arquivos de uso e sobe o Next em modo dev
npm run build # mesmo fluxo, saída estática em out/
npm run preview # serve out/ como o GitHub Pages serve (base path incluído)
npm run preview # serve out/ como o Cloudflare Pages serve (base path incluído)
npm run lint # ESLint (Next.js, React hooks, jsx-a11y)
npm run typecheck # TypeScript
npm run check:i18n
Expand Down Expand Up @@ -167,11 +167,11 @@ Com `--strict`, o script falha no CI quando falta a versão de um idioma.
## Deploy

Um pipeline só faz tudo: `.github/workflows/conformance.yml`. Ele roda o validador (check, diff,
issues), o `site-data` e o build do site. Ele publica `out/` no GitHub Pages quando
`vars.PUBLISH_SITE == 'true'`. Ele roda a cada merge em `main` (contrato, libs, site), uma vez
issues), o `site-data` e o build do site. Ele publica `out/` no Cloudflare Pages (projeto
`brazilian-utils-docs`, branch de produção `main`) quando `vars.PUBLISH_SITE == 'true'`. Ele roda a cada merge em `main` (contrato, libs, site), uma vez
por dia, e quando uma biblioteca manda `repository_dispatch` com `event_type=lib-released` na
release. `SITE_URL` é a URL pública com o caminho (padrão
`https://<org>.github.io/docs`); o caminho vira o `basePath` do Next. `site-check.yml`
`https://brazilian-utils.com.br`); o caminho vira o `basePath` do Next. `site-check.yml`
roda em todo PR que toca `contract/`, `libs/` ou `site/`: `npm audit` (produção), `check:i18n --strict`,
`lint` sem avisos, `typecheck`, o build (com os arquivos de uso e os guias das bibliotecas), a
verificação de acessibilidade (`npm run a11y`) e a de design (`npm run design`).
Expand All @@ -185,23 +185,26 @@ ferramenta ou uma convenção fixa: `README.md`, `DESIGN.md`, `LICENSE`, `CONTRI
`Cargo.toml`, os arquivos que começam com `_` (ignorados pelo validador) e as fixtures escritas
na convenção de cada linguagem.

## Deploy de revisão (Vercel)

Cada push e cada PR que mexe no site, no contrato, nas libs ou no schema ganha um deploy de
revisão na Vercel, com o link no PR. A configuração é o `vercel.json` na raiz do repositório (o
projeto da Vercel usa a raiz como Root Directory): instala e monta `site/` e publica `site/out/`.

Na Vercel o site fica na raiz do domínio (sem `basePath`). Nenhum deploy da Vercel entra em
buscador, em três camadas: o header `X-Robots-Tag: noindex, nofollow, noarchive` em toda resposta
(`vercel.json`), a meta `robots` `noindex, nofollow` em toda página e um `robots.txt` sem sitemap
(ele deixa o robô entrar, porque só assim ele lê o `noindex`). As URLs canônicas apontam para
`SITE_URL`. Para o dia em que a Vercel servir o site oficial: `SITE_INDEXABLE=true` tira a meta, e
o header sai do `vercel.json`. A Vercel não roda
o validador (precisaria de todas as linguagens), então o build baixa a situação da última execução
publicada. O `ignoreCommand` pula o build quando nada que o site usa mudou desde o último deploy. Não
precisa de variável de ambiente. As opcionais são `SITE_URL`, `SITE_DATA_URL` e `GITHUB_TOKEN`
(sem token, a versão de cada lib sai da tag mais nova, lida com `git ls-remote`). `SITE_DATA=skip`
monta sem a situação. O check de PR (`site-check.yml`) também baixa a situação publicada.
## Deploy de revisão (Cloudflare Pages)

Cada push num PR que mexe no site, no contrato ou nas libs ganha um deploy de revisão no
Cloudflare Pages, no branch `pr-<número>`, com o link num comentário do PR. Quem publica é o
`site-check.yml`, depois do build, com os segredos `CLOUDFLARE_API_TOKEN` e
`CLOUDFLARE_ACCOUNT_ID`. PRs de fork não têm segredos e não ganham deploy de revisão.

O build de revisão roda com `SITE_PREVIEW=true`: o site fica na raiz do domínio (sem `basePath`)
e nenhum deploy de revisão entra em buscador, em três camadas: a Cloudflare põe
`X-Robots-Tag: noindex` em toda resposta de um deploy de revisão, toda página leva a meta
`robots` `noindex, nofollow` e o `robots.txt` sai sem sitemap (ele deixa o robô entrar, porque só
assim ele lê o `noindex`). As URLs canônicas apontam para `SITE_URL`. O check não roda o validador
(precisaria de todas as linguagens), então o build baixa a situação da última execução
publicada. As variáveis opcionais são `SITE_URL`, `SITE_DATA_URL` e `GITHUB_TOKEN` (sem token, a
versão de cada lib sai da tag mais nova, lida com `git ls-remote`). `SITE_DATA=skip` monta sem a
situação.

`public/_headers` define os headers de toda resposta, e `public/_redirects` leva as páginas do
antigo site da biblioteca JavaScript, que ficava em `brazilian-utils.com.br`, para onde elas estão
agora.

## Pendências conhecidas

Expand Down
2 changes: 1 addition & 1 deletion site/content/docs/about/privacy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This site has no accounts, no cookies, no analytics and no ads. It is a set of s

## Where the site is hosted

The site is served by [GitHub Pages](https://pages.github.com/). Like any web server, GitHub receives your IP address and your browser's details when you open a page. The [GitHub Privacy Statement](https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement) covers what GitHub does with them. Preview copies of the site, made for pull requests, are served by [Vercel](https://vercel.com/legal/privacy-policy) and are hidden from search engines.
The site is served by [Cloudflare Pages](https://pages.cloudflare.com/). Like any web server, Cloudflare receives your IP address and your browser's details when you open a page. The [Cloudflare Privacy Policy](https://www.cloudflare.com/privacypolicy/) covers what Cloudflare does with them. Preview copies of the site, made for pull requests, are served the same way and are hidden from search engines.

## What stays in your browser

Expand Down
2 changes: 1 addition & 1 deletion site/content/docs/about/privacy.pt-br.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Este site não tem contas, cookies, analytics nem anúncios. Ele é um conjunto

## Onde o site fica hospedado

O site é servido pelo [GitHub Pages](https://pages.github.com/). Como qualquer servidor web, o GitHub recebe o seu endereço IP e os dados do seu navegador quando você abre uma página. A [Declaração de Privacidade do GitHub](https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement) diz o que o GitHub faz com eles. As cópias de prévia do site, feitas para os pull requests, são servidas pela [Vercel](https://vercel.com/legal/privacy-policy) e ficam fora dos buscadores.
O site é servido pelo [Cloudflare Pages](https://pages.cloudflare.com/). Como qualquer servidor web, a Cloudflare recebe o seu endereço IP e os dados do seu navegador quando você abre uma página. A [Política de Privacidade da Cloudflare](https://www.cloudflare.com/privacypolicy/) diz o que a Cloudflare faz com eles. As cópias de prévia do site, feitas para os pull requests, são servidas do mesmo jeito e ficam fora dos buscadores.

## O que fica no seu navegador

Expand Down
Loading
Loading