chore: padroniza arquitetura Ciberfauna, logo na raiz, hooks de verificação e CI multi-arch no runner ARM
CI / Formatação e clippy (push) Successful in 1m9s
CI / Auditoria de dependências (push) Successful in 12s
CI / Testes (Linux) (push) Successful in 1m34s
Release / Cria a release (push) Successful in 5s
CI / Compila o app desktop (Linux) (push) Successful in 1m36s
Release / CLI (linux-aarch64) (push) Successful in 1m23s
Release / CLI (linux-x86_64) (push) Successful in 1m40s
Release / App desktop (linux-aarch64) (push) Successful in 7m32s
Release / App desktop (linux-x86_64) (push) Failing after 9m5s
Release / App desktop (windows) (push) Failing after 6m20s
No files matched your search
@@ -1,26 +1,27 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
# Mudança só de documentação não precisa de 6 jobs em 3 sistemas.
|
||||
# Mudança só de documentação ou estado de ferramentas não precisa dos jobs de CI.
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', 'scripts/release-notes.sh', '.gitea/workflows/release.yml']
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', '.opencode/**', '.codex/**', '.cursor/**', '.superpowers/**', '.superset/**', '.gitea/ISSUE_TEMPLATE/**', 'scripts/release-notes.sh', 'scripts/release-upload.sh', '.gitea/workflows/release.yml']
|
||||
pull_request:
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', 'scripts/release-notes.sh', '.gitea/workflows/release.yml']
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', '.opencode/**', '.codex/**', '.cursor/**', '.superpowers/**', '.superset/**', '.gitea/ISSUE_TEMPLATE/**', 'scripts/release-notes.sh', 'scripts/release-upload.sh', '.gitea/workflows/release.yml']
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
group: ci-${{ gitea.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: -D warnings
|
||||
# O runner mantém /opt/hostedtoolcache num volume que sobrevive entre jobs:
|
||||
# a toolchain do rustup, o registry do cargo e o diretório target ficam ali,
|
||||
# então nada disso é baixado ou recompilado do zero a cada execução. Cada job
|
||||
# a toolchain do rustup, o registry do cargo, caches de npm/apt e o diretório
|
||||
# target ficam ali, então nada disso é baixado ou recompilado do zero. Cada job
|
||||
# usa o seu próprio target (flags diferentes invalidariam o de outro job).
|
||||
CARGO_HOME: /opt/hostedtoolcache/cargo
|
||||
RUSTUP_HOME: /opt/hostedtoolcache/rustup
|
||||
NPM_CONFIG_CACHE: /opt/hostedtoolcache/npm-cache
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -77,14 +78,9 @@ jobs:
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cache dos pacotes do Tauri (.deb)
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/apt-cache
|
||||
key: apt-tauri-${{ runner.os }}-${{ runner.arch }}-${{ env.ImageVersion }}-${{ hashFiles('scripts/apt-install.sh') }}
|
||||
- name: Dependências do Tauri
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Gera os ícones
|
||||
run: npx --yes @tauri-apps/cli@2 icon apps/gui/src-tauri/icons/source.png -o apps/gui/src-tauri/icons
|
||||
- run: cargo check --manifest-path apps/gui/src-tauri/Cargo.toml
|
||||
|
||||
@@ -6,15 +6,21 @@ name: Release
|
||||
#
|
||||
# O CLI é só para Linux (x86_64 e aarch64), compilado no runner Linux; o x86_64
|
||||
# por compilação cruzada. Windows e macOS têm só o app desktop.
|
||||
#
|
||||
# Assinatura do atualizador (opcional): SABIA_TAURI_SIGNING_PRIVATE_KEY e
|
||||
# SABIA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD, com os nomes antigos sem prefixo como
|
||||
# fallback. Sem chave, o build continua e apenas não gera .sig.
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# Toolchain e registry do cargo ficam no volume que sobrevive entre jobs.
|
||||
# Toolchain, caches e registry do cargo ficam no volume que sobrevive entre jobs.
|
||||
CARGO_HOME: /opt/hostedtoolcache/cargo
|
||||
RUSTUP_HOME: /opt/hostedtoolcache/rustup
|
||||
NPM_CONFIG_CACHE: /opt/hostedtoolcache/npm-cache
|
||||
XWIN_CACHE_DIR: /opt/hostedtoolcache/xwin-cache
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -46,7 +52,7 @@ jobs:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release-cli-${{ matrix.platform }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -60,9 +66,7 @@ jobs:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Linker e ferramentas desta plataforma
|
||||
if: ${{ matrix.apt != '' }}
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq ${{ matrix.apt }}
|
||||
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/cli-x86_64 ${{ matrix.apt }}
|
||||
- name: Compila e empacota
|
||||
run: scripts/package-cli.sh "${{ gitea.ref_name }}" "${{ matrix.platform }}" "${{ matrix.target }}" | tee packages.txt
|
||||
- name: Anexa à release
|
||||
@@ -76,26 +80,47 @@ jobs:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release-gui
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release-gui-${{ matrix.platform }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.SABIA_TAURI_SIGNING_PRIVATE_KEY || secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.SABIA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [linux, windows]
|
||||
platform: [linux-aarch64, linux-x86_64, windows]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Dependências do Tauri (Linux)
|
||||
if: matrix.platform == 'linux'
|
||||
- name: Dependências do Tauri (Linux aarch64)
|
||||
if: matrix.platform == 'linux-aarch64'
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Dependências do Tauri (Linux x86_64)
|
||||
if: matrix.platform == 'linux-x86_64'
|
||||
timeout-minutes: 25
|
||||
run: |
|
||||
if [ "$(uname -m)" = aarch64 ]; then
|
||||
dpkg --add-architecture amd64
|
||||
. /etc/os-release
|
||||
if [ "${ID:-}" = ubuntu ]; then
|
||||
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
|
||||
grep -q '^Architectures:' /etc/apt/sources.list.d/ubuntu.sources || sed -i '/^Types: deb/a Architectures: arm64' /etc/apt/sources.list.d/ubuntu.sources
|
||||
printf 'Types: deb\nURIs: http://archive.ubuntu.com/ubuntu/\nSuites: %s %s-updates %s-security\nComponents: main universe\nArchitectures: amd64\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' "$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" >/etc/apt/sources.list.d/amd64.sources
|
||||
elif [ -f /etc/apt/sources.list ]; then
|
||||
sed -i 's/^deb http/deb [arch=arm64] http/' /etc/apt/sources.list
|
||||
printf 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s main universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s-updates main universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s-security main universe\n' "$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" >/etc/apt/sources.list.d/amd64.list
|
||||
fi
|
||||
fi
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-amd64 gcc-x86-64-linux-gnu g++-x86-64-linux-gnu pkg-config patchelf file squashfs-tools qemu-user libglib2.0-bin libwebkit2gtk-4.1-dev:amd64 libayatana-appindicator3-dev:amd64 librsvg2-dev:amd64
|
||||
else
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
fi
|
||||
- name: Ferramentas para compilação cruzada (Windows)
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq nsis lld llvm clang
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-win nsis lld llvm clang
|
||||
command -v cargo-xwin >/dev/null || cargo install cargo-xwin --locked
|
||||
- name: Compila e empacota
|
||||
run: scripts/build-gui.sh "${{ matrix.platform }}"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# Antes de cada push: a verificação rápida. Ignore com `git push --no-verify` (use com parcimônia).
|
||||
exec "$(git rev-parse --show-toplevel)/scripts/verificar.sh"
|
||||
@@ -1,151 +0,0 @@
|
||||
name: Release
|
||||
|
||||
# Publica uma release pública ao empurrar uma tag vX.Y.Z (ex.: git tag v0.1.0 && git push --tags)
|
||||
on:
|
||||
push:
|
||||
tags: ["v*.*.*"]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
name: Cria a release
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
release_id: ${{ steps.rel.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Notas da versão (do CHANGELOG)
|
||||
id: notes
|
||||
run: |
|
||||
v="${GITHUB_REF_NAME#v}"
|
||||
v="${v%%-*}"
|
||||
awk -v v="$v" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md > notes.md
|
||||
[ -s notes.md ] || echo "Veja o CHANGELOG." > notes.md
|
||||
- uses: actions/github-script@v9
|
||||
id: rel
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const tag = context.ref.replace('refs/tags/', '');
|
||||
const r = await github.rest.repos.createRelease({
|
||||
owner: context.repo.owner, repo: context.repo.repo, tag_name: tag,
|
||||
name: `Sabiá ${tag}`, body: fs.readFileSync('notes.md', 'utf8'),
|
||||
draft: true, prerelease: tag.includes('-'),
|
||||
});
|
||||
return r.data.id;
|
||||
|
||||
cli:
|
||||
name: CLI (${{ matrix.name }})
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x86_64, os: ubuntu-24.04, target: x86_64-unknown-linux-gnu }
|
||||
- { name: linux-aarch64, os: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu }
|
||||
- { name: macos-aarch64, os: macos-latest, target: aarch64-apple-darwin }
|
||||
- { name: macos-x86_64, os: macos-latest, target: x86_64-apple-darwin }
|
||||
- { name: windows-x86_64, os: windows-latest, target: x86_64-pc-windows-msvc }
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo build --release --locked -p sabia-cli --target ${{ matrix.target }}
|
||||
- name: Empacota
|
||||
shell: bash
|
||||
run: |
|
||||
name="sabia-${GITHUB_REF_NAME}-${{ matrix.name }}"
|
||||
mkdir "$name"
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
cp target/${{ matrix.target }}/release/sabia.exe "$name/"
|
||||
else
|
||||
cp target/${{ matrix.target }}/release/sabia "$name/"
|
||||
fi
|
||||
cp README.md LICENSE-MIT LICENSE-APACHE "$name/"
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
7z a "$name.zip" "$name" >/dev/null; asset="$name.zip"
|
||||
else
|
||||
tar czf "$name.tar.gz" "$name"; asset="$name.tar.gz"
|
||||
fi
|
||||
if command -v sha256sum >/dev/null; then sha256sum "$asset" > "$asset.sha256"; else shasum -a 256 "$asset" > "$asset.sha256"; fi
|
||||
echo "ASSET=$asset" >> "$GITHUB_ENV"
|
||||
- name: Envia para a release
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release upload "$GITHUB_REF_NAME" "$ASSET" "$ASSET.sha256" --clobber
|
||||
|
||||
gui:
|
||||
name: App desktop (${{ matrix.name }})
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x86_64, os: ubuntu-24.04, args: "" }
|
||||
- { name: macos-aarch64, os: macos-latest, args: "--target aarch64-apple-darwin" }
|
||||
- { name: macos-x86_64, os: macos-latest, args: "--target x86_64-apple-darwin" }
|
||||
- { name: windows-x86_64, os: windows-latest, args: "" }
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ contains(matrix.args, 'apple') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: apps/gui/src-tauri
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cache dos pacotes do Tauri (.deb)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/apt-cache
|
||||
key: apt-tauri-${{ runner.os }}-${{ runner.arch }}-${{ env.ImageVersion }}-${{ hashFiles('scripts/apt-install.sh') }}
|
||||
- name: Dependências do Tauri (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Gera os ícones
|
||||
run: npx --yes @tauri-apps/cli@2 icon apps/gui/src-tauri/icons/source.png -o apps/gui/src-tauri/icons
|
||||
- uses: tauri-apps/tauri-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
projectPath: apps/gui/src-tauri
|
||||
releaseId: ${{ needs.create-release.outputs.release_id }}
|
||||
args: ${{ matrix.args }}
|
||||
|
||||
publish:
|
||||
name: Publica a release
|
||||
needs: [create-release, cli, gui]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Notas da versão (do CHANGELOG)
|
||||
run: |
|
||||
v="${GITHUB_REF_NAME#v}"
|
||||
v="${v%%-*}"
|
||||
awk -v v="$v" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md > notes.md
|
||||
[ -s notes.md ] || echo "Veja o CHANGELOG." > notes.md
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner, repo: context.repo.repo,
|
||||
release_id: ${{ needs.create-release.outputs.release_id }},
|
||||
name: `Sabiá ${context.ref.replace('refs/tags/', '')}`,
|
||||
body: fs.readFileSync('notes.md', 'utf8'),
|
||||
draft: false,
|
||||
});
|
||||
@@ -1,13 +1,21 @@
|
||||
/target
|
||||
# Build
|
||||
target/
|
||||
apps/gui/src-tauri/target/
|
||||
node_modules/
|
||||
*.img
|
||||
*.guara-session.json
|
||||
.DS_Store
|
||||
**/*.rs.bk
|
||||
|
||||
# Capturas e estado local do Sabiá
|
||||
*.pcap
|
||||
*.cap
|
||||
.sabia-audit.log
|
||||
node_modules/
|
||||
apps/gui/src-tauri/target
|
||||
|
||||
# Configuração e memória local de agentes de IA — não faz parte do
|
||||
# produto, fica só na máquina de desenvolvimento.
|
||||
# Logo copiada para a interface pelo build.rs
|
||||
/apps/gui/ui/logo.png
|
||||
|
||||
# Configuração e memória local de agentes de IA — não faz parte do produto
|
||||
.claude/
|
||||
.wolf/
|
||||
.opencode/
|
||||
@@ -19,6 +27,16 @@ CLAUDE.md
|
||||
AGENTS.md
|
||||
GEMINI.md
|
||||
|
||||
# Variáveis de ambiente locais (o setup do Superset copia da raiz)
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Lixo local
|
||||
*.eml
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Pacotes gerados por scripts/package-cli.sh
|
||||
/guara-v*
|
||||
/sabia-v*
|
||||
/dist-gui
|
||||
@@ -11,25 +11,18 @@ Thanks for helping! Golden rules:
|
||||
## Workflow
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy --workspace --all-targets # as in CI (RUSTFLAGS=-D warnings)
|
||||
cargo test --workspace
|
||||
node scripts/check-ui-i18n.mjs # UI translation coverage
|
||||
scripts/instalar-hooks.sh # once: runs the quick check before every push
|
||||
scripts/verificar.sh # fast (seconds): fmt, clippy as in CI, i18n, scripts, workflows
|
||||
scripts/verificar.sh --completo # adds cargo test --workspace and cargo audit
|
||||
```
|
||||
|
||||
CI runs on Linux only (Gitea has one runner); run these before pushing — almost everything that fails CI shows up here in seconds. Documentation-only changes (`*.md`, `docs/`) are light on CI.
|
||||
CI runs on Linux; run the check before pushing: almost everything that fails CI shows up here in seconds. Documentation-only changes (`*.md`, `docs/`) skip CI.
|
||||
|
||||
Live capture exists only on Linux (`AF_PACKET` + netlink); the code behind Windows and macOS paths still compiles and is covered by `cargo check`, but neither runs in CI nor is built for release.
|
||||
Live capture exists only on Linux (`AF_PACKET` + netlink); the code behind Windows and macOS paths still compiles and is covered by `cargo check`.
|
||||
|
||||
## Releases
|
||||
|
||||
Pushing a `v*` tag makes the Gitea CI create the release (notes come from `CHANGELOG.md`, so update it first) and attach the Linux CLI (x86_64 and aarch64) and the desktop app for Linux **aarch64** and Windows. The runner is arm64, so the Linux **x86_64** desktop app comes from an x86_64 Linux machine:
|
||||
|
||||
```bash
|
||||
scripts/build-gui.sh linux # AppImage + .deb in dist-gui/
|
||||
API=https://git.lucasrguerra.dev.br/api/v1/repos/lucasrguerra/sabia TOKEN=... \
|
||||
scripts/release-upload.sh vX.Y.Z dist-gui/*
|
||||
```
|
||||
Pushing a `v*` tag makes the Gitea CI create the release (notes come from `CHANGELOG.md`, so update it first) and attach the Linux CLI (x86_64 and aarch64) and the desktop app for Linux (x86_64 and aarch64) and Windows.
|
||||
|
||||
There is no macOS build: it needs Apple's SDK and code signing, which a Linux runner cannot provide.
|
||||
|
||||
|
||||
@@ -11,25 +11,18 @@ Obrigado por ajudar! Regras de ouro:
|
||||
## Fluxo
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy --workspace --all-targets # como no CI (RUSTFLAGS=-D warnings)
|
||||
cargo test --workspace
|
||||
node scripts/check-ui-i18n.mjs # cobertura da tradução da interface
|
||||
scripts/instalar-hooks.sh # uma vez: roda a verificação rápida antes de cada push
|
||||
scripts/verificar.sh # rápido (segundos): fmt, clippy como no CI, tradução, scripts, workflows
|
||||
scripts/verificar.sh --completo # inclui cargo test --workspace e cargo audit
|
||||
```
|
||||
|
||||
O CI roda só em Linux (o Gitea tem um único runner); rode esses comandos antes do push — quase tudo que faz o CI falhar aparece aqui em segundos. Mudanças só de documentação (`*.md`, `docs/`) são leves no CI.
|
||||
O CI roda em Linux; rode a verificação antes do push: quase tudo que faz o CI falhar aparece aqui em segundos. Mudanças só de documentação (`*.md`, `docs/`) pulam o CI.
|
||||
|
||||
A captura ao vivo só existe no Linux (`AF_PACKET` + netlink); o código dos caminhos de Windows e macOS continua compilando e é coberto pelo `cargo check`, mas não roda no CI nem é compilado para release. são offline.
|
||||
A captura ao vivo só existe no Linux (`AF_PACKET` + netlink); o código dos caminhos de Windows e macOS continua compilando e é coberto pelo `cargo check`.
|
||||
|
||||
## Releases
|
||||
|
||||
Enviar uma tag `v*` faz o CI do Gitea criar a release (as notas vêm do `CHANGELOG.md`, então atualize-o antes) e anexar o CLI para Linux (x86_64 e aarch64) e o app desktop para Linux **aarch64** e Windows. O runner é arm64, então o app Linux **x86_64** vem de uma máquina Linux x86_64:
|
||||
|
||||
```bash
|
||||
scripts/build-gui.sh linux # AppImage + .deb em dist-gui/
|
||||
API=https://git.lucasrguerra.dev.br/api/v1/repos/lucasrguerra/sabia TOKEN=... \
|
||||
scripts/release-upload.sh vX.Y.Z dist-gui/*
|
||||
```
|
||||
Enviar uma tag `v*` faz o CI do Gitea criar a release (as notas vêm do `CHANGELOG.md`, então atualize-o antes) e anexar o CLI para Linux (x86_64 e aarch64) e o app desktop para Linux (x86_64 e aarch64) e Windows.
|
||||
|
||||
Não há build para macOS: ele exige o SDK e a assinatura da Apple, que um runner Linux não tem.
|
||||
|
||||
|
||||
@@ -15,3 +15,7 @@ lto = "thin"
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
strip = true
|
||||
panic = "abort"
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
@@ -1,8 +1,8 @@
|
||||
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
|
||||
<p align="center"><img src="logo.png" width="300" alt="Sabiá"></p>
|
||||
<h1 align="center">Sabiá</h1>
|
||||
<p align="center">Free and open Wi-Fi auditing.<br>Terminal and desktop app · live capture on Linux, offline analysis on Windows too · authorized use.</p>
|
||||
|
||||
<p align="center"><a href="docs/cli.md">Terminal</a> · <a href="docs/interface.md">Interface</a> · <a href="docs/architecture.md">Architecture</a> · <a href="ROADMAP.md">Roadmap</a> · <a href="CHANGELOG.md">Changelog</a></p>
|
||||
<p align="center"><a href="docs/cli.md">Terminal</a> · <a href="docs/interface.md">Interface</a> · <a href="docs/architecture.md">Architecture</a> · <a href="docs/support.md">Support</a> · <a href="ROADMAP.md">Roadmap</a> · <a href="CHANGELOG.md">Changelog</a></p>
|
||||
|
||||
<p align="center"><b>English</b> · <a href="README.pt-BR.md">Português</a></p>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
|
||||
<p align="center"><img src="logo.png" width="300" alt="Sabiá"></p>
|
||||
<h1 align="center">Sabiá</h1>
|
||||
<p align="center">Auditoria de Wi-Fi livre e gratuita.<br>Terminal e interface gráfica · captura no Linux, análise offline também em Windows · uso autorizado.</p>
|
||||
|
||||
<p align="center"><a href="docs/pt-BR/cli.md">Terminal</a> · <a href="docs/pt-BR/interface.md">Interface</a> · <a href="docs/pt-BR/arquitetura.md">Arquitetura</a> · <a href="ROADMAP.pt-BR.md">Roadmap</a> · <a href="CHANGELOG.pt-BR.md">Changelog</a></p>
|
||||
<p align="center"><a href="docs/pt-BR/cli.md">Terminal</a> · <a href="docs/pt-BR/interface.md">Interface</a> · <a href="docs/pt-BR/arquitetura.md">Arquitetura</a> · <a href="docs/pt-BR/suporte.md">Suporte</a> · <a href="ROADMAP.pt-BR.md">Roadmap</a> · <a href="CHANGELOG.pt-BR.md">Changelog</a></p>
|
||||
|
||||
<p align="center"><a href="README.md">English</a> · <b>Português</b></p>
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
**English** · [Português](SECURITY.pt-BR.md)
|
||||
|
||||
Sabiá processes untrusted data from the air and from `.pcap` files (802.11 frames and handshakes, possibly forged) and controls network interfaces with root privilege. Vulnerabilities — memory corruption in the radiotap/802.11/EAPOL parsers, path escape when writing `.pcap` or exporting, privilege misuse — should be reported privately through the repository's **Security → Report a vulnerability** tab on GitHub. Do not open a public issue before a fix.
|
||||
Sabiá processes untrusted data from the air and from `.pcap` files (802.11 frames and handshakes, possibly forged) and controls network interfaces with root privilege. Vulnerabilities — memory corruption in the radiotap/802.11/EAPOL parsers, path escape when writing `.pcap` or exporting, privilege misuse — should be reported privately through the private vulnerability reporting feature of the repository on Gitea. Do not open a public issue before a fix.
|
||||
|
||||
Sabiá is a tool for **authorized auditing**. Reports about using it to attack third-party networks are out of the project's security scope — see the **Authorized use** section of the [README](README.md).
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
[English](SECURITY.md) · **Português**
|
||||
|
||||
O Sabiá processa dados não confiáveis vindos do ar e de arquivos `.pcap` (quadros 802.11 e handshakes, possivelmente forjados) e controla interfaces de rede com privilégio de root. Vulnerabilidades — corrupção de memória nos parsers de radiotap/802.11/EAPOL, escape de caminho na escrita de `.pcap` ou na exportação, uso indevido de privilégio — devem ser reportadas em privado pela aba **Security → Report a vulnerability** do repositório no GitHub. Não abra issue pública antes da correção.
|
||||
O Sabiá processa dados não confiáveis vindos do ar e de arquivos `.pcap` (quadros 802.11 e handshakes, possivelmente forjados) e controla interfaces de rede com privilégio de root. Vulnerabilidades — corrupção de memória nos parsers de radiotap/802.11/EAPOL, escape de caminho na escrita de `.pcap` ou na exportação, uso indevido de privilégio — devem ser reportadas em privado pelo recurso de relato privado de vulnerabilidade do repositório no Gitea. Não abra issue pública antes da correção.
|
||||
|
||||
O Sabiá é uma ferramenta de **auditoria autorizada**. Relatos sobre uso para atacar redes de terceiros não são escopo de segurança do projeto — veja a seção **Uso autorizado** do [README](README.pt-BR.md).
|
||||
@@ -6,6 +6,7 @@ description = "Sabiá: interface gráfica de auditoria de Wi-Fi"
|
||||
version = "1.0.3"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["Sabiá contributors"]
|
||||
repository = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia"
|
||||
rust-version = "1.80"
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../../../logo.png");
|
||||
let _ = std::fs::copy("../../../logo.png", "../ui/logo.png");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Modo monitor e captura crua exigem administrador no Windows: o
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Permissões da janela principal",
|
||||
"windows": ["main"],
|
||||
"permissions": ["core:default", "core:window:allow-destroy", "core:window:allow-close"]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{}
|
||||
{"default":{"identifier":"default","description":"Permissões da janela principal","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-destroy","core:window:allow-close"]}}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 979 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 350 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Sabia",
|
||||
"productName": "Sabiá",
|
||||
"version": "1.0.3",
|
||||
"identifier": "dev.sabia.app",
|
||||
"build": {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Sabiá</title>
|
||||
<link rel="icon" href="/sabia.png">
|
||||
<link rel="icon" href="/logo.png">
|
||||
<link rel="stylesheet" href="/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<img class="logo" src="/sabia.png" alt="">
|
||||
<img class="logo" src="/logo.png" alt="">
|
||||
<div>
|
||||
<strong>Sabiá</strong>
|
||||
<small>auditoria de Wi-Fi</small>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2024 Paged.js contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Paged.js
|
||||
|
||||
`paged.polyfill.min.js` é o polyfill do [Paged.js](https://pagedjs.org/), versão
|
||||
0.4.3, usado para paginar os relatórios em A4 no navegador.
|
||||
|
||||
- Projeto: https://gitlab.coko.foundation/pagedjs/pagedjs
|
||||
- Licença: MIT (texto em `PAGEDJS-LICENSE`)
|
||||
- Arquivo: `paged.polyfill.min.js` (build de distribuição, não modificado)
|
||||
|
||||
O arquivo é vendorizado para que o relatório funcione offline, sem CDN.
|
||||
@@ -17,8 +17,8 @@ const APP_CSS: &str = include_str!("../../../../apps/gui/ui/app.css");
|
||||
const APP_JS: &str = include_str!("../../../../apps/gui/ui/app.js");
|
||||
const I18N_JS: &str = include_str!("../../../../apps/gui/ui/i18n.js");
|
||||
const I18N_EN_JS: &str = include_str!("../../../../apps/gui/ui/i18n-en.js");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../../apps/gui/ui/sabia.png");
|
||||
const PAGED_JS: &str = include_str!("../../../../apps/gui/ui/paged.polyfill.min.js");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../../logo.png");
|
||||
const PAGED_JS: &str = include_str!("../../../../apps/gui/ui/vendor/paged.polyfill.min.js");
|
||||
|
||||
pub fn serve(port: u16, audit_path: std::path::PathBuf, open: bool) -> sabia_core::Result<()> {
|
||||
let token = gen_token();
|
||||
@@ -275,7 +275,7 @@ fn handle(
|
||||
}
|
||||
|
||||
match req.path.as_str() {
|
||||
"/paged.polyfill.min.js" => {
|
||||
"/vendor/paged.polyfill.min.js" | "/paged.polyfill.min.js" => {
|
||||
return respond(
|
||||
&mut stream,
|
||||
200,
|
||||
@@ -308,7 +308,7 @@ fn handle(
|
||||
I18N_EN_JS.as_bytes(),
|
||||
)
|
||||
}
|
||||
"/sabia.png" => return respond(&mut stream, 200, "image/png", LOGO_PNG),
|
||||
"/logo.png" => return respond(&mut stream, 200, "image/png", LOGO_PNG),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,12 @@ pub struct ReportOptions {
|
||||
}
|
||||
|
||||
const PAGED_MARKER: &str = "<!--SABIA_PAGEDJS-->";
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../apps/gui/ui/sabia.png");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../logo.png");
|
||||
const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/// Embute o logo como `data:` URI — sem isso o relatório standalone (modo
|
||||
/// offline, pensado pra abrir sem servidor nenhum) teria um `<img
|
||||
/// src="/sabia.png">` que nunca resolve fora do `sabia ui`. Pequeno o
|
||||
/// src="/logo.png">` que nunca resolve fora do `sabia ui`. Pequeno o
|
||||
/// bastante (poucos KB) pra não valer a pena puxar uma crate de base64 só
|
||||
/// por isso.
|
||||
fn base64(bytes: &[u8]) -> String {
|
||||
@@ -502,7 +502,7 @@ pub fn render(model: &ReportModel, options: ReportOptions) -> String {
|
||||
let paged = if options.inline_pagedjs {
|
||||
PAGED_MARKER.to_string()
|
||||
} else {
|
||||
"<script src=\"/paged.polyfill.min.js\"></script>".to_string()
|
||||
"<script src=\"/vendor/paged.polyfill.min.js\"></script>".to_string()
|
||||
};
|
||||
let stats = &model.stats;
|
||||
let crackable = model.handshakes.iter().filter(|h| h.crackable).count();
|
||||
@@ -765,7 +765,7 @@ mod tests {
|
||||
m.title = "Rede <x>".into();
|
||||
let html = render(&m, ReportOptions::default());
|
||||
assert!(html.contains("Rede <x>"));
|
||||
assert!(html.contains("paged.polyfill.min.js"));
|
||||
assert!(html.contains("/vendor/paged.polyfill.min.js"));
|
||||
assert!(html.contains("@page"));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Ataque de PIN WPS com o `reaver` embutido.
|
||||
//!
|
||||
//! O protocolo EAP-WSC é grande demais para reimplementar aqui; o Sabiá
|
||||
//! carrega uma cópia do reaver (GPL-2, veja `assets/tools/LICENSE.reaver`),
|
||||
//! carrega uma cópia do reaver (GPL-2, veja `crates/sabia-core/assets/tools/LICENSE.reaver`),
|
||||
//! extrai em tempo de execução e mostra a saída ao vivo. Assim o WPS já
|
||||
//! funciona sem instalar nada. Se o usuário tiver um `bully` no PATH, ele
|
||||
//! também aparece como alternativa.
|
||||
@@ -17,7 +17,7 @@ pub struct WpsTool {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const BUNDLED_REAVER: &[u8] = include_bytes!("../../../assets/tools/reaver");
|
||||
const BUNDLED_REAVER: &[u8] = include_bytes!("../assets/tools/reaver");
|
||||
|
||||
/// Procura um binário no `PATH` — usado pelo WPS (reaver/bully) e, fora
|
||||
/// deste módulo, pelo Evil Twin (hostapd/dnsmasq), que precisa da mesma
|
||||
|
||||
@@ -28,13 +28,13 @@ The CLI and the desktop app are thin clients: both call the core's `Engine`. The
|
||||
| `pcapreader.rs`, `pcapwriter.rs` | `.pcap` read and write. |
|
||||
| `audit.rs` | Local hash-chained audit trail. |
|
||||
| `doctor.rs` | Diagnostics of privileges, tools and interfaces. |
|
||||
| `update.rs` | Self-update from GitHub releases (curl + SHA-256). |
|
||||
| `update.rs` | Self-update from Gitea releases (curl + SHA-256). |
|
||||
| `error.rs`, `testutil.rs` | Shared error type; test helpers. |
|
||||
|
||||
## Platform
|
||||
|
||||
Capture and monitor mode are **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), cracking (`crack`) and `.pcap` are cross-platform, being offline — which is why the tests run on Linux, Windows and macOS in CI.
|
||||
Capture and monitor mode are **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), cracking (`crack`) and `.pcap` are cross-platform, being offline — which is why the tests run on Linux in CI.
|
||||
|
||||
## UI transport
|
||||
|
||||
`sabia ui` starts a server on `127.0.0.1` protected by a token generated on the spot; it serves `index.html`/`app.css`/`app.js`/`sabia.png` embedded in the binary plus a JSON API. The Tauri app uses the same files, calling the `Engine` directly via Tauri commands instead of HTTP.
|
||||
`sabia ui` starts a server on `127.0.0.1` protected by a token generated on the spot; it serves `index.html`/`app.css`/`app.js`/`logo.png` embedded in the binary plus a JSON API. The Tauri app uses the same files, calling the `Engine` directly via Tauri commands instead of HTTP.
|
||||
@@ -28,13 +28,13 @@ A CLI e o app desktop são clientes finos: ambos chamam o `Engine` do núcleo. A
|
||||
| `pcapreader.rs`, `pcapwriter.rs` | Leitura e escrita de `.pcap`. |
|
||||
| `audit.rs` | Trilha de auditoria local encadeada por hash. |
|
||||
| `doctor.rs` | Diagnóstico de privilégios, ferramentas e interfaces. |
|
||||
| `update.rs` | Auto-atualização por releases do GitHub (curl + SHA-256). |
|
||||
| `update.rs` | Auto-atualização por releases do Gitea (curl + SHA-256). |
|
||||
| `error.rs`, `testutil.rs` | Tipo de erro compartilhado; ajudantes de teste. |
|
||||
|
||||
## Plataforma
|
||||
|
||||
Captura e modo monitor são **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), quebra (`crack`) e `.pcap` são multiplataforma, por serem offline — por isso os testes rodam em Linux, Windows e macOS no CI.
|
||||
Captura e modo monitor são **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), quebra (`crack`) e `.pcap` são multiplataforma, por serem offline — por isso os testes rodam em Linux no CI.
|
||||
|
||||
## Transporte da UI
|
||||
|
||||
`sabia ui` sobe um servidor em `127.0.0.1` protegido por um token gerado na hora; serve `index.html`/`app.css`/`app.js`/`sabia.png` embutidos no binário e uma API JSON. O app Tauri usa os mesmos arquivos, chamando o `Engine` direto por comandos Tauri em vez de HTTP.
|
||||
`sabia ui` sobe um servidor em `127.0.0.1` protegido por um token gerado na hora; serve `index.html`/`app.css`/`app.js`/`logo.png` embutidos no binário e uma API JSON. O app Tauri usa os mesmos arquivos, chamando o `Engine` direto por comandos Tauri em vez de HTTP.
|
||||
@@ -0,0 +1,67 @@
|
||||
# O que o Sabiá suporta
|
||||
|
||||
[English](../support.md) · **Português**
|
||||
|
||||
Matriz do que existe hoje (versão 1.0.3). Para o que vem a seguir, veja o [ROADMAP](../../ROADMAP.pt-BR.md).
|
||||
|
||||
## Plataformas
|
||||
|
||||
| | Descoberta e captura | Análise e quebra offline | App desktop |
|
||||
|---|---|---|---|
|
||||
| Linux | ✅ root, adaptador em modo monitor | ✅ | ✅ AppImage e deb |
|
||||
| Windows | ❌ | ✅ `.pcap`, dicionário/regras/máscara | ✅ instalador |
|
||||
| macOS | ❌ | ✅ `.pcap`, dicionário/regras/máscara | ❌ sem build |
|
||||
|
||||
A captura ao vivo usa `AF_PACKET` e netlink; exige Linux, root (ou `CAP_NET_ADMIN`/`CAP_NET_RAW`) e uma placa com modo monitor. Injeção (`deauth`, Evil Twin) exige uma placa que também injete. O app desktop pede elevação ao abrir.
|
||||
|
||||
## Descoberta
|
||||
|
||||
- Varredura dos canais de **2.4, 5 e 6 GHz**, limitada pela placa e pelo _regulatory domain_.
|
||||
- **Pontos de acesso e clientes** com SSID, BSSID, canal, sinal, tipo de segurança, detalhes de WPS e fabricante.
|
||||
- **Modo monitor** ligado/desligado por netlink/`iw`, com recuperação automática se o NetworkManager retomar a interface.
|
||||
- **Canais suportados** informados por rádio.
|
||||
|
||||
## Captura e análise
|
||||
|
||||
| Item | Suporte |
|
||||
|---|---|
|
||||
| 4-way handshake (EAPOL) | ✅ detectado e guardado para quebra offline |
|
||||
| PMKID | ✅ detectado e guardado, inclusive caça sem cliente |
|
||||
| Escrita e leitura de `.pcap` | ✅ compatível com as outras ferramentas 802.11 |
|
||||
| Decifração CCMP | ✅ para um cliente cuja PSK já é conhecida |
|
||||
| Parsers radiotap / 802.11 / EAPOL | ✅ |
|
||||
|
||||
## Quebra (WPA/WPA2-PSK)
|
||||
|
||||
| Modo | Suporte |
|
||||
|---|---|
|
||||
| Dicionário | ✅ rockyou + Probable-Wordlists por padrão (9,7 milhões de senhas) |
|
||||
| Regras de mutação | ✅ estilo hashcat |
|
||||
| Máscara/força bruta | ✅ `?d?l?u?s?a` |
|
||||
| Verificação | ✅ PMK por PBKDF2-HMAC-SHA1 + MIC (4-way) ou PMKID |
|
||||
| Exportação | ✅ `.hc22000` para hashcat/John |
|
||||
|
||||
WPA3/SAE **não** cai por dicionário de handshake; o escopo da quebra é WPA/WPA2-PSK.
|
||||
|
||||
## Ataques autorizados
|
||||
|
||||
- **Desautenticação** direcionada ou contínua, com `--yes` / confirmação explícita de autorização.
|
||||
- **Teste de PMF** e **caça de PMKID sem cliente**.
|
||||
- **WPS PIN / Pixie Dust** com o `reaver` embutido (GPL-2) ou `bully` quando instalado.
|
||||
- **Evil Twin PSK**: clona um AP visto com portal cativo; a senha só é aceita depois de conferida contra o handshake real já capturado.
|
||||
- **Evil Twin Enterprise** (802.1X/EAP): captura credenciais automaticamente, sem portal. Exige `hostapd-wpe` e uma segunda interface Wi-Fi em modo AP.
|
||||
|
||||
## Relatórios, auditoria e diagnóstico
|
||||
|
||||
- **Relatório de auditoria paginado** (capa, sumário, achados, pontuação de canais, trilha de auditoria), gerado localmente.
|
||||
- **Trilha de auditoria encadeada por hash** para toda ativação de modo monitor e sessão de captura (`sabia audit show`).
|
||||
- **`sabia doctor`** confere privilégios, ferramentas e interfaces antes da captura.
|
||||
|
||||
## O que o Sabiá não faz
|
||||
|
||||
- Quebrar handshake WPA3/SAE, ou recuperar senha sem handshake/PMKID capturado e uma candidata correspondente.
|
||||
- Capturar no Windows ou no macOS; nessas plataformas só há análise offline.
|
||||
- Dispensar a confirmação de autorização para `deauth`, WPS ou Evil Twin.
|
||||
- Garantir cobertura de 5/6 GHz: depende da placa, do _regulatory domain_ e do driver.
|
||||
- Fazer um cliente enterprise confiar num certificado autoassinado quando ele valida a cadeia (comum em perfil corporativo gerenciado).
|
||||
- Acelerar a quebra por GPU ainda; a quebra é feita na CPU.
|
||||
@@ -0,0 +1,67 @@
|
||||
# What Sabiá supports
|
||||
|
||||
[English](support.md) · [Português](pt-BR/suporte.md)
|
||||
|
||||
Matrix of what exists today (version 1.0.3). For what comes next, see the [ROADMAP](../ROADMAP.md).
|
||||
|
||||
## Platforms
|
||||
|
||||
| | Discovery and capture | Offline analysis and cracking | Desktop app |
|
||||
|---|---|---|---|
|
||||
| Linux | ✅ root, adapter in monitor mode | ✅ | ✅ AppImage and deb |
|
||||
| Windows | ❌ | ✅ `.pcap`, wordlist/rules/mask | ✅ installer |
|
||||
| macOS | ❌ | ✅ `.pcap`, wordlist/rules/mask | ❌ no build |
|
||||
|
||||
Live capture uses `AF_PACKET` and netlink; it needs Linux, root (or `CAP_NET_ADMIN`/`CAP_NET_RAW`) and an adapter that supports monitor mode. Injection (`deauth`, Evil Twin) needs an adapter that also supports injection. The desktop app asks for elevation on startup.
|
||||
|
||||
## Discovery
|
||||
|
||||
- **2.4, 5 and 6 GHz** channel sweep, limited by the adapter and the regulatory domain.
|
||||
- **Access points and clients** with SSID, BSSID, channel, signal, security type, WPS details and vendor.
|
||||
- **Monitor mode** on/off through netlink/`iw`, with automatic recovery if NetworkManager takes the interface back.
|
||||
- **Supported channels** reported per radio.
|
||||
|
||||
## Capture and analysis
|
||||
|
||||
| Item | Support |
|
||||
|---|---|
|
||||
| 4-way handshake (EAPOL) | ✅ detected and stored for offline cracking |
|
||||
| PMKID | ✅ detected and stored, including clientless PMKID hunting |
|
||||
| `.pcap` write and read | ✅ compatible with other 802.11 tools |
|
||||
| CCMP decryption | ✅ for a client whose PSK is already known |
|
||||
| Radiotap / 802.11 / EAPOL parsing | ✅ |
|
||||
|
||||
## Cracking (WPA/WPA2-PSK)
|
||||
|
||||
| Mode | Support |
|
||||
|---|---|
|
||||
| Dictionary | ✅ rockyou + Probable-Wordlists by default (9.7 million passwords) |
|
||||
| Mutation rules | ✅ hashcat-style rules |
|
||||
| Mask/brute force | ✅ `?d?l?u?s?a` |
|
||||
| Verification | ✅ PBKDF2-HMAC-SHA1 PMK + MIC (4-way) or PMKID |
|
||||
| Export | ✅ `.hc22000` for hashcat/John |
|
||||
|
||||
WPA3/SAE does **not** fall to a handshake dictionary attack; the cracking scope is WPA/WPA2-PSK.
|
||||
|
||||
## Authorized attacks
|
||||
|
||||
- **Deauthentication** directed or continuous, with `--yes` / explicit authorization checkbox.
|
||||
- **PMF test** and **clientless PMKID hunt**.
|
||||
- **WPS PIN / Pixie Dust** using the bundled `reaver` (GPL-2) or `bully` when installed.
|
||||
- **Evil Twin PSK**: clones a seen AP with a captive portal; the password is only accepted after being checked against the real captured handshake.
|
||||
- **Evil Twin Enterprise** (802.1X/EAP): captures credentials automatically, without a portal. It needs `hostapd-wpe` and a second Wi-Fi interface in AP mode.
|
||||
|
||||
## Reports, audit and diagnostics
|
||||
|
||||
- **Paginated audit report** (cover, table of contents, findings, channel score, audit trail), generated locally.
|
||||
- **Hash-chained audit trail** for every monitor-mode activation and capture session (`sabia audit show`).
|
||||
- **`sabia doctor`** checks privileges, tools and interfaces before capture.
|
||||
|
||||
## What Sabiá does not do
|
||||
|
||||
- Crack WPA3/SAE handshakes, or recover a password without a captured handshake/PMKID and a matching candidate.
|
||||
- Capture on Windows or macOS; those platforms are offline analysis only.
|
||||
- Bypass the authorization requirement for deauth, WPS or Evil Twin.
|
||||
- Guarantee 5/6 GHz coverage: it depends on the adapter, regulatory domain and driver.
|
||||
- Make an enterprise client trust a self-signed certificate when the client validates the chain (common in managed corporate profiles).
|
||||
- Provide GPU acceleration yet; cracking is CPU-bound.
|
||||
@@ -16,15 +16,15 @@ mkdir -p "$cache/partial"
|
||||
trap 'sudo chown -R "$(id -u):$(id -g)" "$cache" 2>/dev/null || true; rm -f "$cache/lock"' EXIT
|
||||
opts=(-o "Dir::Cache::archives=$cache" -y)
|
||||
|
||||
# 1) com .deb em cache: instala direto, sem nem atualizar o índice do apt
|
||||
if ls "$cache"/*.deb >/dev/null 2>&1 && timeout 300 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
# 1) com .deb em cache e índice presente: instala direto, sem nem atualizar o índice do apt
|
||||
if [ -z "$(dpkg --print-foreign-architectures)" ] && ls "$cache"/*.deb >/dev/null 2>&1 && ls /var/lib/apt/lists/*Packages* >/dev/null 2>&1 && timeout 300 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
echo "instalado a partir do cache"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2) sem cache (ou incompleto): atualiza e instala, até 3 tentativas com tempo limite
|
||||
# 2) sem cache (ou multiarch / incompleto): atualiza e instala, até 3 tentativas com tempo limite
|
||||
for tentativa in 1 2 3; do
|
||||
if timeout 240 sudo apt-get update && timeout 600 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
if timeout 240 sudo apt-get update && { [ -z "$(dpkg --print-foreign-architectures)" ] || timeout 600 sudo apt-get "${opts[@]}" upgrade; } && timeout 600 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
echo "instalado (tentativa $tentativa)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -12,15 +12,22 @@
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
platform=${1:?uso: build-gui.sh <linux|windows|windows-docker>}
|
||||
platform=${1:?uso: build-gui.sh <linux|linux-x86_64|linux-aarch64|windows|windows-docker>}
|
||||
tauri() { npx --yes @tauri-apps/cli@2 "$@"; }
|
||||
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ] && [ -f "$HOME/.tauri/sabia-updater.key" ]; then
|
||||
export TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/sabia-updater.key")"
|
||||
[ -f "$HOME/.tauri/sabia-updater.pass" ] && export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat "$HOME/.tauri/sabia-updater.pass")"
|
||||
fi
|
||||
|
||||
if [ "$platform" = windows-docker ]; then
|
||||
docker volume create sabia-cargo >/dev/null
|
||||
docker volume create sabia-rustup >/dev/null
|
||||
mkdir -p dist-gui
|
||||
exec docker run --rm -v "$PWD":/src -v sabia-cargo:/root/.cargo -v sabia-rustup:/root/.rustup \
|
||||
-w /src -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" ubuntu:24.04 bash -c '
|
||||
-w /src -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \
|
||||
-e TAURI_SIGNING_PRIVATE_KEY -e TAURI_SIGNING_PRIVATE_KEY_PASSWORD \
|
||||
ubuntu:24.04 bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
@@ -32,24 +39,104 @@ if [ "$platform" = windows-docker ]; then
|
||||
chown -R "$HOST_UID:$HOST_GID" dist-gui apps/gui/src-tauri/target 2>/dev/null || true'
|
||||
fi
|
||||
|
||||
rm -rf dist-gui
|
||||
mkdir -p dist-gui
|
||||
cd apps/gui/src-tauri
|
||||
|
||||
# Os ícones derivados do original não ficam todos no repositório.
|
||||
[ -f icons/icon.ico ] || tauri icon icons/source.png -o icons
|
||||
|
||||
tauri_blob_cache="$HOME/.cache/tauri"
|
||||
if [ -d /opt/hostedtoolcache ] && [ -w /opt/hostedtoolcache ]; then
|
||||
tauri_blob_cache="/opt/hostedtoolcache/tauri-tools"
|
||||
fi
|
||||
mkdir -p "$tauri_blob_cache" "$HOME/.cache/tauri"
|
||||
|
||||
fetch_cached_tool() {
|
||||
local name=$1 url=$2 patch_elf=${3:-0}
|
||||
local cached="$tauri_blob_cache/$name"
|
||||
if [ ! -s "$cached" ]; then
|
||||
local tmp="$cached.tmp.$$"
|
||||
curl -fsSL "$url" -o "$tmp"
|
||||
if [ "$patch_elf" = 1 ]; then
|
||||
dd if=/dev/zero of="$tmp" bs=1 count=3 seek=8 conv=notrunc 2>/dev/null
|
||||
fi
|
||||
chmod +x "$tmp"
|
||||
mv -f "$tmp" "$cached"
|
||||
fi
|
||||
if [ "$cached" != "$HOME/.cache/tauri/$name" ]; then
|
||||
cp -f "$cached" "$HOME/.cache/tauri/$name"
|
||||
chmod +x "$HOME/.cache/tauri/$name"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$platform" in
|
||||
linux)
|
||||
tauri build --bundles appimage,deb
|
||||
linux|linux-aarch64)
|
||||
bundle="${CARGO_TARGET_DIR:-target}/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --bundles appimage,deb
|
||||
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
|
||||
# O atualizador do Tauri (quando o projeto usa) precisa do .sig ao lado.
|
||||
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
|
||||
;;
|
||||
linux-x86_64)
|
||||
if [ "$(uname -m)" = x86_64 ]; then
|
||||
bundle="${CARGO_TARGET_DIR:-target}/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --bundles appimage,deb
|
||||
else
|
||||
rustup target add x86_64-unknown-linux-gnu >/dev/null
|
||||
mkdir -p /tmp/x86_64-cross-tools "$HOME/.cache/tauri"
|
||||
cat >/tmp/x86_64-cross-tools/ldd <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
exec qemu-x86_64 /lib64/ld-linux-x86-64.so.2 --list "$@"
|
||||
EOF
|
||||
chmod +x /tmp/x86_64-cross-tools/ldd
|
||||
ln -sf /usr/bin/x86_64-linux-gnu-strip /tmp/x86_64-cross-tools/strip
|
||||
for tool_rel in glib-2.0/gio-querymodules gdk-pixbuf-2.0/gdk-pixbuf-query-loaders libgtk-3-0t64/gtk-query-immodules-3.0 libgtk-3-0/gtk-query-immodules-3.0; do
|
||||
if [ -x "/usr/lib/x86_64-linux-gnu/$tool_rel" ]; then
|
||||
tname=$(basename "$tool_rel")
|
||||
printf '#!/usr/bin/env bash\nexec qemu-x86_64 "/usr/lib/x86_64-linux-gnu/%s" "$@"\n' "$tool_rel" >"/tmp/x86_64-cross-tools/$tname"
|
||||
chmod +x "/tmp/x86_64-cross-tools/$tname"
|
||||
fi
|
||||
done
|
||||
fetch_cached_tool "linuxdeploy-07333c6-aarch64.AppImage" \
|
||||
"https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy-07333c6/linuxdeploy-aarch64.AppImage" 1
|
||||
fetch_cached_tool "linuxdeploy-plugin-appimage.AppImage" \
|
||||
"https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-aarch64.AppImage" 1
|
||||
fetch_cached_tool "runtime-x86_64" \
|
||||
"https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-x86_64" 0
|
||||
cat >"$HOME/.cache/tauri/linuxdeploy-07333c6-x86_64.AppImage" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
export ARCH=x86_64
|
||||
export NO_STRIP=true
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
export LDAI_RUNTIME_FILE="$HOME/.cache/tauri/runtime-x86_64"
|
||||
export LD_GTK_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
|
||||
export DEB_HOST_MULTIARCH=x86_64-linux-gnu
|
||||
export PKG_CONFIG_ALLOW_CROSS=1
|
||||
export PKG_CONFIG_LIBDIR=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
||||
export PATH="/tmp/x86_64-cross-tools:$PATH"
|
||||
exec "$HOME/.cache/tauri/linuxdeploy-07333c6-aarch64.AppImage" "$@"
|
||||
EOF
|
||||
chmod +x "$HOME/.cache/tauri/linuxdeploy-07333c6-x86_64.AppImage"
|
||||
export PKG_CONFIG_ALLOW_CROSS=1
|
||||
export PKG_CONFIG_LIBDIR=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc
|
||||
export CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc
|
||||
export CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++
|
||||
bundle="${CARGO_TARGET_DIR:-target}/x86_64-unknown-linux-gnu/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --target x86_64-unknown-linux-gnu --bundles appimage,deb
|
||||
fi
|
||||
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
|
||||
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
|
||||
;;
|
||||
windows)
|
||||
rustup target add x86_64-pc-windows-msvc >/dev/null
|
||||
tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis
|
||||
bundle="${CARGO_TARGET_DIR:-target}/x86_64-pc-windows-msvc/release/bundle/nsis"
|
||||
rm -rf "$bundle"
|
||||
tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis
|
||||
cp "$bundle"/*-setup.exe ../../../dist-gui/
|
||||
cp "$bundle"/*-setup.exe.sig ../../../dist-gui/ 2>/dev/null || true
|
||||
;;
|
||||
|
||||
@@ -250,7 +250,7 @@ function rustLabels(source, file) {
|
||||
// ---------------------------------------------------------------- coleta
|
||||
|
||||
const uiFiles = readdirSync(join(root, 'apps/gui/ui'))
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js' && f !== 'paged.polyfill.min.js')
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js' && f !== 'vendor')
|
||||
.map((f) => join(root, 'apps/gui/ui', f));
|
||||
uiFiles.push(join(root, 'apps/gui/ui/index.html'));
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gera, a partir da logo oficial, o ícone base usado pelo Tauri.
|
||||
|
||||
A logo oficial é `logo.png` na raiz do repositório (RGBA, fundo transparente,
|
||||
serve aos temas claro e escuro). Ela não é alterada aqui. Saída:
|
||||
apps/gui/src-tauri/icons/source.png 1024 px, a arte sobre o fundo escuro com cantos arredondados;
|
||||
o CI e `npx @tauri-apps/cli icon` geram o resto dos ícones.
|
||||
|
||||
Uso: python3 scripts/gerar-logo.py
|
||||
"""
|
||||
import os
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
RAIZ = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||
ICONES = os.path.join(RAIZ, "apps/gui/src-tauri/icons")
|
||||
FUNDO_ICONE = (10, 16, 28) # o azul-noite do fundo da arte original
|
||||
|
||||
|
||||
def recortar_quadrado(im, margem=0.04):
|
||||
"""Recorta no que é visível (opacidade > 16, ignora resquícios) e centraliza num quadrado."""
|
||||
box = im.getchannel("A").point(lambda v: 255 if v > 16 else 0).getbbox()
|
||||
im = im.crop(box)
|
||||
w, h = im.size
|
||||
lado = round(max(w, h) * (1 + 2 * margem))
|
||||
quadro = Image.new("RGBA", (lado, lado), (0, 0, 0, 0))
|
||||
quadro.paste(im, ((lado - w) // 2, (lado - h) // 2), im)
|
||||
return quadro
|
||||
|
||||
|
||||
def main():
|
||||
logo = recortar_quadrado(Image.open(os.path.join(RAIZ, "logo.png")).convert("RGBA"))
|
||||
|
||||
lado = 1024
|
||||
fundo = Image.new("RGBA", (lado, lado), FUNDO_ICONE + (255,))
|
||||
arte = logo.resize((round(lado * 0.82),) * 2, Image.LANCZOS)
|
||||
fundo.paste(arte, ((lado - arte.width) // 2, (lado - arte.height) // 2), arte)
|
||||
mascara = Image.new("L", (lado, lado), 0)
|
||||
ImageDraw.Draw(mascara).rounded_rectangle((0, 0, lado - 1, lado - 1), radius=round(lado * 0.2237), fill=255)
|
||||
icone = Image.new("RGBA", (lado, lado), (0, 0, 0, 0))
|
||||
icone.paste(fundo, (0, 0), mascara)
|
||||
os.makedirs(ICONES, exist_ok=True)
|
||||
destino = os.path.join(ICONES, "source.png")
|
||||
icone.save(destino, optimize=True)
|
||||
os.chmod(destino, 0o644)
|
||||
print("icons/source.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Liga o hook de pre-push do repositório (roda scripts/verificar.sh antes de cada push).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
git config core.hooksPath .githooks
|
||||
echo "hook ligado: git push passa a rodar scripts/verificar.sh (desligue com: git config --unset core.hooksPath)"
|
||||
@@ -2,7 +2,7 @@
|
||||
# Compila o CLI para uma plataforma e empacota como o atualizador espera:
|
||||
# sabia-<tag>-<plataforma>.tar.gz com um .sha256 ao lado.
|
||||
#
|
||||
# scripts/package-cli.sh v1.0.1 linux-x86_64 x86_64-unknown-linux-gnu
|
||||
# scripts/package-cli.sh v1.0.3 linux-x86_64 x86_64-unknown-linux-gnu
|
||||
#
|
||||
# Plataformas: linux-x86_64 e linux-aarch64. O alvo diferente da máquina de build
|
||||
# usa o linker cruzado correspondente (gcc-x86-64-linux-gnu). Escreve os arquivos
|
||||
|
||||
@@ -16,9 +16,10 @@ id=$(curl -fsS "${auth[@]}" "$API/releases/tags/$tag" | jq -r .id)
|
||||
|
||||
for file in "$@"; do
|
||||
name=$(basename "$file")
|
||||
encoded=$(jq -rn --arg n "$name" '$n|@uri')
|
||||
for old in $(curl -fsS "${auth[@]}" "$API/releases/$id/assets" | jq -r --arg n "$name" '.[] | select(.name == $n) | .id'); do
|
||||
curl -fsS -X DELETE "${auth[@]}" "$API/releases/$id/assets/$old"
|
||||
done
|
||||
curl -fsS -X POST "${auth[@]}" -F "attachment=@$file" "$API/releases/$id/assets?name=$name" >/dev/null
|
||||
curl -fsS -X POST "${auth[@]}" -F "attachment=@$file" "$API/releases/$id/assets?name=$encoded" >/dev/null
|
||||
echo "enviado: $name"
|
||||
done
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verificação local: roda o mesmo que o CI roda, antes de você dar push.
|
||||
#
|
||||
# scripts/verificar.sh rápido (~1 min): formatação, clippy, tradução, scripts, workflows
|
||||
# scripts/verificar.sh --completo inclui os testes do workspace e o cargo audit
|
||||
#
|
||||
# O CI leva de 5 a 30 minutos por execução e roda em Linux; quase tudo que o faz falhar
|
||||
# aparece aqui em segundos. Se isto passa, o CI só deve revelar o que é próprio dele
|
||||
# (imagens baixadas, apt do runner).
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
completo=0
|
||||
[ "${1:-}" = "--completo" ] && completo=1
|
||||
falhas=()
|
||||
passo() { # passo "nome" comando...
|
||||
local nome="$1"; shift
|
||||
printf '\n\033[1m== %s\033[0m\n' "$nome"
|
||||
if "$@"; then printf '\033[32mok\033[0m %s\n' "$nome"; else printf '\033[31mFALHOU\033[0m %s\n' "$nome"; falhas+=("$nome"); fi
|
||||
}
|
||||
|
||||
passo "formatação (cargo fmt)" cargo fmt --all -- --check
|
||||
# as mesmas flags do CI: avisos viram erro, inclusive nos testes
|
||||
passo "clippy (workspace, todos os alvos)" env RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets
|
||||
passo "cobertura da tradução da interface" node scripts/check-ui-i18n.mjs
|
||||
passo "sintaxe dos scripts bash" bash -c 'for f in scripts/*.sh .githooks/*; do bash -n "$f" || exit 1; done'
|
||||
passo "workflows e templates (YAML válido)" python3 -c "
|
||||
import glob, yaml
|
||||
for f in glob.glob('.gitea/**/*.yml', recursive=True):
|
||||
yaml.safe_load(open(f))
|
||||
"
|
||||
passo "versões consistentes" python3 -c "
|
||||
import json, re, sys
|
||||
from pathlib import Path
|
||||
|
||||
def cargo_version(path):
|
||||
m = re.search(r'^version\s*=\s*\"([^\"]+)\"', Path(path).read_text(), re.M)
|
||||
if not m:
|
||||
sys.exit(f'versão não encontrada em {path}')
|
||||
return m.group(1)
|
||||
|
||||
versoes = [
|
||||
cargo_version('Cargo.toml'),
|
||||
cargo_version('apps/gui/src-tauri/Cargo.toml'),
|
||||
json.load(open('apps/gui/src-tauri/tauri.conf.json'))['version'],
|
||||
]
|
||||
if len(set(versoes)) != 1:
|
||||
sys.exit(f'versões divergentes: Cargo.toml={versoes[0]}, src-tauri={versoes[1]}, tauri.conf={versoes[2]}')
|
||||
print(versoes[0])
|
||||
"
|
||||
if [ -f apps/gui/src-tauri/Cargo.toml ] && pkg-config --exists webkit2gtk-4.1 2>/dev/null; then
|
||||
passo "app desktop (cargo check)" env RUSTFLAGS="" cargo check --manifest-path apps/gui/src-tauri/Cargo.toml
|
||||
else
|
||||
printf '\n(app desktop: pulado, faltam as bibliotecas do WebKitGTK; o CI compila)\n'
|
||||
fi
|
||||
|
||||
if [ "$completo" = 1 ]; then
|
||||
passo "testes (cargo test --workspace)" cargo test --workspace
|
||||
passo "auditoria de dependências (cargo audit)" cargo audit
|
||||
fi
|
||||
|
||||
printf '\n'
|
||||
if [ ${#falhas[@]} -eq 0 ]; then
|
||||
printf '\033[32mtudo certo\033[0m: pode dar push.\n'
|
||||
else
|
||||
printf '\033[31mfalhou:\033[0m %s\n' "${falhas[*]}"
|
||||
exit 1
|
||||
fi
|
||||