Skip to content
github-actions[bot] edited this page Nov 13, 2026 · 1 revision

📦 Builds e Empacotamento

Esta página descreve como empacotar o Codis para distribuição.


Script de Empacotamento

O Codis inclui o script package_codis.py na raiz do projeto para gerar distribuições.

python package_codis.py

setup.py

O setup.py na raiz define os metadados do pacote Python. A versão não é hardcoded - é extraída dinamicamente de APP_VERSION em src/constants.py via regex, para nunca ficar dessincronizada:

# setup.py (resumido)
import re
from pathlib import Path
from setuptools import setup, find_packages

def _find_version() -> str:
    """Extrai APP_VERSION de src/constants.py."""
    m = re.search(r'^APP_VERSION\s*=\s*["\']([^"\']+)["\']',
                  Path("src/constants.py").read_text(encoding="utf-8"), re.MULTILINE)
    return m.group(1)

setup(
    name="codis",
    version=_find_version(),
    python_requires=">=3.12",
    packages=find_packages(exclude=["tests*", "docs*", "wiki*", "build*", "dist*"]),
    install_requires=[],           # sem dependências obrigatórias
    extras_require={
        "recommended": ["send2trash>=1.8.0", "Pillow>=9.0.0", "markdown>=3.4.0"],
        "full": ["send2trash>=1.8.0", "Pillow>=9.0.0", "markdown>=3.4.0",
                 "py7zr>=0.20.0", "rarfile>=4.0"],
        "dev": ["pyinstaller", "pytest", "pytest-cov", "flake8", "pylint"],
    },
    entry_points={
        "console_scripts": ["codis = run:main"],
        "gui_scripts": ["codis-gui = run:main"],
    },
)

pip install .[dev] instala as ferramentas de desenvolvimento (PyInstaller, pytest, flake8, pylint) de uma vez.

Instalação via pip (modo desenvolvimento)

pip install -e .

Após isso, o comando codis fica disponível globalmente.


Dependências

O requirements.txt lista pacotes opcionais que habilitam funcionalidades extras:

send2trash>=1.8.0    # lixeira nativa (fallback: pseudo-lixeira interna)
Pillow>=9.0.0        # imagens no K-UI
markdown>=3.4.0      # preview Markdown
py7zr>=0.20.0        # suporte a .7z
rarfile>=4.0         # suporte a .rar

Nenhum deles é obrigatório. O Codis executa com zero dependências externas.


Empacotamento com PyInstaller

O Codis inclui um modo dedicado em package_codis.py que monta automaticamente todos os parâmetros corretos do PyInstaller - é a forma recomendada de gerar um executável, em vez de chamar pyinstaller manualmente:

pip install pyinstaller
python package_codis.py pyinstaller            # gera em modo --onedir (padrão)
python package_codis.py pyinstaller --onefile  # executável único em vez de pasta
python package_codis.py pyinstaller --debug    # mantém console para depuração

O padrão é um executável único (--onefile, mesmo modo do CI); use --onedir para gerar uma pasta (inicia mais rápido, pois não auto-extrai a cada execução).

Por baixo dos panos, o script monta automaticamente:

  • Dados incluídos: os src/locales/<idioma>.json (14 idiomas; o .translate_manifest.json de desenvolvimento não entra), wiki/ e requirements.txt
  • Tcl/Tk: coletado pelo hook oficial do PyInstaller. Não adicione --add-data de Tcl/Tk manualmente (duplica os dados e, sem DISPLAY, pode embutir o prefixo inteiro do Python)
  • Imports ocultos: todos os submódulos de tkinter, mais src.plugins, src.ia, src.lsp, src.lint, src.syntax, src.diff, xmlrpc.client/server e email.mime.*
  • Módulos excluídos (reduzem o tamanho do bundle): matplotlib, numpy, pandas, scipy, cv2, PyQt5/6, PySide2/6, wx, entre outros nunca usados pelo Codis

Para nunca usar UPX (como no CI): python package_codis.py pyinstaller --no-upx ou CODIS_NO_UPX=1. O PyInstaller já ignora o UPX fora do Windows, e no executável --onefile final ele não reduz nada.

Tamanho de referência do executável Linux x64: ~32 MB. O CI falha se passar de 60 MB.

Se send2trash estiver instalado no ambiente de build, ele é incluído automaticamente no bundle. Sem ele, o executável final usa a lixeira do projeto (.codis_trash/) normalmente.


Empacotamento com Nuitka (alternativa não-oficial)

