Skip to content

Commit

Permalink
feat: add usage of Pydantic, interfaces and dedicated config holders
Browse files Browse the repository at this point in the history
  • Loading branch information
seppzer0 committed Feb 19, 2024
1 parent 3359f35 commit 7fb9227
Show file tree
Hide file tree
Showing 43 changed files with 1,096 additions and 556 deletions.
4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# basic
__pycache__
.vscode
**/__pycache__
.coverage
.pytest_cache

# git subrepos
android_*
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# basic
__pycache__
.vscode
**/__pycache__
.mypy_cache
.coverage
.pytest_cache

# git subrepos
/android_*
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ options:
select a kernel base for the build
--codename CODENAME select device codename
--lkv LKV select Linux Kernel Version
-c, --clean don't build anything, just clean the environment
-c, --clean don't build anything, only clean kernel directories
--clean-image remove Docker/Podman image from the host machine after
build
--log-level {normal,verbose,quiet}
Expand Down
8 changes: 4 additions & 4 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class ZeroKernelConan(ConanFile):
topics = ("zero_kernel", "kali-nethunter", "nethunter")
settings = None
options = {
"base": ("los", "pa", "x", "aosp"),
"chroot": ("minimal", "full"),
"codename": ("dumpling", "cheeseburger")
}
"base": ("los", "pa", "x", "aosp"),
"chroot": ("minimal", "full"),
"codename": ("dumpling", "cheeseburger")
}

def export_sources(self):
self.copy("*", src="source", dst=".")
Expand Down
14 changes: 10 additions & 4 deletions docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@
- [x] add a single-point manifest with main info on the tool;
- [x] improve documentation;
- [x] apply OOP paradigm;
- [ ] add tests (unit/integration/etc);
- [x] switch to Poetry for dependency management;
- [ ] create a commit-based lockfile system for reproducible kernel builds;
- [x] switch to `pathlib`;
- [ ] switch to `raise` instead of `sys.exit`;
- [ ] use `pydantic`;
- [x] add a FAQ page;
- [x] refactor Docker/Podman command formation;
- [ ] refactor logging mechanism;
Expand All @@ -50,6 +48,14 @@
- [ ] add system app debloater;
- [x] add a new argument responsible for Linux kernel version selection;
- [x] add 4.14 Linux kernel version builds;
- [ ] decompose `run` methods into separate functions as much as possible;
- [ ] decompose `run` methods into separate methods as much as possible;
- [x] switch to pydantic;
- [ ] new device support: OP9;
- [ ] add type checks with pyright.
- [ ] add type checks with pyright;
- [ ] add unit tests with coverage checks;
- [ ] move device-specific modifications into appropriate folder with custom Modificator (sub)classes;
- [ ] switch to `__enter__` and `__exit__` Python's magic methods for container engines;
- [ ] switch to `exec` commands for Docker and Podman instead of a single `run` command;
- [ ] replace `ccmd.launch()` to just `launch()` (or any other name);
- [ ] embed newlines usage (from both sides) into `messages` functions as arguments;
- [ ] add tests on cases of custom wrapper edge case exits (kernel build on Windows, ROM-only asset collection for a ROM-universal kernel etc.)
274 changes: 267 additions & 7 deletions poetry.lock

Large diffs are not rendered by default.

18 changes: 16 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,25 @@ python = "^3.12"
conan = "~1"
argparse = "*"
requests = "*"
bandit = "*"
pathlib = "*"
dataclasses = "*"
typing = "*"
pydantic = "^2.6"

[tool.poetry.dev-dependencies]
bandit = "*"
pytest-cov = "*"

[tool.coverage.run]
source = [
"wrapper/clients",
"wrapper/configs",
"wrapper/engines",
"wrapper/modules",
"wrapper/utils"
]

[tool.coverage.report]
show_missing = true

[build-system]
requires = ["poetry-core"]
Expand Down
11 changes: 8 additions & 3 deletions scripts/get_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from pathlib import Path


apath = Path(Path(__file__).absolute().parents[1])
with open(Path(apath, "pyproject.toml")) as f:
print(f.read().split('version = "')[1].split('"')[0])
def main() -> None:
rootpath = Path(Path(__file__).absolute().parents[1])
with open(Path(rootpath, "pyproject.toml")) as f:
print(f.read().split('version = "')[1].split('"')[0])


if __name__ == "__main__":
main()
8 changes: 4 additions & 4 deletions scripts/multi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path


