๐ค Contributing¶
This document covers everything needed to develop, test, and extend SecureLoader.
๐ Table of Contents¶
- Dev Environment Setup
- Running Tests
- Code Style and Linting
- Project Layout
- Adding a Firmware Source
- Adding a Language
- Building a Standalone Executable
- Building the Documentation
- Submitting Changes
๐ ๏ธ Dev Environment Setup¶
Python 3.10 or later is required. A virtual environment is strongly recommended.
git clone https://github.com/niwciu/SecureLoader.git
cd SecureLoader
python -m venv .venv
source .venv/bin/activate # Linux / macOS
.venv\Scripts\activate # Windows
pip install -e ".[gui,dev]"
This installs the CLI (sld), the GUI (sld-gui), and all development tools:
pytest, ruff, mypy, black, flake8, bandit, and pip-audit.
๐งช Running Tests¶
pytest # run all tests
pytest -v # verbose output
pytest --cov=src -v # with coverage
pytest tests/test_firmware.py # single file
pytest tests/test_firmware.py::TestParseHeader::test_round_trips_known_values
Tests cover the core layer only. The protocol state machine is tested by
feeding bytes directly into _handle_byte() โ no physical device or mocked
serial library is required.
๐จ Code Style and Linting¶
All tools are configured in pyproject.toml:
| Tool | Command | Notes |
|---|---|---|
| ruff | ruff check . |
Primary linter. Auto-fixable issues: ruff check --fix . |
| flake8 | flake8 src tests |
Additional style checks |
| black | black src tests |
Auto-formatter. Run before committing. |
| mypy | mypy src |
Strict type checking. All public APIs must be fully typed. |
| bandit | bandit -r src/ -ll -x src/secure_loader/gui/resources |
SAST โ reports MEDIUM and above severity findings. |
| pip-audit | pip-audit --skip-editable |
Dependency vulnerability scan. |
Line length is 100 for all tools. Ruff rule set: E, F, W, I, N, UP, B, SIM, RUF
(E501 ignored โ black handles length).
Mypy runs in --strict mode. Every public function and method must carry type
annotations. # type: ignore comments require a specific error code (e.g.
# type: ignore[import-not-found]).
๐ Project Layout¶
src/secure_loader/
โโโ __init__.py # __version__, __app_name__
โโโ config.py # Cross-platform INI config (platformdirs + configparser)
โโโ i18n/
โ โโโ __init__.py # Lightweight in-process translations
โโโ core/
โ โโโ audit.py # Rotating flash-attempt audit log (1 MB ร 5)
โ โโโ firmware.py # Binary header parser
โ โโโ protocol.py # Serial state machine + driver
โ โโโ updater.py # Firmware/device compatibility check
โ โโโ sources/
โ โโโ base.py # FirmwareSource ABC + FirmwareIdentifier
โ โโโ local.py # Read from disk
โ โโโ http.py # HTTP server download (HTTPS by default; allow_insecure opt-in)
โ โโโ github.py # GitHub Releases scaffold (intentionally incomplete)
โโโ cli/
โ โโโ main.py # Click CLI entry point (sld)
โโโ gui/
โโโ app.py # QApplication bootstrap
โโโ main_window.py # Main window
โโโ login_dialog.py # HTTP credentials dialog
โโโ workers.py # QThread wrappers for core
tests/
โโโ conftest.py # Shared fixtures (sample_firmware, sample_header_bytes)
โโโ test_firmware.py # Header parser + helpers
โโโ test_protocol.py # Protocol state machine
โโโ test_updater.py # Compatibility check
โโโ test_config.py # Config load/save + keyring integration
โโโ test_http_source.py # HttpFirmwareSource (URL encoding, TLS, auth, size cap)
โโโ test_local_source.py # LocalFirmwareSource
โโโ test_github_source.py # GithubReleasesFirmwareSource (mocked API)
โโโ test_cli.py # CLI commands: config set/show/path, fetch, flash confirmation
โโโ test_cli_flash.py # CLI flash command integration (Protocol mock)
โโโ test_audit.py # Audit log rotation and entry format
โโโ test_gui.py # GUI smoke tests (QT_QPA_PLATFORM=offscreen)
docs/ # MkDocs documentation source
install_scripts/ # Build and install scripts (build.sh / build.bat)
Strict import rule: core/ never imports from cli/ or gui/. CLI and GUI
import core but not each other.
โ Adding a Firmware Source¶
- Create
src/secure_loader/core/sources/mysource.py. - Subclass
FirmwareSourcefrombase.pyand implement both abstract methods:
from .base import FirmwareSource, FirmwareIdentifier, FirmwareSourceError, ProgressCallback
class MyFirmwareSource(FirmwareSource):
def fetch_latest(
self,
identifier: FirmwareIdentifier,
progress: ProgressCallback | None = None,
) -> bytes:
...
def fetch_previous(
self,
identifier: FirmwareIdentifier,
progress: ProgressCallback | None = None,
) -> bytes:
...
- Export from
src/secure_loader/core/sources/__init__.py. - Wire it up in CLI (
cli/main.py) and/or GUI (gui/main_window.py). - Add tests in
tests/.
Both CLI and GUI depend only on the FirmwareSource abstract interface for
network-fetch paths, so the wiring step is the only frontend change required.
๐ Adding a Language¶
All translations live in src/secure_loader/i18n/__init__.py as a Python
dictionary โ no msgfmt or .mo compilation step is needed.
-
Add the language code to
And update theSUPPORTED:LanguageLiteral type accordingly. -
Add a translation dictionary under
Keys that are missing fall back to the English string automatically.TRANSLATIONS: -
Add the language to
_LANGUAGESingui/main_window.pyso it appears in the Language menu:
๐๏ธ Building a Standalone Executable¶
PyInstaller is supported via the optional [build] extra:
Linux / macOS:
pyinstaller \
--name="sld-gui" \
--icon=src/secure_loader/gui/resources/icons/icon.png \
--windowed \
--onefile \
-p src \
src/secure_loader/gui/app.py
Windows:
pyinstaller ^
--name="sld-gui" ^
--icon=src/secure_loader/gui/resources/icons/icon.ico ^
--windowed ^
--onefile ^
-p src ^
src/secure_loader/gui/app.py
The resulting binary will be in dist/.
Alternatively, use the provided scripts in install_scripts/ which automate
the virtual environment setup, dependency installation, and PyInstaller invocation:
๐ Building the Documentation¶
The documentation site is built with MkDocs and the Material theme.
Install the documentation dependencies:
Serve locally with live reload:
Then open http://127.0.0.1:8000 in your browser.
Build a static site for deployment:
The output is written to site/. Deploy to GitHub Pages:
๐ Submitting Changes¶
- Fork the repository and create a feature branch.
- Run
black src tests,ruff check .,flake8 src tests, andmypy srcโ all must pass cleanly. - Run
pytest --cov=src/secure_loader --cov-fail-under=70โ all tests must pass with coverage at or above 70 %. - Run
bandit -r src/ -ll -x src/secure_loader/gui/resourcesโ no MEDIUM or HIGH findings. - Run
pip-audit --skip-editableโ no known vulnerabilities. - Open a pull request with a clear description of what changed and why.