Resumo
Este fluxo publica alterações do Notion sem usar webhook público. O cron da hospedagem executa um script no servidor; esse script chama a API do GitHub e dispara um workflow mínimo com workflow_dispatch. O workflow consulta rapidamente a data source do Notion, compara o estado atual com o último hash salvo em cache e só dispara o deploy principal se houver mudança.
Vantagens:
- não precisa expor endpoint público para o Notion
- não depende do
scheduledo GitHub Actions - evita rodar build quando nada mudou
- mantém o deploy pesado separado da checagem rápida
- usa o cron já disponível no painel da hospedagem
- mantém token, repositório e branch fora do script
Use este fluxo se o site é estático e precisa rebuildar quando o conteúdo do Notion mudar. O processo não é instantâneo: ele roda no intervalo definido no cron da hospedagem.
Antes de começar, tenha em mãos:
- workflow principal de deploy já funcionando
- workflow principal com
workflow_dispatch - acesso ao repositório no GitHub
- permissão para criar secrets e variables no GitHub
- token da integração do Notion
- ID da data source do Notion
- acesso SSH ao servidor
- acesso ao cron da hospedagem
- branch principal confirmada, normalmente
main
Fluxo
cron da hospedagem executa ~/bin/notion-change-check.sh
> script carrega ~/.env do servidor
> script chama a API do GitHub
> GitHub dispara o workflow Notion Change Check via workflow_dispatch
> runner consulta a data source do Notion
> runner monta um hash com id, last_edited_time, archived e in_trash de cada página
> runner compara esse hash com o cache da última execução
> se nada mudou, o workflow termina sem build
> se mudou, o workflow salva o novo hash
> workflow mínimo dispara o workflow principal via workflow_dispatch
> workflow principal executa sync, build e deploy
Workflow do GitHub Actions
No projeto local:
mkdir -p .github/workflows
nano .github/workflows/notion-change-check.yml
Cole o conteúdo abaixo:
name: Notion Change Check
on:
workflow_dispatch:
permissions:
actions: write
contents: read
concurrency:
group: notion-change-check
cancel-in-progress: true
jobs:
check:
name: Check Notion changes
runs-on: ubuntu-latest
env:
NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
NOTION_DATA_SOURCE_ID: ${{ secrets.NOTION_DATA_SOURCE_ID }}
NOTION_VERSION: "2026-03-11"
DEPLOY_WORKFLOW_FILE: ${{ vars.DEPLOY_WORKFLOW_FILE || 'ci-cd-static-pipeline.yml' }}
DEPLOY_BRANCH: ${{ github.ref_name }}
steps:
- name: Validate configuration
run: |
if [ -z "$NOTION_TOKEN" ]; then
echo "NOTION_TOKEN is empty or was not configured in GitHub Secrets."
exit 1
fi
if [ -z "$NOTION_DATA_SOURCE_ID" ]; then
echo "NOTION_DATA_SOURCE_ID is empty or was not configured in GitHub Secrets."
exit 1
fi
- name: Build Notion state hash
id: notion_state
run: |
python - <<'PY' >> "$GITHUB_OUTPUT"
import hashlib
import json
import os
import urllib.error
import urllib.request
token = os.environ["NOTION_TOKEN"]
data_source_id = os.environ["NOTION_DATA_SOURCE_ID"]
notion_version = os.environ["NOTION_VERSION"]
url = f"https://api.notion.com/v1/data_sources/{data_source_id}/query"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Notion-Version": notion_version,
}
cursor = None
pages = []
while True:
payload = {"page_size": 100}
if cursor:
payload["start_cursor"] = cursor
request = urllib.request.Request(
url,
data=json.dumps(payload).encode("utf-8"),
headers=headers,
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=20) as response:
data = json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as error:
message = error.read().decode("utf-8")
raise SystemExit(f"Notion API returned {error.code}: {message}")
for result in data.get("results", []):
if result.get("object") == "page":
pages.append({
"id": result.get("id"),
"last_edited_time": result.get("last_edited_time"),
"archived": result.get("archived", False),
"in_trash": result.get("in_trash", False),
})
if not data.get("has_more"):
break
cursor = data.get("next_cursor")
state = json.dumps(
sorted(pages, key=lambda page: page["id"] or ""),
sort_keys=True,
separators=(",", ":"),
)
digest = hashlib.sha256(state.encode("utf-8")).hexdigest()
print(f"hash={digest}")
print(f"page_count={len(pages)}")
PY
- name: Restore previous Notion state
id: notion_cache
uses: actions/cache/restore@v5
with:
path: .notion-state
key: notion-state-${{ steps.notion_state.outputs.hash }}
restore-keys: |
notion-state-
- name: Stop when nothing changed
if: steps.notion_cache.outputs.cache-hit == 'true'
run: |
echo "No Notion changes detected."
echo "Pages checked: ${{ steps.notion_state.outputs.page_count }}"
- name: Save current Notion state
if: steps.notion_cache.outputs.cache-hit != 'true'
run: |
mkdir -p .notion-state
printf '%s\n' "${{ steps.notion_state.outputs.hash }}" > .notion-state/hash
printf '%s\n' "${{ steps.notion_state.outputs.page_count }}" > .notion-state/page-count
- name: Cache current Notion state
if: steps.notion_cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: .notion-state
key: notion-state-${{ steps.notion_state.outputs.hash }}
- name: Dispatch deploy workflow
if: steps.notion_cache.outputs.cache-hit != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Notion changes detected."
echo "Pages checked: ${{ steps.notion_state.outputs.page_count }}"
gh workflow run "$DEPLOY_WORKFLOW_FILE" \
--repo "${{ github.repository }}" \
--ref "$DEPLOY_BRANCH"
No nano, salve e saia com: Ctrl + O, Enter, Ctrl + X.
Depois:
git add .github/workflows/notion-change-check.yml notion-change-check.md
git commit -m "Adiciona checagem de mudanças no Notion"
git push
Passo a passo da configuração
1. Confirmar o workflow principal
O workflow principal precisa aceitar workflow_dispatch.
Exemplo:
on:
push:
branches:
- main
workflow_dispatch:
Neste projeto, o checador usa ci-cd-static.yml como workflow principal padrão.
Se quiser usar outro workflow sem editar o arquivo, crie uma variável do repositório:
Settings > Secrets and variables > Actions > Variables > New repository variable
Criar:
DEPLOY_WORKFLOW_FILEValor:ci-cd-static.yml
Se o deploy roda a partir de outra branch, ajuste BRANCH no ~/.env do servidor. O workflow mínimo usa a mesma branch recebida no workflow_dispatch.
2. Criar os secrets do Notion no GitHub
No GitHub, acesse:
Settings > Secrets and variables > Actions > New repository secret
Crie:
NOTION_TOKENValor: token da integração do NotionNOTION_DATA_SOURCE_IDValor: ID da data source usada pelo projeto
Esses valores são usados apenas dentro do GitHub Actions.
3. Criar token do GitHub para o cron da hospedagem
O cron da hospedagem precisa chamar a API do GitHub para disparar o workflow mínimo. Para isso, crie um Fine-grained Personal Access Token.
No GitHub, acesse:
GitHub > Foto do perfil > Settings > Developer settings > Personal access tokens > Fine-grained tokens > Generate new token
Preencha:
Token name:Cron - <nome-projeto>Expiration: escolha um prazo de validadeResource owner: usuário ou organização dona do repositórioRepository access:Only selected repositoriesSelected repositories: repositório do projeto
Em Repository permissions, configure:
Actions:Read and writeContents:ReadMetadata:Read-only
Depois clique em Generate token e copie o valor. O GitHub mostra esse token apenas uma vez; se perder o valor, será necessário gerar outro.
4. Atualizar o ~/.env do servidor
No servidor:
nano ~/.env
Adicione ou ajuste:
GITHUB_TOKEN="github_pat_xxxxxxxxxxxxxxxxx"
GITHUB_OWNER=<usuario-ou-organizacao>
GITHUB_REPO=<repositorio>
BRANCH=main
No nano, salve e saia com: Ctrl + O, Enter, Ctrl + X.
Esses valores são usados pelo script do cron. Se mudar token, repositório ou branch, altere o ~/.env; o script continua igual.
5. Criar o script no servidor
No servidor:
mkdir -p ~/bin
nano ~/bin/notion-change-check.sh
Cole:
#!/usr/bin/env bash
set -e
ENV_PATH="${HOME}/.env"
if [ ! -f "$ENV_PATH" ]; then
echo "Env file not found:$ENV_PATH"
exit 1
fi
set -a
. "$ENV_PATH"
set +a
: "${GITHUB_TOKEN:?GITHUB_TOKEN vazio}"
: "${GITHUB_OWNER:?GITHUB_OWNER vazio}"
: "${GITHUB_REPO:?GITHUB_REPO vazio}"
: "${BRANCH:?BRANCH vazio}"
curl -fsS -X POST \
"https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/actions/workflows/notion-change-check.yml/dispatches" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer${GITHUB_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-H "Content-Type: application/json" \
-d "{\"ref\":\"${BRANCH}\"}"
No nano, salve e saia com: Ctrl + O, Enter, Ctrl + X.
Depois:
chmod 700 ~/bin/notion-change-check.sh
6. Testar o script no servidor
Rode manualmente:
bash ~/bin/notion-change-check.sh
Uma resposta vazia indica sucesso. A API do GitHub retorna 204 No Content quando aceita o dispatch.
Depois, confira no GitHub:
GitHub > Actions > Notion Change Check
Se a chamada funcionou, uma nova execução deve aparecer com evento workflow_dispatch.
7. Testar manualmente no GitHub
No GitHub, rode o workflow mínimo manualmente:
GitHub > Actions > Notion Change Check > Run workflow
Na primeira execução, o cache ainda não existe. Por isso, o workflow deve considerar que houve mudança e disparar o deploy principal.
Na segunda execução, se nada mudou no Notion, o workflow deve terminar com:
No Notion changes detected.
8. Criar o cron na hospedagem
No painel da hospedagem, crie um cron.
Use um nome claro:
Notion Change Check
No campo COMANDO, use apenas:
bash ~/bin/notion-change-check.sh
Para rodar às 08:00 e 16:00 todos os dias, use:
0 8,16 * * *
No painel, preencha:
MINUTO: 0
HORA: 8,16
DIA DO MÊS: *
MÊS: *
DIA DA SEMANA: *
9. Testar com mudança real
Faça uma alteração simples em uma página da data source usada pelo projeto.
Depois, rode o script manualmente ou aguarde o próximo horário configurado no cron.
O workflow deve detectar um novo hash e disparar o deploy principal.
Etapas de apoio
▸ Checklist antes de ativar o cron
Antes de depender do agendamento no dia a dia, confira:
- workflow principal possui
workflow_dispatch NOTION_TOKENcriado emGitHub SecretsNOTION_DATA_SOURCE_IDcriado emGitHub Secrets- integração do Notion tem acesso à data source
DEPLOY_WORKFLOW_FILE, se existir emGitHub Variables, aponta para o workflow corretoGITHUB_TOKENsalvo no~/.envdo servidorGITHUB_OWNERsalvo no~/.envdo servidorGITHUB_REPOsalvo no~/.envdo servidorBRANCHsalvo no~/.envdo servidor- token usado no
~/.envpossuiActions: Read and write - script
~/bin/notion-change-check.shcriado e com permissão de execução - script
~/bin/notion-change-check.shexecuta manualmente no servidor - primeira execução manual do checador funcionou
- segunda execução manual não disparou deploy sem mudança
- cron da hospedagem chama
bash ~/bin/notion-change-check.sh
▸ Como o deploy funciona no dia a dia
O cron da hospedagem executa o script no intervalo definido.
Se o conteúdo do Notion não mudou, nada acontece além da checagem. Se o conteúdo mudou, o workflow mínimo dispara o workflow principal de deploy.
O deploy manual continua disponível:
GitHub > Actions > CI/CD Static Pipeline > Run workflow
O checador manual também continua disponível:
GitHub > Actions > Notion Change Check > Run workflow
▸ Como saber que deu certo
Use o histórico do cron da hospedagem e do GitHub Actions e confira estes resultados:
- script manual não imprime erro
Notion Change Checkaparece no GitHub com eventoworkflow_dispatchNotion Change Checktermina rápido quando nada mudou- logs mostram
No Notion changes detected.quando o hash é igual Notion Change CheckdisparaCI/CD Static Pipelinequando o hash muda- workflow principal termina verde
- conteúdo atualizado aparece no site depois do deploy
▸ Erros comuns
Use a saída do script, os logs do cron da hospedagem e os logs do workflow mínimo como ponto de partida:
- resposta vazia do script: sucesso; a API do GitHub aceitou o dispatch
curl: (22) The requested URL returned error: 401: token ausente, inválido ou expiradocurl: (22) The requested URL returned error: 403: token sem permissãoActions: Read and writecurl: (22) The requested URL returned error: 404: repositório, workflow ou branch incorretos, ou token sem acesso ao repositórioGITHUB_TOKEN vazio: confira se o cron carrega o~/.envdo mesmo usuárioNOTION_TOKEN is empty: o secretNOTION_TOKENnão foi criado ou está vazioNOTION_DATA_SOURCE_ID is empty: o secretNOTION_DATA_SOURCE_IDnão foi criado ou está vazioNotion API returned 401: token do Notion inválido ou integração sem permissãoNotion API returned 404: data source incorreta ou sem acesso para a integração- workflow principal não dispara: confira
DEPLOY_WORKFLOW_FILE,BRANCHe se o workflow principal possuiworkflow_dispatch - primeira execução disparou deploy mesmo sem mudança recente: comportamento esperado, porque ainda não havia cache
- mudança no Notion não foi detectada: confira se a página alterada pertence à data source consultada e se a integração tem acesso a ela