Diferente do PyInstaller, o Nuitka não faz parte do pipeline oficial do Codis (não há workflow de CI usando-o, nem script dedicado em package_codis.py). O comando abaixo é um ponto de partida não usado pelo projeto, caso prefira essa alternativa por conta própria.

pip install nuitka
python -m nuitka --onefile --enable-plugin=tk-inter \
    --include-data-dir=src/locales=src/locales \
    --include-data-dir=wiki=wiki \
    run.py

GitHub Actions

O repositório inclui 10 workflows em .github/workflows/. A regra de ouro: cada commit passa por um único pipeline (pipeline.yml), que valida, compila e publica a release uma só vez - os arquivos compilados são os mesmos do artefato e da release, e nada é refeito quando você lança uma versão.

O pipeline (commit → build → release)

push / tag / PR
      │
   plan ─────────────┬──► quality  (testes, lint, i18n)  ──┐
 (decide tudo        │                                      ├──► release ──► summary
  uma única vez)     └──► build    (executáveis + ZIP)    ──┘
Job O que faz
plan Roda .github/scripts/plan.py: lê APP_VERSION, descobre o canal de release, o que mudou desde o último build publicado e monta a matriz de plataformas
quality Chama _quality.yml: sintaxe (Python 3.12/3.13), imports em Linux/Windows/macOS, Flake8 e Pylint (informativos), pytest com cobertura, i18n e consistência de locales
build Chama _build.yml: executáveis PyInstaller de cada plataforma (com smoke test no Linux e limite de 60 MB) + ZIP do código-fonte. Roda em paralelo com quality
release Só roda se quality e build passaram. Baixa os arquivos já compilados, gera SHA256SUMS.txt e as notas e publica a release. Não recompila nada
summary Resumo da execução; falha o pipeline se alguma etapa obrigatória falhou

Canais de release

O canal é decidido pelo plan, sem nenhuma ação manual:

Canal Quando Resultado
stable Push em main/master quando APP_VERSION ainda não tem tag; ou push de tag vX.Y.Z Release oficial vX.Y.Z (marcada como Latest; -beta.N etc. viram pre-release), com notas do CHANGELOG.md e notas automáticas por label
nightly Push em main/master quando a versão já foi lançada Uma única pré-release móvel chamada nightly, atualizada a cada commit com mudança de código (não acumula releases)
dev Push em develop Pré-release móvel dev (somente as 3 plataformas principais)
none Pull request Só valida e compila (3 plataformas principais); não publica

Cada release traz, de uma vez: os executáveis (Windows x64, Linux x64, macOS Apple Silicon e, em main/tag, também Linux ARM64, Windows ARM64/x86 e macOS Intel, que são experimentais e nunca derrubam o pipeline), o ZIP do código-fonte (Codis-vX.Y.Z-source.zip), o SHA256SUMS.txt (usado pelo atualizador do app para conferir a integridade), as notas e o comparativo com a versão anterior.

Releases oficiais são criadas como rascunho, recebem todos os arquivos e só então são publicadas: nem usuários nem o atualizador enxergam uma release pela metade. Pré-releases nightly/dev não afetam o atualizador (ele lê apenas a última release oficial).

