From 3591ac4c53187863419c9937369b8bbd37f443b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Sp=C3=A1=C4=8Dil?= Date: Tue, 1 Sep 2026 23:43:53 +0200 Subject: [PATCH] =?UTF-8?q?ci:=20kontrola=20kvality=20k=C3=B3du=20p=C5=99i?= =?UTF-8?q?=20ka=C5=BEd=C3=A9m=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workflow code_quality.yml pouští při každém PR a při pushi do main kontroly, které se dosud dělaly ručně, ve čtyřech jobech: - Lint a bezpečnost: check_sources.py, bandit, detect-secrets, flake8, ruff. Bandit a detect-secrets jsou tytéž kontroly, které blokují schválení na plugins.qgis.org. - Kompatibilita s Qt6: pyqgis4-checker v dockeru. Skript končí kódem 0, i když něco najde, výsledek je jen v logu; job proto ověřuje, že log obsahuje pouze hlavičku. - Smoke test: smoke_test.py v qgis/qgis:ltr (3.44, Qt5) i qgis/qgis:stable (4.x, Qt6). Tagy se posouvají schválně, aby bylo vidět, že plugin drží krok s aktuálním QGISem. - Balíček pluginu: sestaví amcr_viewer.zip stejně jako release workflow, ověří, že v něm je metadata.txt, __init__.py i .flake8 a nejsou v něm git soubory, a přiloží ho jako artefakt běhu. Recenzent ho nainstaluje přes Install from ZIP bez ručního balení. detect-secrets se pouští s --all-files. Bez toho prohledá jen soubory sledované gitem a o nesledovaném souboru mlčí, což vypadá jako čistý výsledek. Verze nástrojů jsou napevno. Bez pinu by se výsledek měnil s každým vydáním ruffu, které rozšíří výchozí sadu pravidel. CodeQL a GitGuardian běží zvlášť, nastavené na úrovni organizace, a schválně se tu neduplikují. AGENTS.md popisuje, jak totéž pustit lokálně, a tři místa, kde kontroly tiše lžou (výstupní kód pyqgis4-checkeru, detect-secrets bez --all-files, umístění config souborů pro scanner). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WhQyc1uQFcpkezLwM8if6T --- .github/workflows/code_quality.yml | 171 +++++++++++++++++++++++++++++ AGENTS.md | 56 +++++++++- 2 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/code_quality.yml diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 0000000..dfd7e9a --- /dev/null +++ b/.github/workflows/code_quality.yml @@ -0,0 +1,171 @@ +name: Code Quality + +# Pouští při každém PR tytéž kontroly, které se dosud dělaly ručně: +# +# * co spouští plugins.qgis.org při uploadu (bandit, detect-secrets, +# flake8, analýza souborů) – https://plugins.qgis.org/docs/security-scanning +# * oficiální kontrolu kompatibility s Qt6 (pyqgis4-checker) +# * skutečné načtení pluginu v QGIS 3.44 (Qt5) i v QGIS 4 (Qt6) +# * sestavení ZIPu, který si recenzent stáhne a nainstaluje přímo z PR +# +# CodeQL a GitGuardian běží zvlášť, nastavené na úrovni organizace – +# tady se schválně neduplikují. + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +env: + # Verze se drží napevno, aby se výsledek nezměnil sám od sebe. Výchozí + # sada pravidel ruffu se mezi verzemi mění; povýšení je vědomý krok. + BANDIT: bandit==1.9.4 + DETECT_SECRETS: detect-secrets==1.5.0 + FLAKE8: flake8==7.3.0 + RUFF: ruff==0.16.5 + +jobs: + # -------------------------------------------------------------------- + # 1. Statické kontroly – běží první, protože trvají desítky sekund + # -------------------------------------------------------------------- + lint: + name: Lint a bezpečnost + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + + - name: Install tools + run: pip install "$BANDIT" "$DETECT_SECRETS" "$FLAKE8" "$RUFF" + + # Hygiena repozitáře: BOM, přímé importy z PyQt5/PyQt6, spustitelná + # práva a podezřelé typy souborů. Padá jako první, protože BOM + # zneviditelní soubor pro kontrolu níž. + - name: Source hygiene + run: python3 tests/check_sources.py + + # Blokující kontrola na plugins.qgis.org + - name: Bandit + run: bandit -r amcr_viewer/ + + # Blokující kontrola na plugins.qgis.org. + # --all-files je podstatné: bez něj detect-secrets prohledá jen + # soubory sledované gitem a nesledovaný soubor tiše přeskočí. + - name: detect-secrets + run: | + detect-secrets scan --all-files amcr_viewer/ > vysledek.json + python3 -c " + import json, sys + nalezy = json.load(open('vysledek.json'))['results'] + if nalezy: + print(json.dumps(nalezy, indent=2)) + sys.exit(1) + print('detect-secrets: bez nálezů') + " + + # Na plugins.qgis.org je informativní, tady blokuje – konfigurace + # v amcr_viewer/.flake8 je stejná pro obě místa. + - name: Flake8 + run: flake8 --config amcr_viewer/.flake8 amcr_viewer/ + + # Nad rámec plugins.qgis.org; konfigurace v pyproject.toml + - name: Ruff + run: ruff check . + + # -------------------------------------------------------------------- + # 2. Kompatibilita s Qt6 – oficiální skript z QGISu + # -------------------------------------------------------------------- + qt6: + name: Kompatibilita s Qt6 + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + # Pozor: skript končí kódem 0 i když něco najde, výsledek je jen + # v logu. Prázdný log (samotná hlavička) znamená čisto. + - name: pyqgis4-checker + run: | + docker run --rm --user "$(id -u):$(id -g)" \ + --workdir /workspace/ -v "$PWD:/workspace/" \ + ghcr.io/qgis/pyqgis4-checker:main-ubuntu \ + pyqt5_to_pyqt6.py --dry_run --logfile /workspace/pyqt6_checker.log . + echo "--- pyqt6_checker.log ---" + cat pyqt6_checker.log + nalezu=$(grep -v '=== dry_run mode | Start Logs ===' pyqt6_checker.log | wc -l) + if [ "$nalezu" -ne 0 ]; then + echo "::error::pyqgis4-checker nahlásil nálezy, viz log výše" + exit 1 + fi + + # -------------------------------------------------------------------- + # 3. Načtení pluginu ve skutečném QGIS, v obou podporovaných verzích + # -------------------------------------------------------------------- + qgis: + name: Smoke test (QGIS ${{ matrix.qgis }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # ltr = 3.44 na Qt5, stable = 4.x na Qt6. Tagy se posouvají + # schválně: chceme vědět, že plugin drží krok s aktuálním QGISem. + qgis: [ltr, stable] + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Smoke test + run: | + docker run --rm -v "$PWD:/work:ro" -w /work \ + --user "$(id -u):$(id -g)" -e HOME=/tmp \ + "qgis/qgis:${{ matrix.qgis }}" python3 tests/smoke_test.py + + # -------------------------------------------------------------------- + # 4. ZIP k instalaci – stejný postup jako v release_plugin.yml + # -------------------------------------------------------------------- + package: + name: Balíček pluginu + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Zip plugin + run: zip -r amcr_viewer.zip amcr_viewer -x "*.git*" + + # Kontrola obsahu ZIPu. Config soubory pro scanner musí být uvnitř + # vedle metadata.txt, jinak je plugins.qgis.org nenajde – a některé + # nástroje skryté soubory tiše vynechávají. + - name: Verify archive contents + run: | + unzip -l amcr_viewer.zip + for soubor in amcr_viewer/metadata.txt amcr_viewer/__init__.py \ + amcr_viewer/.flake8; do + unzip -l amcr_viewer.zip | grep -qF " $soubor" \ + || { echo "::error::v ZIPu chybí $soubor"; exit 1; } + done + if unzip -l amcr_viewer.zip | grep -qE '\.git'; then + echo "::error::v ZIPu jsou git soubory" + exit 1 + fi + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: amcr_viewer-plugin + path: amcr_viewer.zip + if-no-files-found: error diff --git a/AGENTS.md b/AGENTS.md index b6107b8..fa12dcd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -176,5 +176,57 @@ flatpak run --command=sh org.qgis.qgis -c \ Plugin se testuje načtením do QGIS (Plugins → Manage and Install Plugins → Install from ZIP, nebo nasazením složky `amcr_viewer/` do adresáře pluginů -QGIS). Automatizované testy zatím repozitář neobsahuje – změny ověřuj ručně -v QGIS na podporované verzi. +QGIS). **Ruční test v QGIS nic nenahrazuje** – automatické kontroly ověřují, +že se plugin načte a že projde kontrolami kvality, ne že dělá správnou věc. + +### Automatické kontroly + +Workflow `.github/workflows/code_quality.yml` pouští při každém PR do `main` +tohle: + +| job | co dělá | +|---|---| +| **Lint a bezpečnost** | `check_sources.py`, bandit, detect-secrets, flake8, ruff | +| **Kompatibilita s Qt6** | `pyqgis4-checker` v dockeru | +| **Smoke test** | `smoke_test.py` v `qgis/qgis:ltr` i `qgis/qgis:stable` | +| **Balíček pluginu** | sestaví ZIP, ověří obsah, přiloží jako artefakt | + +Smoke test běží v obou podporovaných řadách: `ltr` je QGIS 3.44 na Qt5, +`stable` je QGIS 4.x na Qt6. + +Artefakt z posledního jobu se dá stáhnout ze stránky běhu a rovnou +nainstalovat přes *Install from ZIP* – recenzent nemusí nic balit ručně. + +Totéž lokálně: + +```sh +pip install bandit detect-secrets flake8 ruff + +python3 tests/check_sources.py +bandit -r amcr_viewer/ +detect-secrets scan --all-files amcr_viewer/ +flake8 --config amcr_viewer/.flake8 amcr_viewer/ +ruff check . + +# smoke test v obou verzích QGIS (docker, bez instalace čehokoli) +for tag in ltr stable; do + docker run --rm -v "$PWD:/work:ro" -w /work \ + --user "$(id -u):$(id -g)" -e HOME=/tmp \ + "qgis/qgis:$tag" python3 tests/smoke_test.py +done +``` + +Na co si dát pozor: + +- **`pyqgis4-checker` končí kódem 0, i když něco najde** – výsledek je jen + v logu. Workflow proto kontroluje, že log obsahuje jen hlavičku. +- **`detect-secrets` bez `--all-files` prohledá jen soubory sledované + gitem** a o nesledovaném souboru mlčí. Vypadá to jako čistý výsledek. +- **Konfigurace lintů je rozdělená schválně.** `amcr_viewer/.flake8` leží + vedle `metadata.txt`, protože scanner na plugins.qgis.org hledá config + soubory jen v kořeni balíčku uvnitř ZIPu; díky tomu platí stejná pravidla + v CI, lokálně i při uploadu. Konfigurace ruffu je naopak v kořenovém + `pyproject.toml` – ruff se do balíčku pluginu nedistribuuje. + Viz https://plugins.qgis.org/docs/security-scanning/config-files +- **Verze nástrojů jsou v workflow napevno.** Výchozí sada pravidel ruffu se + mezi verzemi mění, takže bez pinu by CI začalo padat samo od sebe.