Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions apps/spsvalidator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# spsvalidator

Aplicação standalone para validação de pacotes SPS (`.zip`) com Flask, Pywebview e SQLite.

## Executar em desenvolvimento

```bash
cd apps/spsvalidator
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
spsvalidator
```

## Modo navegador (sem janela Pywebview)

```bash
spsvalidator --browser
```

Abre em `http://127.0.0.1:5000`.

## Build por sistema operacional

Scripts em `packaging/`.

```bash
cd apps/spsvalidator
source .venv/bin/activate
bash packaging/build_macos.sh
```

Saída: `dist/spsvalidator.app`

## Se o `.app` não abrir ao clicar

1. Rebuild após atualizar o código (build antigo pode não chamar `main()` nem incluir templates).
2. Rode pelo terminal para ver erros:

```bash
apps/spsvalidator/dist/spsvalidator.app/Contents/MacOS/spsvalidator
```

3. Em desenvolvimento, prefira:

```bash
spsvalidator --browser
```

## Erro `No module named 'pkg_resources'`

O `packtools` ainda depende de `pkg_resources` (fornecido pelo `setuptools<82`). Após atualizar o código:

```bash
cd apps/spsvalidator
source .venv/bin/activate
pip install -e ".[dev]"
bash packaging/build_macos.sh
open dist/spsvalidator.app
```

## Erro `No module named 'requests'` (ou `request`)

O `packtools` usa dependências transitivas (`requests`, `tenacity`, `langdetect`) que já estão no `pyproject.toml`. Reinstale e rebuild:

```bash
cd apps/spsvalidator
source .venv/bin/activate
pip install -e ".[dev]"
bash packaging/build_macos.sh
```

Dados locais ficam em `~/.spsvalidator/spsvalidator.sqlite3`.
5 changes: 5 additions & 0 deletions apps/spsvalidator/packaging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Packaging

- macOS: `bash packaging/build_macos.sh` gera bundle em `dist/`.
- Linux: `bash packaging/build_linux.sh` gera binário em `dist/` para converter em AppImage.
- Windows: `powershell -ExecutionPolicy Bypass -File packaging/build_windows.ps1` gera `.exe` em `dist/`.
22 changes: 22 additions & 0 deletions apps/spsvalidator/packaging/build_linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

python -m pip install -e ".[dev]"
python -m pip install pyinstaller
pyinstaller --noconfirm --windowed \
--name spsvalidator \
--icon src/spsvalidator/web/static/img/icon.png \
--paths src \
--collect-all packtools \
--collect-all webview \
--collect-data spsvalidator \
--hidden-import pkg_resources \
--hidden-import requests \
--hidden-import tenacity \
--hidden-import langdetect \
--copy-metadata setuptools \
src/spsvalidator/main.py
echo "Use linuxdeploy/appimagetool to convert dist/spsvalidator into AppImage."
23 changes: 23 additions & 0 deletions apps/spsvalidator/packaging/build_macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

python -m pip install -e ".[dev]"
python -m pip install pyinstaller
bash packaging/generate_icns.sh
bash packaging/generate_build_info.sh
pyinstaller --noconfirm --windowed \
--name spsvalidator \
--icon packaging/icon.icns \
--paths src \
--collect-all packtools \
--collect-all webview \
--collect-data spsvalidator \
--hidden-import pkg_resources \
--hidden-import requests \
--hidden-import tenacity \
--hidden-import langdetect \
--copy-metadata setuptools \
src/spsvalidator/main.py
21 changes: 21 additions & 0 deletions apps/spsvalidator/packaging/build_windows.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$RootDir = Split-Path -Path $PSScriptRoot -Parent
Set-Location $RootDir