Commits só de documentação (*.md, wiki/, docs/, tools/...) não compilam nem publicam release: o plan compara o commit com o último build publicado e só segue se algo que entra no binário mudou (src/**, run.py, requirements.txt, package_codis.py, setup.py, pyproject.toml ou os scripts/workflows de build).

Todos os workflows

Arquivo Trigger O que faz
pipeline.yml Push em main/master/develop, tag v*.*.*, PR para main/master, workflow_dispatch (inputs release e extended) Porta de entrada única: plan → quality ∥ build → release → summary
_quality.yml Chamado pelo pipeline.yml Verificações de código (reutilizável, sem triggers próprios)
_build.yml Chamado pelo pipeline.yml Executáveis multiplataforma + ZIP de código-fonte (reutilizável, não publica)
docs.yml PR com mudanças em docs/código (check); push em main com mudanças em docs/**, wiki/**, *.md, tools/** ou src/constants.py (site + deploy + wiki); workflow_dispatch PR: confere tools/docs_facts.py --check e tools/build_site.py --check. Push: gera docs/ e publica no GitHub Pages; sincroniza wiki/ com a GitHub Wiki somente se wiki/ mudou
codeql.yml Push/PR em main/master/develop com mudanças de código, e semanalmente (segunda 08:00 UTC) Análise estática de segurança CodeQL (independente do pipeline; não bloqueia releases)
dependency-review.yml PR com mudanças em requirements.txt ou .github/dependabot.yml Bloqueia PRs com dependências de CVE alto/crítico e comenta o resumo no PR
label.yml Abertura/edição/sync de PR, abertura/edição de issue Rotulagem automática (as labels alimentam as notas de release por categoria)
locale-sync.yml Somente workflow_dispatch (flag apply) Ferramenta manual: sincroniza as chaves dos 14 locales e commita. A verificação automática já roda em quality
artifact-cleanup.yml Semanalmente (segunda 07:00 UTC), ou workflow_dispatch (dry_run, min_age_days, keep_last_builds) Remove artifacts antigos do Actions, protegendo as execuções mais recentes de pipeline.yml
stale.yml Diariamente (06:00 UTC), ou workflow_dispatch Marca issues/PRs inativos como stale

As categorias das notas automáticas ficam em .github/release.yml (por label: bug, melhoria, segurança, documentação, i18n, dependencies etc.).

Disparo manual

Em Actions → Pipeline → Run workflow:

  • release = auto (padrão): comporta-se como um push no branch escolhido, ignorando o filtro de "o que mudou".
  • release = none: valida e compila sem publicar (útil para testar o build).
  • extended = false: só as 3 plataformas principais.
  • Rodar a partir de uma tag (Use workflow from: Tag: vX.Y.Z) recompila e atualiza os arquivos da release dessa tag (reexecução segura, sem duplicar).

Onde cada coisa roda (para não duplicar)

Tarefa Onde roda Onde não roda mais
Checagem de sintaxe quality (uma vez) no build (antes havia outra)
ZIP do código-fonte build (uma vez) package.yml (removido)
Compilar executáveis build (uma vez por commit) release.yml (recompilava ao criar a tag)
Verificar locales quality locale-sync.yml em push/PR
Site, wiki e conferência de docs docs.yml três workflows separados

Mapa do que mudou: ci.yml → _quality.yml; build.yml + package.yml + release.yml → _build.yml + pipeline.yml; docs-check.yml + pages.yml + wiki-sync.yml → docs.yml. O badge de CI do README aponta para pipeline.yml.


Testes

Os testes ficam em tests/ - 70 arquivos, 1683 casos no total, rodando 100% headless (sem display gráfico):

tests/
├── conftest.py              ← Fixtures compartilhadas + stub de Tkinter
├── test_config.py           ← Testes do ConfigManager
├── test_diff_engine.py      ← Testes do DiffEngine
├── test_lsp_protocol.py     ← Testes do protocolo LSP
├── test_syntax_patterns.py  ← Testes de highlight (8 linguagens)
└── ... (mais 13 arquivos)

Executar todos os testes:

pytest tests/ -v
pytest tests/ --cov=src --cov-report=term-missing   # com cobertura

→ Veja Testes para a cobertura completa por módulo, fixtures disponíveis e convenções.


Distribuição como ZIP Portátil

O Codis pode ser distribuído como um ZIP portátil (sem instalação):

  1. Clone o repositório.
  2. Compacte o diretório: zip -r Codis.zip .
  3. O usuário extrai e executa python run.py - sem nenhum pip install.

Para funcionalidades extras (lixeira nativa, imagens, etc.):

pip install send2trash Pillow markdown

Versionamento

O Codis usa Semantic Versioning (SemVer):

  • MAJOR.MINOR.PATCH - ex: 1.0.0
  • A versão é definida em src/constants.py (APP_VERSION)
  • O codinome de cada versão é definido em APP_CODENAME (ex: "Aether")

Para lançar uma versão não é preciso criar tag nem rodar nada manualmente:

  1. Atualize CHANGELOG.md (seção ## [X.Y.Z]) e APP_VERSION em src/constants.py.
  2. Faça o commit/merge em main.

O pipeline.yml percebe que vX.Y.Z ainda não existe, valida, compila todas as plataformas, gera o ZIP do código-fonte e os checksums e publica a release oficial - tudo na mesma execução. Os commits seguintes (sem mudar a versão) apenas atualizam a pré-release nightly.

Também continua válido criar a tag manualmente (git tag vX.Y.Z && git push origin vX.Y.Z): ela precisa ser igual a APP_VERSION, e se a release já tiver sido publicada o pipeline não faz nada.

Requisitos de runtime do executável compilado

O binário gerado por package_codis.py pyinstaller não embute Python para rodar pip, venv, servidores LSP baseados em Python (pylsp, pyright...) nem o code runner de Python: eles usam o Python do sistema (ver Troubleshooting). Ao adicionar código que lança subprocessos Python, use src/pyexec.py (python_cmd(), child_env(), script_command()) - nunca sys.executable, que no executável compilado é o próprio Codis.

Clone this wiki locally