diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..acea793
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,23 @@
+---
+name: "🐛 Relato de Problema / Bug"
+about: Ajude-nos a identificar e corrigir algo que não está funcionando como esperado
+title: "[BUG] "
+labels: ["bug"]
+assignees: ""
+---
+
+### ⚠️ O que está acontecendo?
+
+
+### 🔄 Como reproduzir?
+1. Acesse o site em `...`
+2. Clique em `...`
+3. Observe o erro: `...`
+
+### 📱 Ambiente onde ocorreu o problema
+- **Dispositivo:** (ex: Celular iPhone 14, Computador Dell)
+- **Navegador:** (ex: Chrome 124, Safari, Firefox)
+- **Sistema Operacional:** (ex: Android, iOS, Linux, Windows, macOS)
+
+### 📸 Captura de tela (opcional)
+
diff --git a/.github/ISSUE_TEMPLATE/content_update.md b/.github/ISSUE_TEMPLATE/content_update.md
new file mode 100644
index 0000000..7a42c4f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/content_update.md
@@ -0,0 +1,24 @@
+---
+name: "📝 Atualização de Conteúdo / Texto"
+about: Sugira correções de texto, links, fotos ou informações do evento
+title: "[DOCS] "
+labels: ["documentation", "good first issue"]
+assignees: ""
+---
+
+### 📄 Onde está o texto ou informação a ser alterada?
+- [ ] No arquivo `variables.yaml`
+- [ ] No arquivo `README.md`
+- [ ] Em uma página Jinja2 (`pages/index.j2` ou `templates/base.j2`)
+- [ ] Imagem ou foto da galeria (`static/img/`)
+
+### ✏️ O que deve ser alterado?
+- **Texto / Conteúdo Atual:**
+ > ...
+
+- **Novo Texto / Sugestão:**
+ > ...
+
+### 🤝 Você gostaria de enviar um Pull Request com essa correção?
+- [ ] Sim, quero fazer a alteração!
+- [ ] Não, apenas sugerindo.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..ff5a69c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: "✨ Sugestão de Melhoria / Funcionalidade"
+about: Sugira uma nova ideia, seção ou melhoria para o site da PyNE 2027
+title: "[FEAT] "
+labels: ["enhancement", "good first issue"]
+assignees: ""
+---
+
+### 💡 Qual é a ideia ou funcionalidade sugerida?
+
+
+### 🎯 Qual problema isso resolve ou qual valor agrega à comunidade?
+
+
+### 🎨 Proposta visual ou referências (opcional)
+
+
+### 🤝 Você gostaria de trabalhar nessa issue?
+- [ ] Sim, quero enviar um Pull Request!
+- [ ] Não, apenas sugerindo para a comunidade.
diff --git a/.github/labels.json b/.github/labels.json
new file mode 100644
index 0000000..d1050e8
--- /dev/null
+++ b/.github/labels.json
@@ -0,0 +1,67 @@
+[
+ {
+ "name": "type: feat",
+ "color": "0e8a16",
+ "description": "Nova funcionalidade ou componente visual"
+ },
+ {
+ "name": "type: enhancement",
+ "color": "1d76db",
+ "description": "Melhoria em funcionalidade existente"
+ },
+ {
+ "name": "type: bug",
+ "color": "d93f0b",
+ "description": "Correção de comportamento inesperado ou erro"
+ },
+ {
+ "name": "type: docs",
+ "color": "0075ca",
+ "description": "Atualização de textos, README ou documentação"
+ },
+ {
+ "name": "type: chore",
+ "color": "7057ff",
+ "description": "Tarefas técnicas, CI/CD ou dependências"
+ },
+ {
+ "name": "type: a11y",
+ "color": "fbca04",
+ "description": "Acessibilidade (WCAG, leitores de tela, contraste)"
+ },
+ {
+ "name": "scope: ui/ux",
+ "color": "bfdadc",
+ "description": "Interface, CSS, animações e responsividade"
+ },
+ {
+ "name": "scope: content",
+ "color": "d4c5f9",
+ "description": "Textos, copies e informações do evento"
+ },
+ {
+ "name": "scope: community",
+ "color": "f9d0c4",
+ "description": "Código de conduta, voluntários e inclusão"
+ },
+ {
+ "name": "scope: sponsor",
+ "color": "fef2c0",
+ "description": "Patrocínio, cotas e media kit"
+ },
+ {
+ "name": "scope: schedule",
+ "color": "c2e0c6",
+ "description": "Programação, palestras e palestrantes"
+ },
+ {
+ "name": "good first issue",
+ "color": "7057ff",
+ "description": "Ideal para pessoas iniciando no código aberto"
+ },
+ {
+ "name": "help wanted",
+ "color": "008672",
+ "description": "Tarefa aberta aguardando contribuição"
+ }
+]
diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml
new file mode 100644
index 0000000..424c8d9
--- /dev/null
+++ b/.github/workflows/ci-dev.yml
@@ -0,0 +1,77 @@
+name: CI - Contribuições e Deploy Staging (dev)
+
+on:
+ push:
+ branches:
+ - dev
+ pull_request:
+ branches:
+ - dev
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ validate:
+ name: Validação de Código e Renderização
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout do código
+ uses: actions/checkout@v4
+
+ - name: Instalar uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ version: "latest"
+ enable-cache: true
+
+ - name: Instalar Python e dependências
+ run: |
+ uv python install 3.14
+ uv sync --extra dev
+
+ - name: Verificar Lint (Ruff)
+ run: uv run ruff check .
+
+ - name: Verificar Formatação (Ruff Format)
+ run: uv run ruff format --check .
+
+ - name: Executar Testes Automatizados (Pytest)
+ run: uv run pytest -v
+
+ - name: Testar Renderização Estática (Build)
+ run: uv run main render
+
+ - name: Verificar Integridade do Site Gerado
+ run: |
+ test -f dist/index.html || (echo "dist/index.html não foi gerado!" && exit 1)
+ test -f dist/static/img/favicon.png || (echo "dist/static/img/favicon.png não foi encontrado!" && exit 1)
+ test -f dist/robots.txt || (echo "dist/robots.txt não foi gerado!" && exit 1)
+ test -f dist/.nojekyll || (echo "dist/.nojekyll não foi gerado!" && exit 1)
+
+ - name: Upload de Artefato do GitHub Pages
+ if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./dist
+
+ deploy-staging:
+ name: Deploy Staging no GitHub Pages
+ if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
+ needs: validate
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Publicar no GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml
new file mode 100644
index 0000000..ff10820
--- /dev/null
+++ b/.github/workflows/ci-main.yml
@@ -0,0 +1,77 @@
+name: CI/CD - Produção e Deploy (main)
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+jobs:
+ build-and-test:
+ name: Testes e Build de Produção
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout do código
+ uses: actions/checkout@v4
+
+ - name: Instalar uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ version: "latest"
+ enable-cache: true
+
+ - name: Instalar Python e dependências
+ run: |
+ uv python install 3.14
+ uv sync --extra dev
+
+ - name: Verificar Lint (Ruff)
+ run: uv run ruff check .
+
+ - name: Verificar Formatação (Ruff Format)
+ run: uv run ruff format --check .
+
+ - name: Executar Testes Automatizados (Pytest)
+ run: uv run pytest -v
+
+ - name: Gerar Build de Produção (Render)
+ run: uv run main render
+
+ - name: Verificar Integridade do Site Gerado
+ run: |
+ test -f dist/index.html || (echo "dist/index.html não foi gerado!" && exit 1)
+ test -f dist/static/img/favicon.png || (echo "dist/static/img/favicon.png não foi encontrado!" && exit 1)
+ test -f dist/robots.txt || (echo "dist/robots.txt não foi gerado!" && exit 1)
+ test -f dist/.nojekyll || (echo "dist/.nojekyll não foi gerado!" && exit 1)
+
+ - name: Upload de Artefato do GitHub Pages
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./dist
+
+ deploy:
+ name: Deploy para GitHub Pages
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ needs: build-and-test
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy para GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/sync-issues.yml b/.github/workflows/sync-issues.yml
new file mode 100644
index 0000000..3e56509
--- /dev/null
+++ b/.github/workflows/sync-issues.yml
@@ -0,0 +1,31 @@
+name: Sincronizar Issues e Labels
+
+on:
+ workflow_dispatch:
+
+permissions:
+ issues: write
+
+jobs:
+ sync:
+ name: Criar Labels e Issues no Repositório
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout do código
+ uses: actions/checkout@v4
+
+ - name: Instalar uv
+ uses: astral-sh/setup-uv@v5
+ with:
+ version: "latest"
+ enable-cache: true
+
+ - name: Instalar Python
+ run: uv python install 3.14
+
+ - name: Executar sincronização de issues e labels
+ run: uv run python scripts/create_issues.py
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
diff --git a/ISSUES.md b/ISSUES.md
new file mode 100644
index 0000000..6b83921
--- /dev/null
+++ b/ISSUES.md
@@ -0,0 +1,198 @@
+# 📌 Quadro de Issues & Roadmap — Python Nordeste 2027
+
+Quadro oficial de acompanhamento do desenvolvimento do site da **Python Nordeste 2027 (Parnaíba/PI)**.
+
+---
+
+## 🏷️ Taxonomia de Labels Oficiais
+
+Para organizar as contribuições da comunidade no GitHub, utilizamos a seguinte convenção de etiquetas:
+
+| Categoria | Label | Cor | Descrição |
+| :--- | :--- | :--- | :--- |
+| **Tipo** | `type: feat` | `#0E8A16` | Nova funcionalidade ou componente visual |
+| | `type: enhancement` | `#1D76DB` | Melhoria em funcionalidade existente |
+| | `type: bug` | `#D93F0B` | Correção de comportamento inesperado ou erro |
+| | `type: docs` | `#0075CA` | Atualização de textos, README ou documentação |
+| | `type: chore` | `#7057FF` | Tarefas técnicas, CI/CD ou dependências |
+| | `type: a11y` | `#FBCA04` | Acessibilidade (WCAG, leitores de tela, contraste) |
+| **Escopo** | `scope: ui/ux` | `#BFDADC` | Interface, CSS, animações e responsividade |
+| | `scope: content` | `#D4C5F9` | Textos, copies e informações do evento |
+| | `scope: community` | `#F9D0C4` | Código de conduta, voluntários e inclusão |
+| | `scope: sponsor` | `#FEF2C0` | Patrocínio, cotas e media kit |
+| | `scope: schedule` | `#C2E0C6` | Programação, palestras e palestrantes |
+| **Onboarding** | `good first issue` | `#7057FF` | Ideal para pessoas iniciando no código aberto |
+| | `help wanted` | `#008672` | Tarefa aberta aguardando contribuição |
+
+---
+
+## 🟢 Entregas Realizadas (Done)
+
+- [x] **Setup do Ambiente & Ferramentas**
+ - Configuração do projeto com Python 3.14 + `uv` + Hatchling.
+ - Estrutura de dependências de desenvolvimento (`ruff`, `pytest`, `livereload`).
+- [x] **Servidor Local com LiveReload**
+ - Implementação do comando `uv run main start` com abertura automática de navegador e recarregamento em tempo real.
+ - Contribuição e correção de resiliência no repositório fork do `python-livereload`.
+- [x] **Modularização Dinâmica (YAML + Jinja2)**
+ - Criação do [variables.yaml](variables.yaml) para controle completo de textos, datas, local e links sem alterar código.
+- [x] **Identidade Visual Oficial PyNE 2027**
+ - Extração do brasão do mapa do Piauí/Nordeste (`static/img/pyne_emblem.png`).
+ - Vetorização do fundo de curvas de nível (`static/img/contour_lines.svg`).
+ - Aplicação da paleta *Rosa Dunas* (`#E47A9F`), *Rosa Destaque* (`#D4638A`), *Areia/Off-white* (`#F9F4E8`) e *Café Escuro* (`#2B1B22`).
+- [x] **Padronização de Ícones com Font Awesome 6**
+ - Integração do CDN do Font Awesome 6 e substituição de emojis soltos por ícones semânticos (``, ``).
+- [x] **Link do Repositório GitHub no Rodapé**
+ - Adicionado botão estilo badge no rodapé e pílula no Hero com link direto para o repositório da comunidade.
+- [x] **Animações Visuais Modernas (GPU-Accelerated)**
+ - Efeito *Dunas Vivas* com deslocamento orgânico do fundo topográfico.
+ - Flutuação suave do brasão oficial (`floatEmblem`).
+ - Interação 3D tilt no emblema ao mover o cursor (`static/js/hero_animations.js`).
+ - Entrada escalonada dos elementos (*Staggered Reveal*).
+ - Pulso no pin de localização e brilho reflexivo no número "2027".
+ - Carrossel infinito cinematográfico com máscara de gradiente nas fotos de Parnaíba.
+- [x] **Favicon com Fundo e Alto Contraste**
+ - Geração do favicon em formato squircle com fundo sólido Rosa Dunas, além de `static/img/apple-touch-icon.png` e `favicon.ico`.
+- [x] **Copywriting Comunitário ("Pessoas > Tecnologia")**
+ - Ajuste da mensagem principal com o lema histórico da comunidade Python.
+- [x] **Esteira de CI/CD (GitHub Actions)**
+ - Criação do workflow `.github/workflows/ci-dev.yml` para validação de PRs e deploy de staging no GitHub Pages.
+ - Criação do workflow `.github/workflows/ci-main.yml` para produção.
+ - Suíte de testes automatizados em `tests/test_site.py`.
+- [x] **Blindagem de Staging contra Motores de Busca**
+ - Injeção automática de ``.
+ - Geração automática de `dist/robots.txt` com `Disallow: /`.
+ - Desativação do Google Analytics em ambiente de staging.
+ - Criação de `dist/.nojekyll` para compatibilidade com o GitHub Pages.
+
+---
+
+## 🟡 Próximas Prioridades (To Do / In Progress)
+
+### #01 — Barra de Navegação (Navbar Fixa e Responsiva)
+- **Tipo:** `type: feat`
+- **Labels:** `scope: ui/ux`, `good first issue`
+- **Milestone:** `v0.2.0 - Navegação & Identidade`
+- **Esforço:** `Médio`
+- **Descrição:** Adicionar barra de navegação no topo com logo, links de rolagem suave para as seções da página (*Início*, *Sobre*, *Destino*, *CDC*) e atalhos para as redes sociais.
+- **Critérios de Aceite:**
+ - [ ] Navbar fixa no topo com efeito vidro/blur translúcido ao rolar a página.
+ - [ ] Links âncora funcionando com rolagem suave (`scroll-behavior: smooth`).
+ - [ ] Ícones sociais do GitHub, Instagram, LinkedIn e YouTube à direita.
+ - [ ] Menu hambúrguer responsivo para celulares (< 768px).
+
+---
+
+### #02 — Seção "Sobre a Python Nordeste"
+- **Tipo:** `type: feat`
+- **Labels:** `scope: content`, `good first issue`
+- **Milestone:** `v0.2.0 - Navegação & Identidade`
+- **Esforço:** `Pequeno`
+- **Descrição:** Criar um bloco institucional contextualizando a história itinerante de mais de 10 anos do evento, o compromisso com diversidade e inclusão, e a celebração da chegada inédita ao litoral do Piauí.
+- **Critérios de Aceite:**
+ - [ ] Texto e títulos configuráveis via `variables.yaml`.
+ - [ ] Layout harmonizado com a paleta oficial (fundo Areia/Off-white e tipografia *Outfit*).
+ - [ ] Destaques em números ou marcos da comunidade (anos de história, edições passadas).
+
+---
+
+### #03 — Página Dedicada do Código de Conduta (`/cdc.html`)
+- **Tipo:** `type: feat`
+- **Labels:** `scope: community`, `type: docs`, `good first issue`
+- **Milestone:** `v0.2.0 - Navegação & Identidade`
+- **Esforço:** `Pequeno`
+- **Descrição:** Implementar uma página dedicada (`pages/cdc.j2`) com o texto completo do Código de Conduta da APyB e canais de contato da comissão de resposta a incidentes.
+- **Critérios de Aceite:**
+ - [ ] Página acessível diretamente pela rota `/cdc.html`.
+ - [ ] Texto integral com formatação limpa e legível.
+ - [ ] Informações claras sobre como reportar incidentes de forma segura.
+
+---
+
+### #04 — Easter Egg no DevTools Console
+- **Tipo:** `type: chore`
+- **Labels:** `scope: community`, `good first issue`
+- **Milestone:** `v0.2.0 - Navegação & Identidade`
+- **Esforço:** `Pequeno`
+- **Descrição:** Injetar no console do navegador uma mensagem acolhedora com arte em texto da edição 2027, bandeiras da diversidade e o lema comunitário *Pessoas > Tecnologia*.
+- **Critérios de Aceite:**
+ - [ ] Mensagem estilizável com CSS no `console.log`.
+ - [ ] Execução leve no carregamento da página sem impactar performance.
+
+---
+
+### #05 — Rodapé Modular e Configurável (Multi-colunas)
+- **Tipo:** `type: enhancement`
+- **Labels:** `scope: ui/ux`, `type: feat`
+- **Milestone:** `v0.2.0 - Navegação & Identidade`
+- **Esforço:** `Médio`
+- **Descrição:** Estruturar o rodapé de forma totalmente genérica e parametrizável via `variables.yaml`, suportando colunas opcionais (links rápidos, créditos de realização e canais de contato).
+- **Critérios de Aceite:**
+ - [ ] Colunas opcionais: se uma seção não estiver preenchida no YAML, ela não é renderizada.
+ - [ ] Preservação do link oficial do repositório no GitHub com ícone Font Awesome.
+ - [ ] Layout responsivo em colunas no desktop e empilhado no celular.
+
+---
+
+## 📋 Backlog de Fases Futuras (Roadmap do Evento)
+
+### #06 — Módulo de Voluntárias(os) & Organização
+- **Tipo:** `type: feat`
+- **Labels:** `scope: community`, `type: enhancement`
+- **Milestone:** `v0.3.0 - Envolvimento Comunitário`
+- **Esforço:** `Médio`
+- **Descrição:** Suporte modular no `variables.yaml` para listar voluntárias e voluntários da organização com foto, nome, bio e links sociais, renderizados em grid dinâmico.
+
+---
+
+### #07 — Seção de Captação de Patrocínio & Media Kit
+- **Tipo:** `type: feat`
+- **Labels:** `scope: sponsor`, `type: feat`
+- **Milestone:** `v0.4.0 - Captação de Recursos`
+- **Esforço:** `Médio`
+- **Descrição:** Bloco de chamada para empresas parceiras com botões de download do Media Kit (pt-br / en) ativável condicionalmente conforme a fase de captação.
+
+---
+
+### #08 — Grade de Patrocinadoras por Cotas
+- **Tipo:** `type: feat`
+- **Labels:** `scope: sponsor`, `type: feat`
+- **Milestone:** `v0.4.0 - Captação de Recursos`
+- **Esforço:** `Médio`
+- **Descrição:** Estrutura pronta para exibir marcas apoiadoras organizadas por cotas (*Diamante, Ouro, Prata, Bronze, Apoio*), ativadas dinamicamente via YAML.
+
+---
+
+### #09 — Seção do Local do Evento (Venue & Informações de Parnaíba)
+- **Tipo:** `type: feat`
+- **Labels:** `scope: content`, `scope: ui/ux`
+- **Milestone:** `v0.5.0 - Experiência do Participante`
+- **Esforço:** `Médio`
+- **Descrição:** Bloco com detalhes do espaço em Parnaíba, dicas de deslocamento, hospedagem na região e mapa interativo integrado.
+
+---
+
+### #10 — Página de Palestrantes e Agenda (`/speakers.html`)
+- **Tipo:** `type: feat`
+- **Labels:** `scope: schedule`, `type: feat`
+- **Milestone:** `v0.6.0 - Grade de Programação`
+- **Esforço:** `Grande`
+- **Descrição:** Grid de palestrantes confirmados, keynotes e grade de horários integrada ao Pretalx ou lista estática.
+
+---
+
+### #11 — Modal de Boas-Vindas Comunitário
+- **Tipo:** `type: feat`
+- **Labels:** `scope: community`, `scope: ui/ux`
+- **Milestone:** `v0.5.0 - Experiência do Participante`
+- **Esforço:** `Pequeno`
+- **Descrição:** Popup acolhedor na primeira visita (com persistência em `localStorage`) destacando o Código de Conduta e avisos importantes.
+
+---
+
+### #12 — Auditoria de Acessibilidade (a11y) & SEO Final
+- **Tipo:** `type: a11y`
+- **Labels:** `type: a11y`, `scope: ui/ux`
+- **Milestone:** `v1.0.0 - Release Oficial`
+- **Esforço:** `Médio`
+- **Descrição:** Verificação de contraste de cores nos níveis WCAG AA/AAA, tags ARIA semânticas, navegação por teclado e pontuação 100 no Lighthouse.
diff --git a/README.md b/README.md
index 32720f3..f6966ce 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ O projeto utiliza o **[uv](https://docs.astral.sh/uv/)** para gerenciamento de d
| --- | --- |
| `uv run main start` | Compila o site e inicia o servidor local em `http://localhost:8000` |
| `uv run main render` | Apenas compila as páginas e estáticos para a pasta `dist/` |
+| `uv run pytest` | Executa os testes automatizados de integridade do site |
| `uv run ruff check` | Analisa e valida a qualidade do código Python |
| `uv run ruff format` | Formata o código Python automaticamente |
@@ -87,9 +88,10 @@ Damos as boas-vindas a todas as pessoas! Se esta é a sua primeira experiência
2. **Faça suas alterações** e visualize localmente com `uv run main start`.
-3. **Valide a formatação do código:**
+3. **Valide a formatação e os testes locais:**
```bash
uv run ruff check
+ uv run pytest
```
4. **Faça o commit e envie sua branch:**
@@ -99,4 +101,4 @@ Damos as boas-vindas a todas as pessoas! Se esta é a sua primeira experiência
git push origin minha-contribuicao
```
-5. **Abra um Pull Request (PR)** apontando para a branch `dev`.
+5. **Abra um Pull Request (PR)** apontando para a branch `dev`. A CI do GitHub executará as validações automaticamente em seu PR!
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..4fbd24c
Binary files /dev/null and b/favicon.ico differ
diff --git a/main.py b/main.py
index 0ced965..a6922bc 100644
--- a/main.py
+++ b/main.py
@@ -8,20 +8,16 @@
from typer import Typer
from yaml import safe_load
-settings_file = Path(__file__).parent / 'variables.yaml'
-pages_folder = Path(__file__).parent / 'pages'
-templates_folder = Path(__file__).parent / 'templates'
-static_folder = Path(__file__).parent / 'static'
-output_folder = Path(__file__).parent / 'dist'
+settings_file = Path(__file__).parent / "variables.yaml"
+pages_folder = Path(__file__).parent / "pages"
+templates_folder = Path(__file__).parent / "templates"
+static_folder = Path(__file__).parent / "static"
+output_folder = Path(__file__).parent / "dist"
cli = Typer(no_args_is_help=True)
-environment = Environment(
- loader=FileSystemLoader(
- searchpath=pages_folder.parent
- )
-)
-environment.filters['shuffle'] = lambda seq: random.sample(list(seq), k=len(seq))
+environment = Environment(loader=FileSystemLoader(searchpath=pages_folder.parent))
+environment.filters["shuffle"] = lambda seq: random.sample(list(seq), k=len(seq))
def load_settings() -> dict:
@@ -41,13 +37,32 @@ def render():
output_folder.mkdir(exist_ok=True)
static_folder.copy(output_folder / static_folder.name)
- for file in pages_folder.glob('*.j2'):
+ root_favicon = Path(__file__).parent / "favicon.ico"
+ if root_favicon.exists():
+ shutil.copy(root_favicon, output_folder / "favicon.ico")
+
+ for file in pages_folder.glob("*.j2"):
template_path = file.relative_to(pages_folder.parent).as_posix()
template = environment.get_template(template_path)
rendered_content = template.render(settings)
- output_file = output_folder / file.with_suffix('.html').name
+ output_file = output_folder / file.with_suffix(".html").name
output_file.write_text(rendered_content)
+ # Geração automática de robots.txt com base no ambiente (staging vs production)
+ is_staging = settings.get("environment") == "staging" or not settings.get(
+ "allow_indexing", True
+ )
+ robots_file = output_folder / "robots.txt"
+ if is_staging:
+ robots_file.write_text("User-agent: *\nDisallow: /\n")
+ else:
+ site_url = settings.get("event", {}).get("url", "").rstrip("/")
+ sitemap_line = f"\nSitemap: {site_url}/sitemap.xml" if site_url else ""
+ robots_file.write_text(f"User-agent: *\nAllow: /{sitemap_line}\n")
+
+ # Arquivo .nojekyll para o GitHub Pages servir arquivos estáticos diretamente
+ (output_folder / ".nojekyll").touch()
+
@cli.command()
def start(port: int = 8000, host: str = "127.0.0.1"):
@@ -60,17 +75,25 @@ def start(port: int = 8000, host: str = "127.0.0.1"):
server = Server()
# Monitora dados, templates, páginas e estáticos
server.watch(str(settings_file), render)
- server.watch(str(pages_folder / '*.j2'), render)
- server.watch(str(templates_folder / '*.j2'), render)
+ server.watch(str(pages_folder / "*.j2"), render)
+ server.watch(str(templates_folder / "*.j2"), render)
server.watch(str(static_folder), render)
- print(f"🔥 Servidor com Live Reload rodando em http://{host}:{port}/ (Ctrl+C para encerrar)")
+ print(
+ f"🔥 Servidor com Live Reload rodando em http://{host}:{port}/ (Ctrl+C para encerrar)"
+ )
server.serve(root=str(output_folder), host=host, port=port, open_url=True)
except ImportError:
- handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(output_folder))
+ handler = functools.partial(
+ http.server.SimpleHTTPRequestHandler, directory=str(output_folder)
+ )
server = http.server.ThreadingHTTPServer((host, port), handler)
- print(f"Servidor rodando em http://{host}:{port}/ (pressione Ctrl+C para encerrar)")
- print("💡 Dica: instale as dependências de dev ('uv sync --extra dev') para ativar o Live Reload automático.")
+ print(
+ f"Servidor rodando em http://{host}:{port}/ (pressione Ctrl+C para encerrar)"
+ )
+ print(
+ "💡 Dica: instale as dependências de dev ('uv sync --extra dev') para ativar o Live Reload automático."
+ )
try:
server.serve_forever()
except KeyboardInterrupt:
diff --git a/pages/index.j2 b/pages/index.j2
index 6ee1327..171e514 100644
--- a/pages/index.j2
+++ b/pages/index.j2
@@ -5,45 +5,116 @@
{% endblock %}
{% block content %}
-