def parse_args() -> None:
def parse_args() -> argparse.Namespace:
"""Parse arguments."""
parser = argparse.ArgumentParser()
parser.add_argument(
Expand All @@ -17,7 +17,7 @@ def parse_args() -> None:


def rmove(src: Path, dst: Path) -> None:
"""Recusrively move files from one directory to another.
"""Recursively move files from one directory to another.
:param src: Source path.
:param dst: Destination path.
Expand Down Expand Up @@ -100,9 +100,9 @@ def main(args: argparse.Namespace) -> None:
size = f'--package-type {argset["size"]}' if argset["module"] == "bundle" else ""
extra = "--chroot minimal --rom-only --clean" if argset["module"] == "assets" else ""
# if the build is last, make it automatically remove the Docker/Podman image from runner
clean = "--clean-image" if count == len(argsets) and args.env in ("docker", "podman") else ""
clean_image = "--clean-image" if count == len(argsets) and args.env in ("docker", "podman") else ""
# form and launch the command
cmd = f"python3 wrapper {module} {benv} {base} {codename} {lkv} {size} {ksu} {clean} {extra}"
cmd = f"python3 wrapper {module} {benv} {base} {codename} {lkv} {size} {ksu} {clean_image} {extra}"
print(f"[CMD]: {cmd}")
subprocess.run(cmd.strip(), shell=True, check=True)
# copy artifacts into the shared directory
Expand Down
44 changes: 44 additions & 0 deletions scripts/run_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
import subprocess
from typing import List
from pathlib import Path
from subprocess import CompletedProcess

ROOTPATH: Path = Path(Path(__file__).absolute().parents[1])


class Tester:
"""A single class for all types of tests."""

def _launch_cmd(cmd: str) -> CompletedProcess:
"""Launch specified command."""
return subprocess.run(cmd, shell=True, check=True)

def pytest_checks(self) -> CompletedProcess:
"""Run unit tests with Pytest and coverage checks."""
os.environ["PYTHONPATH"] = ROOTPATH
return self._launch_cmd("python3 -m pytest tests/ --cov")

def pyright_checks(self) -> CompletedProcess:
"""Run type (hint) checks with Pyright."""
return self._launch_cmd("python3 -m pyright wrapper")

def bandit_checks(self) -> List[CompletedProcess]:
"""Run SAST with Bandit."""
fmts = ("json", "html")
cps = []
for fmt in fmts:
cps.append(self._launch(f"python3 -m bandit -r -f {fmt} {ROOTPATH} -o report.{fmt}"))
return cps


def main() -> None:
os.chdir(ROOTPATH)
t = Tester()
t.pytest_checks()
t.pyright_checks()
t.bandit_checks()


if __name__ == "__main__":
main()
10 changes: 0 additions & 10 deletions scripts/sast.py

This file was deleted.

29 changes: 29 additions & 0 deletions tests/unit/wrapper/modules/test_kernel_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest
from pathlib import Path

from wrapper.modules.kernel_builder import KernelBuilder


@pytest.mark.parametrize(
"config, expected_defconfig",
(
(
{"codename": "dumpling", "base": "los", "lkv": "4.4", "clean_kernel": False, "ksu": False},
Path("lineage_oneplus5_defconfig")
),
(
{"codename": "cheeseburger", "base": "pa", "lkv": "4.14", "clean_kernel": False, "ksu": True},
Path("vendor", "paranoid_defconfig")
),
(
{"codename": "dumpling", "base": "x", "lkv": "4.4", "clean_kernel": True, "ksu": True},
Path("oneplus5_defconfig")
)
)
)
def test__defconfig__check(config: dict[str], expected_defconfig: Path) -> bool:
"""Test defconfig path definition."""
t = KernelBuilder(**config)
res_actual = t._defconfig
res_expected = expected_defconfig
assert res_actual == res_expected
10 changes: 10 additions & 0 deletions tests/unit/wrapper/tools/test_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pytest

import wrapper.tools.commands as ccmd


def test__launch__invalid_command(capfd) -> None:
"""Test an invalid command execution handling."""
cmd = "some_invalid_command"
with pytest.raises(SystemExit):
ccmd.launch(cmd)
55 changes: 55 additions & 0 deletions tests/unit/wrapper/tools/test_messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest

import wrapper.tools.messages as msg


def test__message_note__validate(capfd):
"""Check "note" message construction."""
m = "This is a test message."
expected_result = f"[ * ] {m}"
msg.note(m)
out, err = capfd.readouterr()
assert out.rstrip() == expected_result


@pytest.mark.parametrize("dont_exit", (False, True))
def test__message_error__validate(capfd, dont_exit: bool) -> None:
"""Check "error" message construction."""
m = "This is a test message."
expected_result = f"[ ! ] {m}"
if dont_exit is False:
with pytest.raises(SystemExit):
msg.error(m, dont_exit)
else:
msg.error(m, dont_exit)
out, err = capfd.readouterr()
assert err.rstrip() == expected_result


def test__message_done__validate(capfd) -> None:
"""Check "done" message construction."""
m = "This is a test message."
expected_result = f"[ + ] {m}"
msg.done(m)
out, err = capfd.readouterr()
assert out.rstrip() == expected_result


def test__message_cancel__validate(capfd) -> None:
"""Check "cancel" message construction."""
m = "This is a test message."
expected_result = f"[ ~ ] {m}"
# system exit with code 0 is still a SystemExit
with pytest.raises(SystemExit):
msg.cancel(m)
out, err = capfd.readouterr()
assert out.rstrip() == expected_result


def test__message_debug__validate(capfd) -> None:
"""Check "debug" message construction."""
m = "This is a test message."
expected_result = f"[ DEBUG ] {m}"
msg.debug(m)
out, err = capfd.readouterr()
assert out.rstrip() == expected_result
Loading

0 comments on commit 7fb9227

Please sign in to comment.