python -m pip install -e ".[dev]"
python -m pip install pyinstaller
pyinstaller --noconfirm --windowed `
--name spsvalidator `
--icon src/spsvalidator/web/static/img/icon.png `
--paths src `
--collect-all packtools `
--collect-all webview `
--collect-data spsvalidator `
--hidden-import pkg_resources `
--hidden-import requests `
--hidden-import tenacity `
--hidden-import langdetect `
--copy-metadata setuptools `
src/spsvalidator/main.py
32 changes: 32 additions & 0 deletions apps/spsvalidator/packaging/generate_build_info.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET="$ROOT_DIR/src/spsvalidator/build_info.py"
cd "$ROOT_DIR"

APP_VERSION="$(python - <<'PY'
import tomllib
from pathlib import Path

with Path("pyproject.toml").open("rb") as file_pointer:
data = tomllib.load(file_pointer)
print(data["project"]["version"])
PY
)"

if [[ "$(uname -s)" == "Darwin" ]]; then
MACOS_VERSION="$(sw_vers -productVersion)"
MACOS_BUILD="$(sw_vers -buildVersion)"
cat > "$TARGET" <<EOF
APP_VERSION = "${APP_VERSION}"
BUILD_MACOS_VERSION = "${MACOS_VERSION} (${MACOS_BUILD})"
BUILD_PLATFORM = "macOS"
EOF
else
cat > "$TARGET" <<EOF
APP_VERSION = "${APP_VERSION}"
BUILD_MACOS_VERSION = "development"
BUILD_PLATFORM = "$(uname -s)"
EOF
fi
19 changes: 19 additions & 0 deletions apps/spsvalidator/packaging/generate_icns.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ICON_SRC="$ROOT_DIR/src/spsvalidator/web/static/img/icon.png"
ICONSET="$ROOT_DIR/packaging/icon.iconset"
OUTPUT="$ROOT_DIR/packaging/icon.icns"

rm -rf "$ICONSET"
mkdir -p "$ICONSET"

for size in 16 32 128 256 512; do
sips -z "$size" "$size" "$ICON_SRC" --out "$ICONSET/icon_${size}x${size}.png" >/dev/null
double=$((size * 2))
sips -z "$double" "$double" "$ICON_SRC" --out "$ICONSET/icon_${size}x${size}@2x.png" >/dev/null
done

iconutil -c icns "$ICONSET" -o "$OUTPUT"
rm -rf "$ICONSET"
Binary file added apps/spsvalidator/packaging/icon.icns
Binary file not shown.
50 changes: 50 additions & 0 deletions apps/spsvalidator/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "spsvalidator"
version = "1.0.0"
description = "Standalone SPS package validator desktop webapp"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"Flask>=3.0.0",
"lxml>=5.0.0",
"packtools @ git+https://git@github.com/scieloorg/packtools@4.12.6",
"pywebview>=5.1",
"setuptools>=68,<82",
"requests>=2.31.0",
"tenacity>=8.2.0",
"langdetect~=1.0.9",
]

[project.optional-dependencies]
dev = [
"black>=24.0.0",
"isort>=5.13.0",
"pytest>=8.0.0",
]

[project.scripts]
spsvalidator = "spsvalidator.main:main"

[tool.setuptools]
package-dir = { "" = "src" }

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
spsvalidator = ["web/templates/*.html", "web/static/**/*"]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]

[tool.isort]
profile = "black"
line_length = 88

[tool.black]
line-length = 88
64 changes: 64 additions & 0 deletions apps/spsvalidator/spsvalidator.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
from PyInstaller.utils.hooks import collect_all
from PyInstaller.utils.hooks import copy_metadata

datas = []
binaries = []
hiddenimports = ['pkg_resources', 'requests', 'tenacity', 'langdetect']
datas += collect_data_files('spsvalidator')
datas += copy_metadata('setuptools')
tmp_ret = collect_all('packtools')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('webview')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]


a = Analysis(
['src/spsvalidator/main.py'],
pathex=['src'],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='spsvalidator',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['packaging/icon.icns'],
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='spsvalidator',
)
app = BUNDLE(
coll,
name='spsvalidator.app',
icon='packaging/icon.icns',
bundle_identifier=None,
)
Loading
Loading