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

This commit is contained in:
Lucas Rayan committed 2026-10-30 17:38:58 -03:00
1 parent 30a086a808
commit 659701b2d4
95 files changed
+2826 -253

No files matched your search

+4 -4
View File
@@ -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
+92 -5
View File
@@ -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
;;
+1 -1
View File
@@ -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'));
+49
View File
@@ -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()
+6
View File
@@ -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)"
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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
+67
View File
@@ -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