chore(i18n): finish translating code comments and scripts to English
CI / Format and clippy (push) Successful in 1m13s
CI / Dependency audit (push) Successful in 13s
CI / Tests (Linux) (push) Successful in 1m35s
Release / Create release (push) Successful in 7s
CI / Check desktop app (Linux) (push) Successful in 3m19s
Release / CLI (linux-aarch64) (push) Successful in 1m3s
Release / CLI (linux-x86_64) (push) Successful in 1m28s
Release / Desktop app (linux-aarch64) (push) Successful in 7m16s
Release / Desktop app (linux-x86_64) (push) Successful in 10m17s
Release / Desktop app (windows) (push) Successful in 4m54s
CI / Format and clippy (push) Successful in 1m13s
CI / Dependency audit (push) Successful in 13s
CI / Tests (Linux) (push) Successful in 1m35s
Release / Create release (push) Successful in 7s
CI / Check desktop app (Linux) (push) Successful in 3m19s
Release / CLI (linux-aarch64) (push) Successful in 1m3s
Release / CLI (linux-x86_64) (push) Successful in 1m28s
Release / Desktop app (linux-aarch64) (push) Successful in 7m16s
Release / Desktop app (linux-x86_64) (push) Successful in 10m17s
Release / Desktop app (windows) (push) Successful in 4m54s
Completes the English-only standardization for code, comments, scripts and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en, untouched). Covers the remaining crates/, apps/gui/ui/ and apps/gui/src-tauri/ comments, .gitea/ workflow and template comments, .githooks/ hooks, and the scripts/ source comments for the files already renamed in a prior commit (check.sh, generate-logo.py, install-hooks.sh). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
1 parent
bf059fd0bc
commit
55e19a8e5b
59 files changed
+935
-879
No files matched your search
+14
-17
@@ -1,35 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Instala pacotes do apt no CI sem depender do espelho estar rápido: usa os .deb em cache quando
|
||||
# existem e, se o apt travar ou falhar, tenta de novo com tempo limite por tentativa.
|
||||
# Installs apt packages in CI without depending on fast mirrors: reuses cached .deb
|
||||
# archives when present and retries with per-attempt timeouts if apt stalls or fails.
|
||||
#
|
||||
# Os passos de apt do CI já levaram de 13 a 28 minutos num dia de espelho lento, contra uns 35 s
|
||||
# no normal. O cache evita o download; o tempo limite evita ficar preso.
|
||||
#
|
||||
# Uso: scripts/apt-install.sh <pasta-do-cache> <pacote>...
|
||||
# Usage: scripts/apt-install.sh <cache-dir> <package>...
|
||||
set -euo pipefail
|
||||
[ $# -ge 2 ] || { echo "uso: $0 <pasta-do-cache> <pacote>..." >&2; exit 2; }
|
||||
[ $# -ge 2 ] || { echo "usage: $0 <cache-dir> <package>..." >&2; exit 2; }
|
||||
cache="$1"; shift
|
||||
pkgs=("$@")
|
||||
mkdir -p "$cache/partial"
|
||||
# O apt roda como root e deixa arquivos (lock, .deb) que o usuário do runner não lê; sem devolver a
|
||||
# posse, o actions/cache falha ao empacotar a pasta ("tar failed with exit code 2") e nunca grava.
|
||||
# apt runs as root and leaves files (lock, .deb) unreadable by non-root runner users;
|
||||
# restore ownership on exit so cache directories remain accessible.
|
||||
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 e índice presente: instala direto, sem nem atualizar o índice do apt
|
||||
# 1) With cached .deb files and package index present: install directly without updating apt lists
|
||||
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"
|
||||
echo "installed from cache"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2) sem cache (ou multiarch / incompleto): atualiza e instala, até 3 tentativas com tempo limite
|
||||
for tentativa in 1 2 3; do
|
||||
# 2) Cache miss (or multiarch / incomplete): update and install, up to 3 attempts with timeout
|
||||
for attempt in 1 2 3; do
|
||||
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)"
|
||||
echo "installed (attempt $attempt)"
|
||||
exit 0
|
||||
fi
|
||||
echo "apt falhou ou passou do tempo (tentativa $tentativa de 3)" >&2
|
||||
sleep $((tentativa * 10))
|
||||
echo "apt failed or timed out (attempt $attempt of 3)" >&2
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
echo "não foi possível instalar: ${pkgs[*]}" >&2
|
||||
echo "failed to install: ${pkgs[*]}" >&2
|
||||
exit 1
|
||||
+21
-22
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
// Confere se o dicionário inglês cobre os textos da interface.
|
||||
// Verifies that the English dictionary covers all user-facing interface strings.
|
||||
//
|
||||
// A varredura é estática e sem dependências: lê os arquivos da interface
|
||||
// (JS e HTML) e as mensagens pt-BR que o núcleo em Rust manda pra tela
|
||||
// (erros, diagnóstico, status do engine), e falha quando encontra uma
|
||||
// string que parece português e não tem tradução. Também recusa chaves
|
||||
// duplicadas no dicionário (num objeto JS a última vence em silêncio).
|
||||
// Static zero-dependency scanner: reads the UI files (JS and HTML) and the
|
||||
// pt-BR messages emitted by the Rust core (errors, diagnostics, engine status),
|
||||
// failing whenever a string looks like Portuguese and lacks a translation. Also
|
||||
// rejects duplicate keys in the dictionary (in a JS object literal the last key wins silently).
|
||||
//
|
||||
// Uso: node scripts/check-ui-i18n.mjs
|
||||
// Usage: node scripts/check-ui-i18n.mjs
|
||||
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
@@ -16,7 +15,7 @@ import { fileURLToPath } from 'node:url';
|
||||
const root = join(fileURLToPath(new URL('.', import.meta.url)), '..');
|
||||
const rel = (p) => relative(root, p).replaceAll('\\', '/');
|
||||
|
||||
// ---------------------------------------------------------------- dicionário
|
||||
// ---------------------------------------------------------------- dictionary
|
||||
|
||||
const dictPath = join(root, 'apps/gui/ui/i18n-en.js');
|
||||
const dictSource = readFileSync(dictPath, 'utf8');
|
||||
@@ -33,8 +32,8 @@ while ((m = keyRe.exec(dictSource))) {
|
||||
}
|
||||
|
||||
if (duplicates.length) {
|
||||
console.error('Chaves duplicadas em apps/gui/ui/i18n-en.js:');
|
||||
for (const d of duplicates) console.error(` ${JSON.stringify(d.key)} (linhas ${d.first} e ${d.again})`);
|
||||
console.error('Duplicate keys in apps/gui/ui/i18n-en.js:');
|
||||
for (const d of duplicates) console.error(` ${JSON.stringify(d.key)} (lines ${d.first} and ${d.again})`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
@@ -53,7 +52,7 @@ function translated(text) {
|
||||
return patterns.some((re) => re.test(text));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- heurística
|
||||
// ---------------------------------------------------------------- heuristic
|
||||
|
||||
const ACCENT = /[áàâãäéèêëíìîïóòôõöúùûüçÁÀÂÃÄÉÈÊËÍÌÎÏÓÒÔÕÖÚÙÛÜÇ]/;
|
||||
const WORDS = [
|
||||
@@ -95,13 +94,13 @@ function suspicious(text) {
|
||||
if (ALLOW.has(s) || ALLOW_EXACT.has(s)) return false;
|
||||
if (ALLOW_PREFIX.some((p) => s.startsWith(p))) return false;
|
||||
if (!isPortuguese(s)) return false;
|
||||
// técnico: seletores, classes, extensões, expressões
|
||||
// Technical tokens: CSS selectors, classes, extensions, expressions
|
||||
if (/^[.#\w-]+(\s*[,>+~]\s*[.#\w-]+)+$/.test(s)) return false;
|
||||
if (/^[a-z0-9_.:/@%{}$-]+$/i.test(s) && !ACCENT.test(s)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- extratores
|
||||
// ---------------------------------------------------------------- extractors
|
||||
|
||||
function jsStrings(source, file, baseLine = 1) {
|
||||
const out = [];
|
||||
@@ -215,23 +214,23 @@ function jsStrings(source, file, baseLine = 1) {
|
||||
function htmlStrings(source, file, baseLine = 1) {
|
||||
const out = [];
|
||||
const lineAt = (idx) => baseLine + source.slice(0, idx).split('\n').length - 1;
|
||||
// todo texto entre tags (inclusive começo e fim) — nós de texto reais
|
||||
// Text between tags (including leading and trailing text nodes)
|
||||
let last = 0;
|
||||
for (const m of source.matchAll(/<[^>]*>/g)) {
|
||||
out.push({ text: source.slice(last, m.index), file, line: lineAt(last) });
|
||||
last = m.index + m[0].length;
|
||||
}
|
||||
out.push({ text: source.slice(last), file, line: lineAt(last) });
|
||||
// atributos traduzíveis (com aspas simples ou duplas)
|
||||
// Translatable attributes (single or double quotes)
|
||||
for (const m of source.matchAll(/\b(placeholder|title|aria-label|alt|data-empty)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
|
||||
out.push({ text: m[2] ?? m[3], file, line: lineAt(m.index) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Extrai mensagens pt-BR do núcleo em Rust: #[error("...")], .push("...".into()),
|
||||
// "...".into() e format!("...", ...). Placeholders ({name}, {0}, {} etc.) são
|
||||
// normalizados para {n}, igual ao que o dicionário usa.
|
||||
// Extracts pt-BR messages from the Rust core: #[error("...")], .push("...".into()),
|
||||
// "...".into(), and format!("...", ...). Placeholders ({name}, {0}, {} etc.) are
|
||||
// normalized to {n}, matching the dictionary format.
|
||||
function rustLabels(source, file) {
|
||||
const out = [];
|
||||
const lineAt = (idx) => source.slice(0, idx).split('\n').length;
|
||||
@@ -247,7 +246,7 @@ function rustLabels(source, file) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- coleta
|
||||
// ---------------------------------------------------------------- collection
|
||||
|
||||
const uiFiles = readdirSync(join(root, 'apps/gui/ui'))
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js' && f !== 'vendor')
|
||||
@@ -286,12 +285,12 @@ for (const { text, file, line } of found) {
|
||||
}
|
||||
|
||||
if (missing.length) {
|
||||
console.error('Textos em português sem tradução em apps/gui/ui/i18n-en.js:\n');
|
||||
console.error('Untranslated Portuguese strings in apps/gui/ui/i18n-en.js:\n');
|
||||
for (const x of missing) console.error(` ${x.file}:${x.line} ${JSON.stringify(x.text)}`);
|
||||
console.error(`\n${missing.length} texto(s) sem tradução.`);
|
||||
console.error(`\n${missing.length} untranslated string(s).`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (!process.exitCode) {
|
||||
console.log(`i18n ok: ${found.length} textos verificados, ${Object.keys(EN).length} chaves no dicionário.`);
|
||||
console.log(`i18n ok: ${found.length} strings checked, ${Object.keys(EN).length} dictionary keys.`);
|
||||
}
|
||||
+40
-33
@@ -1,67 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verificação local: roda o mesmo que o CI roda, antes de você dar push.
|
||||
# Local verification: runs the same fast checks as CI before pushing.
|
||||
#
|
||||
# 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
|
||||
# scripts/check.sh fast (~1 min): fmt, clippy, i18n, scripts, workflows, semver
|
||||
# scripts/check.sh --full also runs workspace tests and 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).
|
||||
# CI takes 5 to 30 minutes per run; almost everything that breaks CI is caught
|
||||
# here in seconds.
|
||||
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
|
||||
full=0
|
||||
[ "${1:-}" = "--full" ] || [ "${1:-}" = "--completo" ] && full=1
|
||||
failures=()
|
||||
step() { # step "name" command...
|
||||
local name="$1"; shift
|
||||
printf '\n\033[1m== %s\033[0m\n' "$name"
|
||||
if "$@"; then printf '\033[32mok\033[0m %s\n' "$name"; else printf '\033[31mFAILED\033[0m %s\n' "$name"; failures+=("$name"); 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 "
|
||||
step "formatting (cargo fmt)" cargo fmt --all -- --check
|
||||
# Same flags as CI: warnings are treated as errors across all targets, including tests
|
||||
step "clippy (workspace, all targets)" env RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets
|
||||
step "UI translation coverage" node scripts/check-ui-i18n.mjs
|
||||
step "bash script syntax" bash -c 'for f in scripts/*.sh .githooks/*; do bash -n "$f" || exit 1; done'
|
||||
step "workflows and templates (valid YAML)" 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 "
|
||||
step "consistent Semantic Versioning 2.0.0" python3 -c "
|
||||
import json, re, sys
|
||||
from pathlib import Path
|
||||
|
||||
semver_re = re.compile(
|
||||
r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)'
|
||||
r'(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?'
|
||||
r'(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
|
||||
)
|
||||
|
||||
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}')
|
||||
sys.exit(f'version not found in {path}')
|
||||
return m.group(1)
|
||||
|
||||
versoes = [
|
||||
versions = [
|
||||
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 len(set(versions)) != 1:
|
||||
sys.exit(f'version mismatch: Cargo.toml={versions[0]}, src-tauri={versions[1]}, tauri.conf={versions[2]}')
|
||||
if not semver_re.match(versions[0]):
|
||||
sys.exit(f'invalid SemVer 2.0.0 version: {versions[0]}')
|
||||
print(versions[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
|
||||
step "desktop app (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'
|
||||
printf '\n(desktop app: skipped, WebKitGTK development libraries not installed; built in CI)\n'
|
||||
fi
|
||||
|
||||
if [ "$completo" = 1 ]; then
|
||||
passo "testes (cargo test --workspace)" cargo test --workspace
|
||||
passo "auditoria de dependências (cargo audit)" cargo audit
|
||||
if [ "$full" = 1 ]; then
|
||||
step "tests (cargo test --workspace)" cargo test --workspace
|
||||
step "dependency audit (cargo audit)" cargo audit
|
||||
fi
|
||||
|
||||
printf '\n'
|
||||
if [ ${#falhas[@]} -eq 0 ]; then
|
||||
printf '\033[32mtudo certo\033[0m: pode dar push.\n'
|
||||
if [ ${#failures[@]} -eq 0 ]; then
|
||||
printf '\033[32mall checks passed\033[0m: ready to push.\n'
|
||||
else
|
||||
printf '\033[31mfalhou:\033[0m %s\n' "${falhas[*]}"
|
||||
printf '\033[31mfailed:\033[0m %s\n' "${failures[*]}"
|
||||
exit 1
|
||||
fi
|
||||
+30
-28
@@ -1,47 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gera, a partir da logo oficial, o ícone base usado pelo Tauri.
|
||||
"""Generates the base Tauri icon from the official project logo.
|
||||
|
||||
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.
|
||||
The official logo is `logo.png` in the repository root (RGBA, transparent
|
||||
background, suitable for both light and dark themes). It is not modified here.
|
||||
Output:
|
||||
apps/gui/src-tauri/icons/source.png 1024 px artwork centered over a rounded
|
||||
dark background; CI and `npx @tauri-apps/cli icon`
|
||||
derive all remaining icon formats from it.
|
||||
|
||||
Uso: python3 scripts/gerar-logo.py
|
||||
Usage: python3 scripts/generate-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
|
||||
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||
ICONS_DIR = os.path.join(ROOT, "apps/gui/src-tauri/icons")
|
||||
ICON_BG = (10, 16, 28) # dark navy background matching the original artwork
|
||||
|
||||
|
||||
def recortar_quadrado(im, margem=0.04):
|
||||
"""Recorta no que é visível (opacidade > 16, ignora resquícios) e centraliza num quadrado."""
|
||||
def crop_square(im, margin=0.04):
|
||||
"""Crops to visible pixels (opacity > 16, ignoring faint artifacts) and centers in a square."""
|
||||
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
|
||||
side = round(max(w, h) * (1 + 2 * margin))
|
||||
canvas = Image.new("RGBA", (side, side), (0, 0, 0, 0))
|
||||
canvas.paste(im, ((side - w) // 2, (side - h) // 2), im)
|
||||
return canvas
|
||||
|
||||
|
||||
def main():
|
||||
logo = recortar_quadrado(Image.open(os.path.join(RAIZ, "logo.png")).convert("RGBA"))
|
||||
logo = crop_square(Image.open(os.path.join(ROOT, "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)
|
||||
side = 1024
|
||||
bg = Image.new("RGBA", (side, side), ICON_BG + (255,))
|
||||
art = logo.resize((round(side * 0.82),) * 2, Image.LANCZOS)
|
||||
bg.paste(art, ((side - art.width) // 2, (side - art.height) // 2), art)
|
||||
mask = Image.new("L", (side, side), 0)
|
||||
ImageDraw.Draw(mask).rounded_rectangle((0, 0, side - 1, side - 1), radius=round(side * 0.2237), fill=255)
|
||||
icon = Image.new("RGBA", (side, side), (0, 0, 0, 0))
|
||||
icon.paste(bg, (0, 0), mask)
|
||||
os.makedirs(ICONS_DIR, exist_ok=True)
|
||||
dest = os.path.join(ICONS_DIR, "source.png")
|
||||
icon.save(dest, optimize=True)
|
||||
os.chmod(dest, 0o644)
|
||||
print("icons/source.png")
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Liga o hook de pre-push do repositório (roda scripts/verificar.sh antes de cada push).
|
||||
# Enables the repository Git hooks (.githooks/pre-push and .githooks/commit-msg).
|
||||
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)"
|
||||
echo "hooks enabled: git commit validates Conventional Commits in English and git push runs scripts/check.sh (disable with: git config --unset core.hooksPath)"
|
||||
@@ -1,19 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Compila o CLI para uma plataforma e empacota como o atualizador espera:
|
||||
# sabia-<tag>-<plataforma>.tar.gz com um .sha256 ao lado.
|
||||
# Builds the CLI for a target platform and packages it as expected by the updater:
|
||||
# sabia-<tag>-<platform>.tar.gz alongside a .sha256 checksum file.
|
||||
#
|
||||
# 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
|
||||
# na raiz do repositório e imprime os nomes.
|
||||
# Supported platforms: linux-x86_64 and linux-aarch64. When cross-compiling
|
||||
# from a non-x86_64 host, uses gcc-x86-64-linux-gnu. Writes the archives to the
|
||||
# repository root and prints their filenames.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
tag=${1:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
|
||||
platform=${2:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
|
||||
target=${3:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
|
||||
tag=${1:?usage: package-cli.sh <tag> <platform> <rust-target>}
|
||||
platform=${2:?usage: package-cli.sh <tag> <platform> <rust-target>}
|
||||
target=${3:?usage: package-cli.sh <tag> <platform> <rust-target>}
|
||||
|
||||
case "$target" in
|
||||
x86_64-unknown-linux-gnu)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Imprime as notas de uma release: a seção do CHANGELOG.md daquela versão, ou o
|
||||
# log de commits desde a tag anterior quando o changelog não tem a seção.
|
||||
# Prints release notes for a given tag: extracts the matching CHANGELOG.md
|
||||
# section, or falls back to the commit log since the previous tag.
|
||||
#
|
||||
# scripts/release-notes.sh v1.2.3
|
||||
set -euo pipefail
|
||||
|
||||
tag=${1:?uso: release-notes.sh <tag>}
|
||||
tag=${1:?usage: release-notes.sh <tag>}
|
||||
version=${tag#v}
|
||||
|
||||
section=""
|
||||
@@ -22,7 +22,7 @@ if [ -n "$(printf '%s' "$section" | tr -d '[:space:]')" ]; then
|
||||
printf '%s\n' "$section"
|
||||
else
|
||||
previous=$(git describe --tags --abbrev=0 "$tag^" 2>/dev/null || true)
|
||||
echo "## Mudanças${previous:+ desde $previous}"
|
||||
echo "## Changes${previous:+ since $previous}"
|
||||
echo
|
||||
git log --no-merges --format='- %s (%h)' "${previous:+$previous..}$tag"
|
||||
fi
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Anexa arquivos a uma release do Gitea, trocando os de mesmo nome (assim uma
|
||||
# execução repetida do CI não falha por anexo duplicado).
|
||||
# Uploads assets to a Gitea release, replacing any existing asset with the same
|
||||
# filename so re-running a CI job never fails on duplicate attachments.
|
||||
#
|
||||
# API=https://git.exemplo.com/api/v1/repos/dono/repo TOKEN=... \
|
||||
# scripts/release-upload.sh v1.2.3 arquivo1 arquivo2
|
||||
# API=https://git.example.com/api/v1/repos/owner/repo TOKEN=... \
|
||||
# scripts/release-upload.sh v1.2.3 file1 file2
|
||||
set -euo pipefail
|
||||
|
||||
: "${API:?defina API (URL do repositório na API do Gitea)}"
|
||||
: "${TOKEN:?defina TOKEN}"
|
||||
tag=${1:?uso: release-upload.sh <tag> <arquivo>...}
|
||||
: "${API:?set API (Gitea repository API URL)}"
|
||||
: "${TOKEN:?set TOKEN}"
|
||||
tag=${1:?usage: release-upload.sh <tag> <file>...}
|
||||
shift
|
||||
|
||||
auth=(-H "Authorization: token $TOKEN")
|
||||
@@ -21,5 +21,5 @@ for file in "$@"; 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=$encoded" >/dev/null
|
||||
echo "enviado: $name"
|
||||
echo "uploaded: $name"
|
||||
done
|
||||
Reference in new issue
Block a user