pmb: Add lots of type hints (MR 2464)

This commit is contained in:
Newbyte 2024-10-29 23:06:59 +01:00
parent d05d57b37e
commit 225d8b30a0
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
71 changed files with 566 additions and 325 deletions

View file

@ -161,7 +161,7 @@ def rewrite(
handle.truncate() handle.truncate()
def get_upstream_aport(pkgname: str, arch: Arch | None = None, retain_branch: bool = False): def get_upstream_aport(pkgname: str, arch: Arch | None = None, retain_branch: bool = False) -> Path:
""" """
Perform a git checkout of Alpine's aports and get the path to the aport. Perform a git checkout of Alpine's aports and get the path to the aport.

View file

@ -193,8 +193,8 @@ def generate_deviceinfo(
chassis: str, chassis: str,
has_external_storage: bool, has_external_storage: bool,
flash_method: str, flash_method: str,
bootimg=None, bootimg: Bootimg | None = None,
): ) -> None:
codename = "-".join(pkgname.split("-")[1:]) codename = "-".join(pkgname.split("-")[1:])
external_storage = "true" if has_external_storage else "false" external_storage = "true" if has_external_storage else "false"
# Note: New variables must be added to pmb/config/__init__.py as well # Note: New variables must be added to pmb/config/__init__.py as well
@ -281,7 +281,7 @@ def generate_modules_initfs() -> None:
handle.write(line.lstrip() + "\n") handle.write(line.lstrip() + "\n")
def generate_apkbuild(pkgname: str, name: str, arch: Arch, flash_method: str): def generate_apkbuild(pkgname: str, name: str, arch: Arch, flash_method: str) -> None:
# Dependencies # Dependencies
depends = ["postmarketos-base", "linux-" + "-".join(pkgname.split("-")[1:])] depends = ["postmarketos-base", "linux-" + "-".join(pkgname.split("-")[1:])]
if flash_method in ["fastboot", "heimdall-bootimg"]: if flash_method in ["fastboot", "heimdall-bootimg"]:
@ -331,7 +331,7 @@ def generate_apkbuild(pkgname: str, name: str, arch: Arch, flash_method: str):
handle.write(line[8:].replace(" " * 4, "\t") + "\n") handle.write(line[8:].replace(" " * 4, "\t") + "\n")
def generate(pkgname: str): def generate(pkgname: str) -> None:
arch = ask_for_architecture() arch = ask_for_architecture()
manufacturer = ask_for_manufacturer() manufacturer = ask_for_manufacturer()
name = ask_for_name(manufacturer) name = ask_for_name(manufacturer)

View file

@ -8,7 +8,7 @@ import pmb.helpers.git
import pmb.helpers.run import pmb.helpers.run
def generate(pkgname: str): def generate(pkgname: str) -> None:
# Copy original aport # Copy original aport
prefix = pkgname.split("-")[0] prefix = pkgname.split("-")[0]
arch = Arch.from_str(pkgname.split("-")[1]) arch = Arch.from_str(pkgname.split("-")[1])

View file

@ -7,7 +7,7 @@ import pmb.aportgen.core
import pmb.parse.apkindex import pmb.parse.apkindex
def generate_apkbuild(pkgname: str, deviceinfo: Deviceinfo, patches: list[str]): def generate_apkbuild(pkgname: str, deviceinfo: Deviceinfo, patches: list[str]) -> None:
device = "-".join(pkgname.split("-")[1:]) device = "-".join(pkgname.split("-")[1:])
carch = deviceinfo.arch.kernel() carch = deviceinfo.arch.kernel()
@ -117,7 +117,7 @@ def generate_apkbuild(pkgname: str, deviceinfo: Deviceinfo, patches: list[str]):
hndl.write(line[8:].replace(" " * 4, "\t") + "\n") hndl.write(line[8:].replace(" " * 4, "\t") + "\n")
def generate(pkgname: str): def generate(pkgname: str) -> None:
device = "-".join(pkgname.split("-")[1:]) device = "-".join(pkgname.split("-")[1:])
deviceinfo = pmb.parse.deviceinfo(device) deviceinfo = pmb.parse.deviceinfo(device)
work = get_context().config.work work = get_context().config.work

View file

@ -30,7 +30,7 @@ from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def check_build_for_arch(pkgname: str, arch: Arch): def check_build_for_arch(pkgname: str, arch: Arch) -> bool:
"""Check if pmaport can be built or exists as binary for a specific arch. """Check if pmaport can be built or exists as binary for a specific arch.
:returns: * True when it can be built :returns: * True when it can be built
@ -67,7 +67,7 @@ def check_build_for_arch(pkgname: str, arch: Arch):
raise RuntimeError(f"Can't build '{pkgname}' for architecture {arch}") raise RuntimeError(f"Can't build '{pkgname}' for architecture {arch}")
def get_depends(context: Context, apkbuild): def get_depends(context: Context, apkbuild: dict[str, Any]) -> list[str]:
"""Alpine's abuild always builds/installs the "depends" and "makedepends" of a package """Alpine's abuild always builds/installs the "depends" and "makedepends" of a package
before building it. before building it.
@ -95,7 +95,7 @@ def get_depends(context: Context, apkbuild):
return ret return ret
def get_pkgver(original_pkgver: str, original_source=False): def get_pkgver(original_pkgver: str, original_source: bool = False) -> str:
"""Get the original pkgver when using the original source. """Get the original pkgver when using the original source.
Otherwise, get the pkgver with an appended suffix of current date and time. Otherwise, get the pkgver with an appended suffix of current date and time.
@ -122,7 +122,14 @@ def output_path(arch: Arch, pkgname: str, pkgver: str, pkgrel: str) -> Path:
return arch / f"{pkgname}-{pkgver}-r{pkgrel}.apk" return arch / f"{pkgname}-{pkgver}-r{pkgrel}.apk"
def finish(apkbuild, channel, arch, output: Path, chroot: Chroot, strict=False): def finish(
apkbuild: dict[str, Any],
channel: str,
arch: Arch,
output: Path,
chroot: Chroot,
strict: bool = False,
) -> None:
"""Various finishing tasks that need to be done after a build.""" """Various finishing tasks that need to be done after a build."""
# Verify output file # Verify output file
out_dir = get_context().config.work / "packages" / channel out_dir = get_context().config.work / "packages" / channel
@ -212,7 +219,9 @@ class BuildQueueItem(TypedDict):
chroot: Chroot chroot: Chroot
def has_cyclical_dependency(unmet_deps: dict[str, list[str]], item: BuildQueueItem, dep: str): def has_cyclical_dependency(
unmet_deps: dict[str, list[str]], item: BuildQueueItem, dep: str
) -> bool:
pkgnames = [item["name"]] + list(item["apkbuild"]["subpackages"].keys()) pkgnames = [item["name"]] + list(item["apkbuild"]["subpackages"].keys())
for pkgname in pkgnames: for pkgname in pkgnames:
@ -245,7 +254,7 @@ def prioritise_build_queue(disarray: list[BuildQueueItem]) -> list[BuildQueueIte
all_pkgnames.append(item["name"]) all_pkgnames.append(item["name"])
all_pkgnames += item["apkbuild"]["subpackages"].keys() all_pkgnames += item["apkbuild"]["subpackages"].keys()
def queue_item(item: BuildQueueItem): def queue_item(item: BuildQueueItem) -> None:
queue.append(item) queue.append(item)
disarray.remove(item) disarray.remove(item)
all_pkgnames.remove(item["name"]) all_pkgnames.remove(item["name"])
@ -443,10 +452,10 @@ def packages(
context: Context, context: Context,
pkgnames: list[str], pkgnames: list[str],
arch: Arch | None = None, arch: Arch | None = None,
force=False, force: bool = False,
strict=False, strict: bool = False,
src=None, src: str | None = None,
bootstrap_stage=BootstrapStage.NONE, bootstrap_stage: int = BootstrapStage.NONE,
log_callback: Callable | None = None, log_callback: Callable | None = None,
) -> list[str]: ) -> list[str]:
""" """

View file

@ -3,7 +3,6 @@
from pathlib import Path from pathlib import Path
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.helpers import logging from pmb.helpers import logging
from typing import Any
import pmb.config import pmb.config
import pmb.chroot.apk import pmb.chroot.apk
@ -11,11 +10,11 @@ import pmb.helpers.pmaports
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.meta import Cache from pmb.meta import Cache
from pmb.types import CrossCompileType from pmb.types import Apkbuild, CrossCompileType
# FIXME (#2324): type hint Arch # FIXME (#2324): type hint Arch
def arch_from_deviceinfo(pkgname, aport: Path) -> Arch | None: def arch_from_deviceinfo(pkgname: str, aport: Path) -> Arch | None:
""" """
The device- packages are noarch packages. But it only makes sense to build The device- packages are noarch packages. But it only makes sense to build
them for the device's architecture, which is specified in the deviceinfo them for the device's architecture, which is specified in the deviceinfo
@ -39,7 +38,7 @@ def arch_from_deviceinfo(pkgname, aport: Path) -> Arch | None:
@Cache("package") @Cache("package")
def arch(package: str | dict[str, Any]) -> Arch: def arch(package: str | Apkbuild) -> Arch:
""" """
Find a good default in case the user did not specify for which architecture Find a good default in case the user did not specify for which architecture
a package should be built. a package should be built.
@ -84,7 +83,7 @@ def arch(package: str | dict[str, Any]) -> Arch:
return Arch.native() return Arch.native()
def chroot(apkbuild: dict[str, str], arch: Arch) -> Chroot: def chroot(apkbuild: Apkbuild, arch: Arch) -> Chroot:
if arch == Arch.native(): if arch == Arch.native():
return Chroot.native() return Chroot.native()
@ -94,7 +93,7 @@ def chroot(apkbuild: dict[str, str], arch: Arch) -> Chroot:
return Chroot.buildroot(arch) return Chroot.buildroot(arch)
def crosscompile(apkbuild, arch: Arch) -> CrossCompileType: def crosscompile(apkbuild: Apkbuild, arch: Arch) -> CrossCompileType:
"""Decide the type of compilation necessary to build a given APKBUILD.""" """Decide the type of compilation necessary to build a given APKBUILD."""
if not get_context().cross: if not get_context().cross:
return None return None

View file

@ -1,6 +1,5 @@
import enum import enum
from pathlib import Path from pathlib import Path
from typing import Any
from pmb.core.pkgrepo import pkgrepo_paths from pmb.core.pkgrepo import pkgrepo_paths
import pmb.helpers.run import pmb.helpers.run
import pmb.chroot import pmb.chroot
@ -9,6 +8,7 @@ from pmb.core import Context
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.core.chroot import Chroot from pmb.core.chroot import Chroot
from pmb.helpers import logging from pmb.helpers import logging
from pmb.types import Apkbuild, CrossCompileType, Env
class BootstrapStage(enum.IntEnum): class BootstrapStage(enum.IntEnum):
@ -21,7 +21,9 @@ class BootstrapStage(enum.IntEnum):
# We don't need explicit representations of the other numbers. # We don't need explicit representations of the other numbers.
def override_source(apkbuild, pkgver, src, chroot: Chroot = Chroot.native()): def override_source(
apkbuild: Apkbuild, pkgver: str, src: str | None, chroot: Chroot = Chroot.native()
) -> None:
"""Mount local source inside chroot and append new functions (prepare() etc.) """Mount local source inside chroot and append new functions (prepare() etc.)
to the APKBUILD to make it use the local source. to the APKBUILD to make it use the local source.
""" """
@ -132,7 +134,7 @@ def mount_pmaports(chroot: Chroot = Chroot.native()) -> dict[str, Path]:
return dest_paths return dest_paths
def link_to_git_dir(chroot: Chroot): def link_to_git_dir(chroot: Chroot) -> None:
"""Make ``/home/pmos/build/.git`` point to the .git dir from pmaports.git, with a """Make ``/home/pmos/build/.git`` point to the .git dir from pmaports.git, with a
symlink so abuild does not fail (#1841). symlink so abuild does not fail (#1841).
@ -158,7 +160,7 @@ def link_to_git_dir(chroot: Chroot):
pmb.chroot.user(["ln", "-sf", dest_paths["pmaports"] / ".git", "/home/pmos/build/.git"], chroot) pmb.chroot.user(["ln", "-sf", dest_paths["pmaports"] / ".git", "/home/pmos/build/.git"], chroot)
def handle_csum_failure(apkbuild, chroot: Chroot): def handle_csum_failure(apkbuild: Apkbuild, chroot: Chroot) -> None:
csum_fail_path = chroot / "tmp/apkbuild_verify_failed" csum_fail_path = chroot / "tmp/apkbuild_verify_failed"
if not csum_fail_path.exists(): if not csum_fail_path.exists():
return return
@ -181,17 +183,17 @@ def handle_csum_failure(apkbuild, chroot: Chroot):
def run_abuild( def run_abuild(
context: Context, context: Context,
apkbuild: dict[str, Any], apkbuild: Apkbuild,
pkgver: str, pkgver: str,
channel, channel: str,
arch: Arch, arch: Arch,
strict=False, strict: bool = False,
force=False, force: bool = False,
cross=None, cross: CrossCompileType = None,
suffix: Chroot = Chroot.native(), suffix: Chroot = Chroot.native(),
src=None, src: str | None = None,
bootstrap_stage=BootstrapStage.NONE, bootstrap_stage: int = BootstrapStage.NONE,
): ) -> None:
""" """
Set up all environment variables and construct the abuild command (all Set up all environment variables and construct the abuild command (all
depending on the cross-compiler method and target architecture), copy depending on the cross-compiler method and target architecture), copy
@ -234,7 +236,7 @@ def run_abuild(
) )
# Environment variables # Environment variables
env = {"CARCH": arch, "SUDO_APK": "abuild-apk --no-progress"} env: Env = {"CARCH": str(arch), "SUDO_APK": "abuild-apk --no-progress"}
if cross == "native": if cross == "native":
hostspec = arch.alpine_triple() hostspec = arch.alpine_triple()
env["CROSS_COMPILE"] = hostspec + "-" env["CROSS_COMPILE"] = hostspec + "-"

View file

@ -12,7 +12,7 @@ import pmb.aportgen.core
import pmb.build import pmb.build
import pmb.build.autodetect import pmb.build.autodetect
import pmb.chroot import pmb.chroot
from pmb.types import PathString, PmbArgs from pmb.types import Env, PathString, PmbArgs
import pmb.helpers import pmb.helpers
import pmb.helpers.mount import pmb.helpers.mount
import pmb.helpers.pmaports import pmb.helpers.pmaports
@ -97,7 +97,7 @@ def find_kbuild_output_dir(function_body):
) )
def modify_apkbuild(pkgname: str, aport: Path): def modify_apkbuild(pkgname: str, aport: Path) -> None:
"""Modify kernel APKBUILD to package build output from envkernel.sh.""" """Modify kernel APKBUILD to package build output from envkernel.sh."""
work = get_context().config.work work = get_context().config.work
apkbuild_path = aport / "APKBUILD" apkbuild_path = aport / "APKBUILD"
@ -119,7 +119,9 @@ def modify_apkbuild(pkgname: str, aport: Path):
pmb.aportgen.core.rewrite(pkgname, apkbuild_path, fields=fields) pmb.aportgen.core.rewrite(pkgname, apkbuild_path, fields=fields)
def run_abuild(context: Context, pkgname: str, arch: Arch, apkbuild_path: Path, kbuild_out): def run_abuild(
context: Context, pkgname: str, arch: Arch, apkbuild_path: Path, kbuild_out: str
) -> None:
""" """
Prepare build environment and run abuild. Prepare build environment and run abuild.
@ -190,7 +192,7 @@ def run_abuild(context: Context, pkgname: str, arch: Arch, apkbuild_path: Path,
pmb.helpers.run.root(cmd) pmb.helpers.run.root(cmd)
# Create the apk package # Create the apk package
env = { env: Env = {
"CARCH": str(arch), "CARCH": str(arch),
"CHOST": str(arch), "CHOST": str(arch),
"CBUILD": str(Arch.native()), "CBUILD": str(Arch.native()),
@ -211,7 +213,7 @@ def run_abuild(context: Context, pkgname: str, arch: Arch, apkbuild_path: Path,
pmb.chroot.root(["rm", build_path / "src"]) pmb.chroot.root(["rm", build_path / "src"])
def package_kernel(args: PmbArgs): def package_kernel(args: PmbArgs) -> None:
"""Frontend for 'pmbootstrap build --envkernel': creates a package from envkernel output.""" """Frontend for 'pmbootstrap build --envkernel': creates a package from envkernel output."""
pkgname = args.packages[0] pkgname = args.packages[0]
if len(args.packages) > 1 or not pkgname.startswith("linux-"): if len(args.packages) > 1 or not pkgname.startswith("linux-"):

View file

@ -13,9 +13,10 @@ import pmb.chroot.apk
import pmb.helpers.run import pmb.helpers.run
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.types import CrossCompileType
def init_abuild_minimal(chroot: Chroot = Chroot.native(), build_pkgs: list[str] = []): def init_abuild_minimal(chroot: Chroot = Chroot.native(), build_pkgs: list[str] = []) -> None:
"""Initialize a minimal chroot with abuild where one can do 'abuild checksum'.""" """Initialize a minimal chroot with abuild where one can do 'abuild checksum'."""
marker = chroot / "tmp/pmb_chroot_abuild_init_done" marker = chroot / "tmp/pmb_chroot_abuild_init_done"
if os.path.exists(marker): if os.path.exists(marker):
@ -111,7 +112,9 @@ def init(chroot: Chroot = Chroot.native()) -> bool:
return True return True
def init_compiler(context: Context, depends, cross, arch: Arch): def init_compiler(
context: Context, depends: list[str], cross: CrossCompileType, arch: Arch
) -> None:
arch_str = str(arch) arch_str = str(arch)
cross_pkgs = ["ccache-cross-symlinks", "abuild"] cross_pkgs = ["ccache-cross-symlinks", "abuild"]
if "gcc4" in depends: if "gcc4" in depends:

View file

@ -18,6 +18,7 @@ import pmb.helpers.pmaports
import pmb.helpers.run import pmb.helpers.run
import pmb.parse import pmb.parse
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import Apkbuild, Env
class KConfigUI(enum.Enum): class KConfigUI(enum.Enum):
@ -45,7 +46,7 @@ class KConfigUI(enum.Enum):
return self.value return self.value
def get_arch(apkbuild: dict[str, Any]) -> Arch: def get_arch(apkbuild: Apkbuild) -> Arch:
"""Take the architecture from the APKBUILD or complain if it's ambiguous. """Take the architecture from the APKBUILD or complain if it's ambiguous.
This function only gets called if --arch is not set. This function only gets called if --arch is not set.
@ -76,7 +77,7 @@ def get_arch(apkbuild: dict[str, Any]) -> Arch:
return Arch.from_str(apkbuild["arch"][0]) return Arch.from_str(apkbuild["arch"][0])
def get_outputdir(pkgname: str, apkbuild: dict[str, Any]) -> Path: def get_outputdir(pkgname: str, apkbuild: Apkbuild) -> Path:
"""Get the folder for the kernel compilation output. """Get the folder for the kernel compilation output.
For most APKBUILDs, this is $builddir. But some older ones still use For most APKBUILDs, this is $builddir. But some older ones still use
@ -121,7 +122,7 @@ def get_outputdir(pkgname: str, apkbuild: dict[str, Any]) -> Path:
) )
def extract_and_patch_sources(pkgname: str, arch) -> None: def extract_and_patch_sources(pkgname: str, arch: Arch) -> None:
pmb.build.copy_to_buildpath(pkgname) pmb.build.copy_to_buildpath(pkgname)
logging.info("(native) extract kernel source") logging.info("(native) extract kernel source")
pmb.chroot.user(["abuild", "unpack"], working_dir=Path("/home/pmos/build")) pmb.chroot.user(["abuild", "unpack"], working_dir=Path("/home/pmos/build"))
@ -130,11 +131,18 @@ def extract_and_patch_sources(pkgname: str, arch) -> None:
["abuild", "prepare"], ["abuild", "prepare"],
working_dir=Path("/home/pmos/build"), working_dir=Path("/home/pmos/build"),
output="interactive", output="interactive",
env={"CARCH": arch}, env={"CARCH": str(arch)},
) )
def _make(chroot: pmb.core.Chroot, make_command: str, env, pkgname, arch, apkbuild) -> None: def _make(
chroot: pmb.core.Chroot,
make_command: str,
env: Env,
pkgname: str,
arch: Arch,
apkbuild: Apkbuild,
) -> None:
aport = pmb.helpers.pmaports.find(pkgname) aport = pmb.helpers.pmaports.find(pkgname)
outputdir = get_outputdir(pkgname, apkbuild) outputdir = get_outputdir(pkgname, apkbuild)
@ -155,7 +163,7 @@ def _make(chroot: pmb.core.Chroot, make_command: str, env, pkgname, arch, apkbui
pmb.build.checksum.update(pkgname) pmb.build.checksum.update(pkgname)
def _init(pkgname: str, arch: Arch | None) -> tuple[str, Arch, Any, Chroot, dict[str, str]]: def _init(pkgname: str, arch: Arch | None) -> tuple[str, Arch, Any, Chroot, Env]:
""" """
:returns: pkgname, arch, apkbuild, chroot, env :returns: pkgname, arch, apkbuild, chroot, env
""" """

View file

@ -4,6 +4,7 @@ import enum
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
from typing import Any
import shlex import shlex
import datetime import datetime
@ -17,10 +18,13 @@ import pmb.helpers.run
import pmb.parse.apkindex import pmb.parse.apkindex
import pmb.parse.version import pmb.parse.version
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.arch import Arch
from pmb.core.context import get_context from pmb.core.context import get_context
def copy_to_buildpath(package, chroot: Chroot = Chroot.native(), no_override: bool = False): def copy_to_buildpath(
package: str, chroot: Chroot = Chroot.native(), no_override: bool = False
) -> None:
# Sanity check # Sanity check
aport = pmb.helpers.pmaports.find(package) aport = pmb.helpers.pmaports.find(package)
if not os.path.exists(aport / "APKBUILD"): if not os.path.exists(aport / "APKBUILD"):
@ -49,7 +53,7 @@ def copy_to_buildpath(package, chroot: Chroot = Chroot.native(), no_override: bo
pmb.chroot.root(["chown", "-R", "pmos:pmos", "/home/pmos/build"], chroot) pmb.chroot.root(["chown", "-R", "pmos:pmos", "/home/pmos/build"], chroot)
def abuild_overrides(apkbuild: Path): def abuild_overrides(apkbuild: Path) -> None:
"""Override some abuild functions by patching the APKBUILD file.""" """Override some abuild functions by patching the APKBUILD file."""
if apkbuild.is_relative_to(get_context().config.work / "cache_git"): if apkbuild.is_relative_to(get_context().config.work / "cache_git"):
@ -77,7 +81,7 @@ class BuildStatus(enum.Enum):
return self in [BuildStatus.OUTDATED, BuildStatus.NEW] return self in [BuildStatus.OUTDATED, BuildStatus.NEW]
def get_status(arch, apkbuild) -> BuildStatus: def get_status(arch: Arch | None, apkbuild: dict[str, Any]) -> BuildStatus:
"""Check if the package has already been built. """Check if the package has already been built.
Compared to abuild's check, this check also works for different architectures. Compared to abuild's check, this check also works for different architectures.
@ -170,7 +174,7 @@ def index_repo(arch=None):
pmb.parse.apkindex.clear_cache(path / "APKINDEX.tar.gz") pmb.parse.apkindex.clear_cache(path / "APKINDEX.tar.gz")
def configure_abuild(chroot: Chroot, verify=False): def configure_abuild(chroot: Chroot, verify: bool = False) -> None:
"""Set the correct JOBS count in ``abuild.conf``. """Set the correct JOBS count in ``abuild.conf``.
:param verify: internally used to test if changing the config has worked. :param verify: internally used to test if changing the config has worked.
@ -198,7 +202,7 @@ def configure_abuild(chroot: Chroot, verify=False):
pmb.chroot.root(["sed", "-i", f"$ a\\{prefix}{jobs}", "/etc/abuild.conf"], chroot) pmb.chroot.root(["sed", "-i", f"$ a\\{prefix}{jobs}", "/etc/abuild.conf"], chroot)
def configure_ccache(chroot: Chroot = Chroot.native(), verify=False): def configure_ccache(chroot: Chroot = Chroot.native(), verify: bool = False) -> None:
"""Set the maximum ccache size. """Set the maximum ccache size.
:param verify: internally used to test if changing the config has worked. :param verify: internally used to test if changing the config has worked.

View file

@ -32,8 +32,11 @@ from pmb.helpers.exceptions import NonBugError
@Cache("chroot", "user_repository", mirrors_exclude=[]) @Cache("chroot", "user_repository", mirrors_exclude=[])
def update_repository_list( def update_repository_list(
chroot: Chroot, user_repository=False, mirrors_exclude: list[str] = [], check=False chroot: Chroot,
): user_repository: bool = False,
mirrors_exclude: list[str] = [],
check: bool = False,
) -> None:
""" """
Update /etc/apk/repositories, if it is outdated (when the user changed the Update /etc/apk/repositories, if it is outdated (when the user changed the
--mirror-alpine or --mirror-pmOS parameters). --mirror-alpine or --mirror-pmOS parameters).
@ -79,7 +82,7 @@ def update_repository_list(
@Cache("chroot") @Cache("chroot")
def check_min_version(chroot: Chroot = Chroot.native()): def check_min_version(chroot: Chroot = Chroot.native()) -> None:
""" """
Check the minimum apk version, before running it the first time in the Check the minimum apk version, before running it the first time in the
current session (lifetime of one pmbootstrap call). current session (lifetime of one pmbootstrap call).
@ -179,7 +182,9 @@ def packages_get_locally_built_apks(package_list: list[str], arch: Arch) -> list
# FIXME: list[Sequence[PathString]] weirdness # FIXME: list[Sequence[PathString]] weirdness
# mypy: disable-error-code="operator" # mypy: disable-error-code="operator"
def install_run_apk(to_add: list[str], to_add_local: list[Path], to_del: list[str], chroot: Chroot): def install_run_apk(
to_add: list[str], to_add_local: list[Path], to_del: list[str], chroot: Chroot
) -> None:
""" """
Run apk to add packages, and ensure only the desired packages get Run apk to add packages, and ensure only the desired packages get
explicitly marked as installed. explicitly marked as installed.
@ -248,7 +253,7 @@ def install_run_apk(to_add: list[str], to_add_local: list[Path], to_del: list[st
pmb.chroot.root(["apk", "--no-progress"] + command, chroot) pmb.chroot.root(["apk", "--no-progress"] + command, chroot)
def install(packages, chroot: Chroot, build=True, quiet: bool = False): def install(packages: list[str], chroot: Chroot, build: bool = True, quiet: bool = False) -> None:
""" """
Install packages from pmbootstrap's local package index or the pmOS/Alpine Install packages from pmbootstrap's local package index or the pmOS/Alpine
binary package mirrors. Iterate over all dependencies recursively, and binary package mirrors. Iterate over all dependencies recursively, and

View file

@ -11,11 +11,11 @@ import pmb.parse
import pmb.chroot.apk import pmb.chroot.apk
def is_registered(arch_qemu: Arch): def is_registered(arch_qemu: Arch) -> bool:
return os.path.exists(f"/proc/sys/fs/binfmt_misc/qemu-{arch_qemu}") return os.path.exists(f"/proc/sys/fs/binfmt_misc/qemu-{arch_qemu}")
def register(arch: Arch): def register(arch: Arch) -> None:
""" """
Get arch, magic, mask. Get arch, magic, mask.
""" """
@ -56,7 +56,7 @@ def register(arch: Arch):
pmb.helpers.run.root(["sh", "-c", 'echo "' + code + '" > ' + register]) pmb.helpers.run.root(["sh", "-c", 'echo "' + code + '" > ' + register])
def unregister(arch: Arch): def unregister(arch: Arch) -> None:
arch_qemu = arch.qemu() arch_qemu = arch.qemu()
binfmt_file = "/proc/sys/fs/binfmt_misc/qemu-" + arch_qemu binfmt_file = "/proc/sys/fs/binfmt_misc/qemu-" + arch_qemu
if not os.path.exists(binfmt_file): if not os.path.exists(binfmt_file):

View file

@ -30,7 +30,7 @@ class UsrMerge(enum.Enum):
OFF = 2 OFF = 2
def copy_resolv_conf(chroot: Chroot): def copy_resolv_conf(chroot: Chroot) -> None:
""" """
Use pythons super fast file compare function (due to caching) Use pythons super fast file compare function (due to caching)
and copy the /etc/resolv.conf to the chroot, in case it is and copy the /etc/resolv.conf to the chroot, in case it is
@ -46,7 +46,7 @@ def copy_resolv_conf(chroot: Chroot):
pmb.helpers.run.root(["touch", resolv_path]) pmb.helpers.run.root(["touch", resolv_path])
def mark_in_chroot(chroot: Chroot = Chroot.native()): def mark_in_chroot(chroot: Chroot = Chroot.native()) -> None:
""" """
Touch a flag so we can know when we're running in chroot (and Touch a flag so we can know when we're running in chroot (and
don't accidentally flash partitions on our host). This marker don't accidentally flash partitions on our host). This marker
@ -74,7 +74,7 @@ def init_keys():
pmb.helpers.run.root(["cp", key, target]) pmb.helpers.run.root(["cp", key, target])
def init_usr_merge(chroot: Chroot): def init_usr_merge(chroot: Chroot) -> None:
logging.info(f"({chroot}) merge /usr") logging.info(f"({chroot}) merge /usr")
script = f"{pmb.config.pmb_src}/pmb/data/merge-usr.sh" script = f"{pmb.config.pmb_src}/pmb/data/merge-usr.sh"
pmb.helpers.run.root(["sh", "-e", script, "CALLED_FROM_PMB", chroot.path]) pmb.helpers.run.root(["sh", "-e", script, "CALLED_FROM_PMB", chroot.path])
@ -104,7 +104,7 @@ def warn_if_chroots_outdated():
@Cache("chroot") @Cache("chroot")
def init(chroot: Chroot, usr_merge=UsrMerge.AUTO): def init(chroot: Chroot, usr_merge: UsrMerge = UsrMerge.AUTO) -> None:
""" """
Initialize a chroot by copying the resolv.conf and updating Initialize a chroot by copying the resolv.conf and updating
/etc/apk/repositories. If /bin/sh is missing, create the chroot from /etc/apk/repositories. If /bin/sh is missing, create the chroot from

View file

@ -12,7 +12,7 @@ from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def build(flavor, chroot: Chroot): def build(flavor: str | None, chroot: Chroot) -> None:
# Update mkinitfs and hooks # Update mkinitfs and hooks
pmb.chroot.apk.install(["postmarketos-mkinitfs"], chroot) pmb.chroot.apk.install(["postmarketos-mkinitfs"], chroot)
pmb.chroot.initfs_hooks.update(chroot) pmb.chroot.initfs_hooks.update(chroot)
@ -31,7 +31,7 @@ def build(flavor, chroot: Chroot):
pmb.chroot.root(["mkinitfs", "-o", f"/boot/initramfs-{flavor}", release], chroot) pmb.chroot.root(["mkinitfs", "-o", f"/boot/initramfs-{flavor}", release], chroot)
def extract(flavor, chroot: Chroot, extra=False): def extract(flavor: str | None, chroot: Chroot, extra: bool = False) -> Path:
""" """
Extract the initramfs to /tmp/initfs-extracted or the initramfs-extra to Extract the initramfs to /tmp/initfs-extracted or the initramfs-extra to
/tmp/initfs-extra-extracted and return the outside extraction path. /tmp/initfs-extra-extracted and return the outside extraction path.
@ -86,7 +86,7 @@ def ls(flavor, suffix, extra=False):
pmb.chroot.root(["rm", "-r", tmp], suffix) pmb.chroot.root(["rm", "-r", tmp], suffix)
def frontend(args: PmbArgs): def frontend(args: PmbArgs) -> None:
# Find the appropriate kernel flavor # Find the appropriate kernel flavor
context = get_context() context = get_context()
chroot = Chroot.rootfs(context.config.device) chroot = Chroot.rootfs(context.config.device)

View file

@ -9,7 +9,7 @@ import pmb.chroot.apk
from pmb.core import Chroot from pmb.core import Chroot
def list_chroot(suffix: Chroot, remove_prefix=True): def list_chroot(suffix: Chroot, remove_prefix: bool = True) -> list[str]:
ret = [] ret = []
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
for pkgname in pmb.chroot.apk.installed(suffix).keys(): for pkgname in pmb.chroot.apk.installed(suffix).keys():
@ -21,7 +21,7 @@ def list_chroot(suffix: Chroot, remove_prefix=True):
return ret return ret
def list_aports(): def list_aports() -> list[str]:
ret = [] ret = []
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
for path in pkgrepo_iglob(f"*/{prefix}*"): for path in pkgrepo_iglob(f"*/{prefix}*"):
@ -29,7 +29,7 @@ def list_aports():
return ret return ret
def ls(suffix: Chroot): def ls(suffix: Chroot) -> None:
hooks_chroot = list_chroot(suffix) hooks_chroot = list_chroot(suffix)
hooks_aports = list_aports() hooks_aports = list_aports()
@ -38,7 +38,7 @@ def ls(suffix: Chroot):
logging.info(line) logging.info(line)
def add(hook, suffix: Chroot): def add(hook: str, suffix: Chroot) -> None:
if hook not in list_aports(): if hook not in list_aports():
raise RuntimeError( raise RuntimeError(
"Invalid hook name!" " Run 'pmbootstrap initfs hook_ls'" " to get a list of all hooks." "Invalid hook name!" " Run 'pmbootstrap initfs hook_ls'" " to get a list of all hooks."
@ -47,14 +47,14 @@ def add(hook, suffix: Chroot):
pmb.chroot.apk.install([f"{prefix}{hook}"], suffix) pmb.chroot.apk.install([f"{prefix}{hook}"], suffix)
def delete(hook, suffix: Chroot): def delete(hook: str, suffix: Chroot) -> None:
if hook not in list_chroot(suffix): if hook not in list_chroot(suffix):
raise RuntimeError("There is no such hook installed!") raise RuntimeError("There is no such hook installed!")
prefix = pmb.config.initfs_hook_prefix prefix = pmb.config.initfs_hook_prefix
pmb.chroot.root(["apk", "del", f"{prefix}{hook}"], suffix) pmb.chroot.root(["apk", "del", f"{prefix}{hook}"], suffix)
def update(suffix: Chroot): def update(suffix: Chroot) -> None:
""" """
Rebuild and update all hooks that are out of date Rebuild and update all hooks that are out of date
""" """

View file

@ -15,7 +15,7 @@ from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def mount_chroot_image(chroot: Chroot): def mount_chroot_image(chroot: Chroot) -> None:
"""Mount an IMAGE type chroot, to modify an existing rootfs image. This """Mount an IMAGE type chroot, to modify an existing rootfs image. This
doesn't support split images yet!""" doesn't support split images yet!"""
# Make sure everything is nicely unmounted just to be super safe # Make sure everything is nicely unmounted just to be super safe
@ -42,7 +42,7 @@ def mount_chroot_image(chroot: Chroot):
logging.info(f"({chroot}) mounted {chroot.name}") logging.info(f"({chroot}) mounted {chroot.name}")
def create_device_nodes(chroot: Chroot): def create_device_nodes(chroot: Chroot) -> None:
""" """
Create device nodes for null, zero, full, random, urandom in the chroot. Create device nodes for null, zero, full, random, urandom in the chroot.
""" """
@ -90,7 +90,7 @@ def create_device_nodes(chroot: Chroot):
raise RuntimeError(f"Failed to create device nodes in the '{chroot}' chroot.") raise RuntimeError(f"Failed to create device nodes in the '{chroot}' chroot.")
def mount_dev_tmpfs(chroot: Chroot = Chroot.native()): def mount_dev_tmpfs(chroot: Chroot = Chroot.native()) -> None:
""" """
Mount tmpfs inside the chroot's dev folder to make sure we can create Mount tmpfs inside the chroot's dev folder to make sure we can create
device nodes, even if the filesystem of the work folder does not support device nodes, even if the filesystem of the work folder does not support
@ -116,7 +116,7 @@ def mount_dev_tmpfs(chroot: Chroot = Chroot.native()):
pmb.helpers.run.root(["ln", "-sf", "/proc/self/fd", f"{dev}/"]) pmb.helpers.run.root(["ln", "-sf", "/proc/self/fd", f"{dev}/"])
def mount(chroot: Chroot): def mount(chroot: Chroot) -> None:
if chroot.type == ChrootType.IMAGE and not pmb.mount.ismount(chroot.path): if chroot.type == ChrootType.IMAGE and not pmb.mount.ismount(chroot.path):
mount_chroot_image(chroot) mount_chroot_image(chroot)
@ -154,7 +154,7 @@ def mount(chroot: Chroot):
) )
def mount_native_into_foreign(chroot: Chroot): def mount_native_into_foreign(chroot: Chroot) -> None:
source = Chroot.native().path source = Chroot.native().path
target = chroot / "native" target = chroot / "native"
pmb.helpers.mount.bind(source, target) pmb.helpers.mount.bind(source, target)
@ -166,7 +166,7 @@ def mount_native_into_foreign(chroot: Chroot):
# pmb.helpers.run.root(["ln", "-sf", "/native/usr/bin/pigz", "/usr/local/bin/pigz"]) # pmb.helpers.run.root(["ln", "-sf", "/native/usr/bin/pigz", "/usr/local/bin/pigz"])
def remove_mnt_pmbootstrap(chroot: Chroot): def remove_mnt_pmbootstrap(chroot: Chroot) -> None:
"""Safely remove /mnt/pmbootstrap directories from the chroot, without """Safely remove /mnt/pmbootstrap directories from the chroot, without
running rm -r as root and potentially removing data inside the running rm -r as root and potentially removing data inside the
mountpoint in case it was still mounted (bug in pmbootstrap, or user mountpoint in case it was still mounted (bug in pmbootstrap, or user

View file

@ -8,7 +8,7 @@ import pmb.install
from pmb.core import Chroot from pmb.core import Chroot
def kernel_flavor_installed(chroot: Chroot, autoinstall=True) -> str | None: def kernel_flavor_installed(chroot: Chroot, autoinstall: bool = True) -> str | None:
""" """
Get installed kernel flavor. Optionally install the device's kernel Get installed kernel flavor. Optionally install the device's kernel
beforehand. beforehand.

View file

@ -3,7 +3,9 @@
import os import os
from pathlib import Path, PurePath from pathlib import Path, PurePath
import shutil import shutil
import subprocess
from collections.abc import Sequence from collections.abc import Sequence
from typing import overload, Literal
import pmb.config import pmb.config
import pmb.chroot import pmb.chroot
@ -11,7 +13,14 @@ import pmb.chroot.binfmt
import pmb.helpers.run import pmb.helpers.run
import pmb.helpers.run_core import pmb.helpers.run_core
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import Env, PathString from pmb.types import (
Env,
PathString,
RunOutputType,
RunOutputTypeDefault,
RunOutputTypePopen,
RunReturnType,
)
def executables_absolute_path(): def executables_absolute_path():
@ -35,13 +44,13 @@ def rootm(
cmds: Sequence[Sequence[PathString]], cmds: Sequence[Sequence[PathString]],
chroot: Chroot = Chroot.native(), chroot: Chroot = Chroot.native(),
working_dir: PurePath = PurePath("/"), working_dir: PurePath = PurePath("/"),
output="log", output: RunOutputType = "log",
output_return=False, output_return: bool = False,
check=None, check: bool | None = None,
env={}, env: Env = {},
disable_timeout=False, disable_timeout: bool = False,
add_proxy_env_vars=True, add_proxy_env_vars: bool = True,
): ) -> RunReturnType:
""" """
Run a list of commands inside a chroot as root. Run a list of commands inside a chroot as root.
@ -110,17 +119,59 @@ def rootm(
) )
@overload
def root(
cmds: Sequence[PathString],
chroot: Chroot = ...,
working_dir: PurePath = ...,
output: RunOutputTypePopen = ...,
output_return: Literal[False] = ...,
check: bool | None = ...,
env: Env = ...,
disable_timeout: bool = ...,
add_proxy_env_vars: bool = ...,
) -> subprocess.Popen: ...
@overload
def root(
cmds: Sequence[PathString],
chroot: Chroot = ...,
working_dir: PurePath = ...,
output: RunOutputTypeDefault = ...,
output_return: Literal[False] = ...,
check: bool | None = ...,
env: Env = ...,
disable_timeout: bool = ...,
add_proxy_env_vars: bool = ...,
) -> int: ...
@overload
def root(
cmds: Sequence[PathString],
chroot: Chroot = ...,
working_dir: PurePath = ...,
output: RunOutputType = ...,
output_return: Literal[True] = ...,
check: bool | None = ...,
env: Env = ...,
disable_timeout: bool = ...,
add_proxy_env_vars: bool = ...,
) -> str: ...
def root( def root(
cmds: Sequence[PathString], cmds: Sequence[PathString],
chroot: Chroot = Chroot.native(), chroot: Chroot = Chroot.native(),
working_dir: PurePath = PurePath("/"), working_dir: PurePath = PurePath("/"),
output="log", output: RunOutputType = "log",
output_return=False, output_return: bool = False,
check=None, check: bool | None = None,
env={}, env: Env = {},
disable_timeout=False, disable_timeout: bool = False,
add_proxy_env_vars=True, add_proxy_env_vars: bool = True,
): ) -> RunReturnType:
return rootm( return rootm(
[cmds], [cmds],
chroot, chroot,
@ -138,11 +189,11 @@ def userm(
cmds: Sequence[Sequence[PathString]], cmds: Sequence[Sequence[PathString]],
chroot: Chroot = Chroot.native(), chroot: Chroot = Chroot.native(),
working_dir: Path = Path("/"), working_dir: Path = Path("/"),
output="log", output: RunOutputType = "log",
output_return=False, output_return: bool = False,
check=None, check: bool | None = None,
env={}, env: Env = {},
): ) -> RunReturnType:
""" """
Run a command inside a chroot as "user". We always use the BusyBox Run a command inside a chroot as "user". We always use the BusyBox
implementation of 'su', because other implementations may override the PATH implementation of 'su', because other implementations may override the PATH
@ -162,24 +213,61 @@ def userm(
flat_cmd = pmb.helpers.run_core.flat_cmd(cmds, env=env) flat_cmd = pmb.helpers.run_core.flat_cmd(cmds, env=env)
cmd = ["busybox", "su", "pmos", "-c", flat_cmd] cmd = ["busybox", "su", "pmos", "-c", flat_cmd]
return pmb.chroot.root( # Can't figure out why this one fails :(
return pmb.chroot.root( # type: ignore[call-overload]
cmd, chroot, working_dir, output, output_return, check, {}, add_proxy_env_vars=False cmd, chroot, working_dir, output, output_return, check, {}, add_proxy_env_vars=False
) )
@overload
def user(
cmd: Sequence[PathString],
chroot: Chroot = ...,
working_dir: Path = ...,
output: RunOutputTypePopen = ...,
output_return: Literal[False] = ...,
check: bool | None = ...,
env: Env = ...,
) -> subprocess.Popen: ...
@overload
def user(
cmd: Sequence[PathString],
chroot: Chroot = ...,
working_dir: Path = ...,
output: RunOutputTypeDefault = ...,
output_return: Literal[False] = ...,
check: bool | None = ...,
env: Env = ...,
) -> int: ...
@overload
def user(
cmd: Sequence[PathString],
chroot: Chroot = ...,
working_dir: Path = ...,
output: RunOutputType = ...,
output_return: Literal[True] = ...,
check: bool | None = ...,
env: Env = ...,
) -> str: ...
def user( def user(
cmd: Sequence[PathString], cmd: Sequence[PathString],
chroot: Chroot = Chroot.native(), chroot: Chroot = Chroot.native(),
working_dir: Path = Path("/"), working_dir: Path = Path("/"),
output="log", output: RunOutputType = "log",
output_return=False, output_return: bool = False,
check=None, check: bool | None = None,
env={}, env: Env = {},
): ) -> RunReturnType:
return userm([cmd], chroot, working_dir, output, output_return, check, env) return userm([cmd], chroot, working_dir, output, output_return, check, env)
def exists(username, chroot: Chroot = Chroot.native()): def exists(username: str, chroot: Chroot = Chroot.native()) -> bool:
""" """
Checks if username exists in the system Checks if username exists in the system

View file

@ -33,7 +33,7 @@ def kill_sccache():
pmb.chroot.root(["sccache", "--stop-server"]) pmb.chroot.root(["sccache", "--stop-server"])
def shutdown_cryptsetup_device(name: str): def shutdown_cryptsetup_device(name: str) -> None:
""" """
:param name: cryptsetup device name, usually "pm_crypt" in pmbootstrap :param name: cryptsetup device name, usually "pm_crypt" in pmbootstrap
""" """

View file

@ -20,7 +20,7 @@ from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def del_chroot(path: Path, confirm=True, dry=False): def del_chroot(path: Path, confirm: bool = True, dry: bool = False) -> None:
if confirm and not pmb.helpers.cli.confirm(f"Remove {path}?"): if confirm and not pmb.helpers.cli.confirm(f"Remove {path}?"):
return return
if dry: if dry:
@ -37,16 +37,16 @@ def del_chroot(path: Path, confirm=True, dry=False):
def zap( def zap(
confirm=True, confirm: bool = True,
dry=False, dry: bool = False,
pkgs_local=False, pkgs_local: bool = False,
http=False, http: bool = False,
pkgs_local_mismatch=False, pkgs_local_mismatch: bool = False,
pkgs_online_mismatch=False, pkgs_online_mismatch: bool = False,
distfiles=False, distfiles: bool = False,
rust=False, rust: bool = False,
netboot=False, netboot: bool = False,
): ) -> None:
""" """
Shutdown everything inside the chroots (e.g. adb), umount Shutdown everything inside the chroots (e.g. adb), umount
everything and then safely remove folders from the work-directory. everything and then safely remove folders from the work-directory.

View file

@ -6,7 +6,7 @@ from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
import pmb.chroot import pmb.chroot
from pmb.types import PmbArgs from pmb.types import Env, PmbArgs
import pmb.helpers.cli import pmb.helpers.cli
from pmb.core import Chroot from pmb.core import Chroot
@ -176,7 +176,7 @@ def run_scripts(topdir, scripts):
copy_git_repo_to_chroot(topdir) copy_git_repo_to_chroot(topdir)
repo_copied = True repo_copied = True
env = {"TESTUSER": "pmos"} env: Env = {"TESTUSER": "pmos"}
rc = pmb.chroot.root( rc = pmb.chroot.root(
[script_path], check=False, env=env, working_dir=Path("/home/pmos/ci"), output="tui" [script_path], check=False, env=env, working_dir=Path("/home/pmos/ci"), output="tui"
) )

View file

@ -55,7 +55,7 @@ unmigrated_commands = [
] ]
def run_command(args: PmbArgs): def run_command(args: PmbArgs) -> None:
# Handle deprecated command format # Handle deprecated command format
if args.action in unmigrated_commands: if args.action in unmigrated_commands:
getattr(frontend, args.action)(args) getattr(frontend, args.action)(args)

View file

@ -114,7 +114,7 @@ class RepoBootstrap(commands.Command):
bootstrap_stage = int(step.split("bootstrap_", 1)[1]) bootstrap_stage = int(step.split("bootstrap_", 1)[1])
def log_wrapper(pkg: BuildQueueItem): def log_wrapper(pkg: BuildQueueItem) -> None:
self.log_progress(f"building {pkg['name']}") self.log_progress(f"building {pkg['name']}")
packages = self.get_packages(bootstrap_line) packages = self.get_packages(bootstrap_line)

View file

@ -50,7 +50,7 @@ def load(path: Path) -> Config:
return config return config
def serialize(config: Config, skip_defaults=True) -> configparser.ConfigParser: def serialize(config: Config, skip_defaults: bool = True) -> configparser.ConfigParser:
"""Serialize the config object into a ConfigParser to write it out """Serialize the config object into a ConfigParser to write it out
in the pmbootstrap_v3.cfg INI format. in the pmbootstrap_v3.cfg INI format.
@ -91,7 +91,7 @@ def serialize(config: Config, skip_defaults=True) -> configparser.ConfigParser:
# FIXME: we should have distinct Config and ConfigFile types # FIXME: we should have distinct Config and ConfigFile types
def save(output: Path, config: Config): def save(output: Path, config: Config) -> None:
"""Save the config object to the specified path. """Save the config object to the specified path.
IMPORTANT: The global config (available via get_context().config) IMPORTANT: The global config (available via get_context().config)

View file

@ -210,7 +210,7 @@ def ask_for_ui(deviceinfo: Deviceinfo) -> str:
) )
def ask_for_ui_extras(config: Config, ui): def ask_for_ui_extras(config: Config, ui: str) -> bool:
apkbuild = pmb.helpers.pmaports.get( apkbuild = pmb.helpers.pmaports.get(
f"postmarketos-ui-{ui}", subpackages=False, must_exist=False f"postmarketos-ui-{ui}", subpackages=False, must_exist=False
) )
@ -226,7 +226,7 @@ def ask_for_ui_extras(config: Config, ui):
return pmb.helpers.cli.confirm("Enable this package?", default=config.ui_extras) return pmb.helpers.cli.confirm("Enable this package?", default=config.ui_extras)
def ask_for_systemd(config: Config, ui): def ask_for_systemd(config: Config, ui: str) -> SystemdConfig:
if "systemd" not in pmb.config.pmaports.read_config_repos(): if "systemd" not in pmb.config.pmaports.read_config_repos():
return config.systemd return config.systemd
@ -292,7 +292,7 @@ def ask_for_timezone() -> str:
return "GMT" return "GMT"
def ask_for_provider_select(apkbuild, providers_cfg) -> None: def ask_for_provider_select(apkbuild: dict[str, Any], providers_cfg: dict[str, str]) -> None:
"""Ask for selectable providers that are specified using "_pmb_select" in a APKBUILD. """Ask for selectable providers that are specified using "_pmb_select" in a APKBUILD.
:param apkbuild: the APKBUILD with the _pmb_select :param apkbuild: the APKBUILD with the _pmb_select

View file

@ -8,7 +8,7 @@ from pmb.meta import Cache
@Cache() @Cache()
def is_systemd_selected(config: Config): def is_systemd_selected(config: Config) -> bool:
if "systemd" not in pmb.config.pmaports.read_config_repos(): if "systemd" not in pmb.config.pmaports.read_config_repos():
return False return False
if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never", skip_extra_repos=True): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never", skip_extra_repos=True):
@ -20,7 +20,7 @@ def is_systemd_selected(config: Config):
return pmb.helpers.ui.check_option(config.ui, "pmb:systemd", skip_extra_repos=True) return pmb.helpers.ui.check_option(config.ui, "pmb:systemd", skip_extra_repos=True)
def systemd_selected_str(config: Config): def systemd_selected_str(config: Config) -> tuple[str, str]:
if "systemd" not in pmb.config.pmaports.read_config_repos(): if "systemd" not in pmb.config.pmaports.read_config_repos():
return "no", "not supported by pmaports branch" return "no", "not supported by pmaports branch"
if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never"): if pmb.helpers.ui.check_option(config.ui, "pmb:systemd-never"):

View file

@ -6,6 +6,7 @@ from pmb.core.pkgrepo import pkgrepo_default_path, pkgrepo_paths, pkgrepo_relati
from pmb.helpers import logging from pmb.helpers import logging
import os import os
import sys import sys
from typing import cast, Any
import pmb.config import pmb.config
from pmb.meta import Cache from pmb.meta import Cache
@ -14,7 +15,7 @@ import pmb.helpers.pmaports
import pmb.parse.version import pmb.parse.version
def clone(): def clone() -> None:
logging.info( logging.info(
"Setting up the native chroot and cloning the package build" " recipes (pmaports)..." "Setting up the native chroot and cloning the package build" " recipes (pmaports)..."
) )
@ -23,7 +24,7 @@ def clone():
pmb.helpers.git.clone("pmaports") pmb.helpers.git.clone("pmaports")
def check_version_pmaports(real): def check_version_pmaports(real: str) -> None:
# Compare versions # Compare versions
min = pmb.config.pmaports_min_version min = pmb.config.pmaports_min_version
if pmb.parse.version.compare(real, min) >= 0: if pmb.parse.version.compare(real, min) >= 0:
@ -41,7 +42,7 @@ def check_version_pmaports(real):
raise RuntimeError("Run 'pmbootstrap pull' to update your pmaports.") raise RuntimeError("Run 'pmbootstrap pull' to update your pmaports.")
def check_version_pmbootstrap(min_ver): def check_version_pmbootstrap(min_ver: str) -> None:
# Compare versions # Compare versions
real = pmb.__version__ real = pmb.__version__
if pmb.parse.version.compare(real, min_ver) >= 0: if pmb.parse.version.compare(real, min_ver) >= 0:
@ -71,7 +72,7 @@ def check_version_pmbootstrap(min_ver):
@Cache() @Cache()
def read_config_repos(): def read_config_repos() -> dict[str, configparser.SectionProxy]:
"""Read the sections starting with "repo:" from pmaports.cfg.""" """Read the sections starting with "repo:" from pmaports.cfg."""
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
@ -88,7 +89,7 @@ def read_config_repos():
@Cache("aports") @Cache("aports")
def read_config(aports: Path | None = None): def read_config(aports: Path | None = None) -> dict[str, Any]:
"""Read and verify pmaports.cfg. If aports is not """Read and verify pmaports.cfg. If aports is not
specified and systemd is enabled, the returned channel specified and systemd is enabled, the returned channel
will be the systemd one (e.g. systemd-edge instead of edge) will be the systemd one (e.g. systemd-edge instead of edge)
@ -128,7 +129,8 @@ def read_config(aports: Path | None = None):
if systemd: if systemd:
ret["channel"] = "systemd-" + ret["channel"] ret["channel"] = "systemd-" + ret["channel"]
return ret # FIXME: This is a hack to work around python/typeshed issue #12919
return cast(dict[str, Any], ret)
def all_channels() -> list[str]: def all_channels() -> list[str]:
@ -141,7 +143,7 @@ def all_channels() -> list[str]:
return list(ret) return list(ret)
def read_config_channel(): def read_config_channel() -> dict[str, str]:
"""Get the properties of the currently active channel in pmaports.git. """Get the properties of the currently active channel in pmaports.git.
As specified in channels.cfg (https://postmarketos.org/channels.cfg). As specified in channels.cfg (https://postmarketos.org/channels.cfg).
@ -180,13 +182,13 @@ def read_config_channel():
) )
def init(): def init() -> None:
if not os.path.exists(pkgrepo_default_path()): if not os.path.exists(pkgrepo_default_path()):
clone() clone()
read_config() read_config()
def switch_to_channel_branch(channel_new): def switch_to_channel_branch(channel_new: str) -> bool:
"""Checkout the channel's branch in pmaports.git. """Checkout the channel's branch in pmaports.git.
:channel_new: channel name (e.g. "edge", "v21.03") :channel_new: channel name (e.g. "edge", "v21.03")
@ -236,7 +238,7 @@ def switch_to_channel_branch(channel_new):
return True return True
def install_githooks(): def install_githooks() -> None:
aports = pkgrepo_default_path() aports = pkgrepo_default_path()
hooks_dir = aports / ".githooks" hooks_dir = aports / ".githooks"
if not hooks_dir.exists(): if not hooks_dir.exists():

View file

@ -17,7 +17,7 @@ from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
def chroot_save_init(suffix: Chroot): def chroot_save_init(suffix: Chroot) -> None:
"""Save the chroot initialization data in $WORK/workdir.cfg.""" """Save the chroot initialization data in $WORK/workdir.cfg."""
# Read existing cfg # Read existing cfg
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
@ -48,7 +48,7 @@ def chroots_outdated() -> list[Chroot]: ...
def chroots_outdated(chroot: Chroot) -> bool: ... def chroots_outdated(chroot: Chroot) -> bool: ...
def chroots_outdated(chroot: Chroot | None = None): def chroots_outdated(chroot: Chroot | None = None) -> bool | list[Chroot]:
"""Check if init dates from workdir.cfg indicate that any chroot is """Check if init dates from workdir.cfg indicate that any chroot is
outdated. outdated.

View file

@ -107,7 +107,7 @@ class Config:
else: else:
raise ValueError(f"Invalid dotted key: {dotted_key}") raise ValueError(f"Invalid dotted key: {dotted_key}")
def __setattr__(self, key: str, value: Any): def __setattr__(self, key: str, value: Any) -> None:
"""Allow for setattr() to be used with a dotted key """Allow for setattr() to be used with a dotted key
to set nested dictionaries (e.g. "mirrors.alpine").""" to set nested dictionaries (e.g. "mirrors.alpine")."""
keys = key.split(".") keys = key.split(".")

View file

@ -56,7 +56,7 @@ def get_context(allow_failure: bool = False) -> Context:
return __context return __context
def set_context(context: Context): def set_context(context: Context) -> None:
"""Set global runtime context.""" """Set global runtime context."""
global __context global __context

View file

@ -9,7 +9,7 @@ from pmb.meta import Cache
@Cache(skip_extras=False) @Cache(skip_extras=False)
def pkgrepo_paths(skip_extras=False) -> list[Path]: def pkgrepo_paths(skip_extras: bool = False) -> list[Path]:
config = get_context().config config = get_context().config
paths = list(map(lambda x: Path(x), config.aports)) paths = list(map(lambda x: Path(x), config.aports))
if not paths: if not paths:
@ -32,7 +32,7 @@ def pkgrepo_default_path() -> Path:
return pkgrepo_paths(skip_extras=True)[0] return pkgrepo_paths(skip_extras=True)[0]
def pkgrepo_names(skip_exras=False) -> list[str]: def pkgrepo_names(skip_exras: bool = False) -> list[str]:
""" """
Return a list of all the package repository names. Return a list of all the package repository names.
""" """
@ -78,7 +78,7 @@ def pkgrepo_glob_one(path: str) -> Path | None:
return None return None
def pkgrepo_iglob(path: str, recursive=False) -> Generator[Path, None, None]: def pkgrepo_iglob(path: str, recursive: bool = False) -> Generator[Path, None, None]:
""" """
Yield each matching glob over each aports repository. Yield each matching glob over each aports repository.
""" """
@ -91,7 +91,7 @@ def pkgrepo_iglob(path: str, recursive=False) -> Generator[Path, None, None]:
yield pdir yield pdir
def pkgrepo_iter_package_dirs(skip_extra_repos=False) -> Generator[Path, None, None]: def pkgrepo_iter_package_dirs(skip_extra_repos: bool = False) -> Generator[Path, None, None]:
""" """
Yield each matching glob over each aports repository. Yield each matching glob over each aports repository.
Detect duplicates within the same aports repository but otherwise Detect duplicates within the same aports repository but otherwise

View file

@ -10,7 +10,7 @@ import pmb.export
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def frontend(args: PmbArgs): # FIXME: ARGS_REFACTOR def frontend(args: PmbArgs) -> None: # FIXME: ARGS_REFACTOR
config = get_context().config config = get_context().config
# Create the export folder # Create the export folder
target = args.export_folder target = args.export_folder

View file

@ -11,7 +11,7 @@ import pmb.helpers.file
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def odin(device: str, flavor, folder: Path): def odin(device: str, flavor: str, folder: Path) -> None:
""" """
Create Odin flashable tar file with kernel and initramfs Create Odin flashable tar file with kernel and initramfs
for devices configured with the flasher method 'heimdall-isorec' for devices configured with the flasher method 'heimdall-isorec'

View file

@ -13,7 +13,7 @@ import pmb.helpers.file
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def symlinks(flavor, folder: Path): def symlinks(flavor: str, folder: Path) -> None:
""" """
Create convenience symlinks to the rootfs and boot files. Create convenience symlinks to the rootfs and boot files.
""" """

View file

@ -42,7 +42,7 @@ def install_depends(method: str) -> None:
pmb.chroot.apk.install(depends, Chroot.native()) pmb.chroot.apk.install(depends, Chroot.native())
def init(device: str, method: str): def init(device: str, method: str) -> None:
install_depends(method) install_depends(method)
# Mount folders from host system # Mount folders from host system

View file

@ -6,7 +6,7 @@ import pmb.chroot.initfs
import pmb.helpers.args import pmb.helpers.args
def check_partition_blacklist(deviceinfo: Deviceinfo, key, value): def check_partition_blacklist(deviceinfo: Deviceinfo, key: str, value: str) -> None:
if not key.startswith("$PARTITION_"): if not key.startswith("$PARTITION_"):
return return

View file

@ -12,7 +12,7 @@ def variables(
no_reboot: bool | None, no_reboot: bool | None,
partition: str | None, partition: str | None,
resume: bool | None, resume: bool | None,
) -> dict[str, str]: ) -> dict[str, str | None]:
device = get_context().config.device device = get_context().config.device
deviceinfo = pmb.parse.deviceinfo() deviceinfo = pmb.parse.deviceinfo()
_cmdline = deviceinfo.kernel_cmdline or "" _cmdline = deviceinfo.kernel_cmdline or ""

View file

@ -66,7 +66,7 @@ def _compute_progress(line):
return cur / tot if tot > 0 else 0 return cur / tot if tot > 0 else 0
def apk_with_progress(command: Sequence[PathString], chroot: Chroot | None = None): def apk_with_progress(command: Sequence[PathString], chroot: Chroot | None = None) -> None:
"""Run an apk subcommand while printing a progress bar to STDOUT. """Run an apk subcommand while printing a progress bar to STDOUT.
:param command: apk subcommand in list form :param command: apk subcommand in list form
@ -86,7 +86,10 @@ def apk_with_progress(command: Sequence[PathString], chroot: Chroot | None = Non
with pmb.helpers.run.root(["cat", fifo], output="pipe") as p_cat: with pmb.helpers.run.root(["cat", fifo], output="pipe") as p_cat:
with pmb.helpers.run.root(command_with_progress, output="background") as p_apk: with pmb.helpers.run.root(command_with_progress, output="background") as p_apk:
while p_apk.poll() is None: while p_apk.poll() is None:
line = p_cat.stdout.readline().decode("utf-8") p_cat_stdout = p_cat.stdout
if p_cat_stdout is None:
raise RuntimeError("cat process had no stdout?")
line = p_cat_stdout.readline().decode("utf-8")
progress = _compute_progress(line) progress = _compute_progress(line)
pmb.helpers.cli.progress_print(progress) pmb.helpers.cli.progress_print(progress)
pmb.helpers.cli.progress_flush() pmb.helpers.cli.progress_flush()

View file

@ -6,12 +6,19 @@ from pmb.helpers import logging
import os import os
import re import re
import urllib.parse import urllib.parse
from typing import TypedDict
from pmb.types import PmbArgs from pmb.types import Apkbuild, PmbArgs
import pmb.helpers.file import pmb.helpers.file
import pmb.helpers.http import pmb.helpers.http
import pmb.helpers.pmaports import pmb.helpers.pmaports
class PackageVersionInfo(TypedDict):
sha: str
date: datetime.datetime
req_headers: dict[str, str] = {} req_headers: dict[str, str] = {}
req_headers_github: dict[str, str] = {} req_headers_github: dict[str, str] = {}
@ -47,7 +54,7 @@ def init_req_headers() -> None:
) )
def get_package_version_info_github(repo_name: str, ref: str | None): def get_package_version_info_github(repo_name: str, ref: str | None) -> PackageVersionInfo:
logging.debug(f"Trying GitHub repository: {repo_name}") logging.debug(f"Trying GitHub repository: {repo_name}")
# Get the URL argument to request a special ref, if needed # Get the URL argument to request a special ref, if needed
@ -63,13 +70,15 @@ def get_package_version_info_github(repo_name: str, ref: str | None):
commit_date = latest_commit["commit"]["committer"]["date"] commit_date = latest_commit["commit"]["committer"]["date"]
# Extract the time from the field # Extract the time from the field
date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%SZ") date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%SZ")
return { return PackageVersionInfo(
"sha": latest_commit["sha"], sha=latest_commit["sha"],
"date": date, date=date,
} )
def get_package_version_info_gitlab(gitlab_host: str, repo_name: str, ref: str | None): def get_package_version_info_gitlab(
gitlab_host: str, repo_name: str, ref: str | None
) -> PackageVersionInfo:
logging.debug(f"Trying GitLab repository: {repo_name}") logging.debug(f"Trying GitLab repository: {repo_name}")
repo_name_safe = urllib.parse.quote(repo_name, safe="") repo_name_safe = urllib.parse.quote(repo_name, safe="")
@ -89,13 +98,13 @@ def get_package_version_info_gitlab(gitlab_host: str, repo_name: str, ref: str |
# Extract the time from the field # Extract the time from the field
# 2019-10-14T09:32:00.000Z / 2019-12-27T07:58:53.000-05:00 # 2019-10-14T09:32:00.000Z / 2019-12-27T07:58:53.000-05:00
date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%S.000%z") date = datetime.datetime.strptime(commit_date, "%Y-%m-%dT%H:%M:%S.000%z")
return { return PackageVersionInfo(
"sha": latest_commit["id"], sha=latest_commit["id"],
"date": date, date=date,
} )
def upgrade_git_package(args: PmbArgs, pkgname: str, package) -> None: def upgrade_git_package(args: PmbArgs, pkgname: str, package: Apkbuild) -> None:
"""Update _commit/pkgver/pkgrel in a git-APKBUILD (or pretend to do it if args.dry is set). """Update _commit/pkgver/pkgrel in a git-APKBUILD (or pretend to do it if args.dry is set).
:param pkgname: the package name :param pkgname: the package name
@ -169,7 +178,7 @@ def upgrade_git_package(args: PmbArgs, pkgname: str, package) -> None:
return return
def upgrade_stable_package(args: PmbArgs, pkgname: str, package) -> None: def upgrade_stable_package(args: PmbArgs, pkgname: str, package: Apkbuild) -> None:
""" """
Update _commit/pkgver/pkgrel in an APKBUILD (or pretend to do it if Update _commit/pkgver/pkgrel in an APKBUILD (or pretend to do it if
args.dry is set). args.dry is set).
@ -254,7 +263,7 @@ def upgrade_stable_package(args: PmbArgs, pkgname: str, package) -> None:
return return
def upgrade(args: PmbArgs, pkgname, git=True, stable=True) -> None: def upgrade(args: PmbArgs, pkgname: str, git: bool = True, stable: bool = True) -> None:
"""Find new versions of a single package and upgrade it. """Find new versions of a single package and upgrade it.
:param pkgname: the name of the package :param pkgname: the name of the package

View file

@ -5,7 +5,7 @@ from pathlib import Path
from pmb.core.pkgrepo import pkgrepo_glob_one, pkgrepo_iglob from pmb.core.pkgrepo import pkgrepo_glob_one, pkgrepo_iglob
def find_path(codename: str, file="") -> Path | None: def find_path(codename: str, file: str = "") -> Path | None:
"""Find path to device APKBUILD under `device/*/device-`. """Find path to device APKBUILD under `device/*/device-`.
:param codename: device codename :param codename: device codename
@ -36,7 +36,7 @@ def list_codenames(vendor=None, archived=True):
return ret return ret
def list_vendors(): def list_vendors() -> set[str]:
"""Get all device vendors, for which aports are available. """Get all device vendors, for which aports are available.
:returns: {"vendor1", "vendor2", ...} :returns: {"vendor1", "vendor2", ...}

View file

@ -10,7 +10,7 @@ import pmb.helpers.run
import pmb.helpers.pmaports import pmb.helpers.pmaports
def replace(path: Path, old: str, new: str): def replace(path: Path, old: str, new: str) -> None:
text = "" text = ""
with path.open("r", encoding="utf-8") as handle: with path.open("r", encoding="utf-8") as handle:
text = handle.read() text = handle.read()
@ -21,7 +21,9 @@ def replace(path: Path, old: str, new: str):
handle.write(text) handle.write(text)
def replace_apkbuild(args: PmbArgs, pkgname, key, new, in_quotes=False): def replace_apkbuild(
args: PmbArgs, pkgname: str, key: str, new: int | str, in_quotes: bool = False
) -> None:
"""Replace one key=value line in an APKBUILD and verify it afterwards. """Replace one key=value line in an APKBUILD and verify it afterwards.
:param pkgname: package name, e.g. "hello-world" :param pkgname: package name, e.g. "hello-world"
@ -90,7 +92,7 @@ def is_older_than(path, seconds):
return lastmod + seconds < time.time() return lastmod + seconds < time.time()
def symlink(file: Path, link: Path): def symlink(file: Path, link: Path) -> None:
"""Check if the symlink is already present, otherwise create it.""" """Check if the symlink is already present, otherwise create it."""
if os.path.exists(link): if os.path.exists(link):
if os.path.islink(link) and os.path.realpath(os.readlink(link)) == os.path.realpath(file): if os.path.islink(link) and os.path.realpath(os.readlink(link)) == os.path.realpath(file):

View file

@ -7,7 +7,7 @@ from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
import sys import sys
from typing import Any, NoReturn from typing import cast, Any, NoReturn
import pmb.aportgen import pmb.aportgen
import pmb.build import pmb.build
@ -19,7 +19,7 @@ import pmb.chroot.other
import pmb.ci import pmb.ci
import pmb.config import pmb.config
from pmb.core import Config from pmb.core import Config
from pmb.types import PmbArgs from pmb.types import Env, PmbArgs, RunOutputType
import pmb.export import pmb.export
import pmb.flasher import pmb.flasher
import pmb.helpers.aportupgrade import pmb.helpers.aportupgrade
@ -43,7 +43,7 @@ from pmb.core import ChrootType, Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def _parse_flavor(device: str, autoinstall=True) -> str: def _parse_flavor(device: str, autoinstall: bool = True) -> str:
"""Verify the flavor argument if specified, or return a default value. """Verify the flavor argument if specified, or return a default value.
:param autoinstall: make sure that at least one kernel flavor is installed :param autoinstall: make sure that at least one kernel flavor is installed
@ -189,10 +189,13 @@ def chroot(args: PmbArgs) -> None:
pmb.chroot.init(chroot) pmb.chroot.init(chroot)
# Xauthority # Xauthority
env = {} env: Env = {}
if args.xauth: if args.xauth:
pmb.chroot.other.copy_xauthority(chroot) pmb.chroot.other.copy_xauthority(chroot)
env["DISPLAY"] = os.environ.get("DISPLAY") x11_display = os.environ.get("DISPLAY")
if x11_display is None:
raise AssertionError("$DISPLAY was unset despite that it should be set at this point")
env["DISPLAY"] = x11_display
env["XAUTHORITY"] = "/home/pmos/.Xauthority" env["XAUTHORITY"] = "/home/pmos/.Xauthority"
# Install blockdevice # Install blockdevice
@ -210,13 +213,16 @@ def chroot(args: PmbArgs) -> None:
pmb.chroot.apk.update_repository_list(chroot, user_repository=True) pmb.chroot.apk.update_repository_list(chroot, user_repository=True)
# TODO: Maybe this could be done better.
output_type = cast(RunOutputType, args.output)
# Run the command as user/root # Run the command as user/root
if user: if user:
logging.info(f"({chroot}) % su pmos -c '" + " ".join(args.command) + "'") logging.info(f"({chroot}) % su pmos -c '" + " ".join(args.command) + "'")
pmb.chroot.user(args.command, chroot, output=args.output, env=env) pmb.chroot.user(args.command, chroot, output=output_type, env=env)
else: else:
logging.info(f"({chroot}) % " + " ".join(args.command)) logging.info(f"({chroot}) % " + " ".join(args.command))
pmb.chroot.root(args.command, chroot, output=args.output, env=env) pmb.chroot.root(args.command, chroot, output=output_type, env=env)
def config(args: PmbArgs) -> None: def config(args: PmbArgs) -> None:

View file

@ -65,7 +65,7 @@ def clone(name_repo: str) -> None:
open(fetch_head, "w").close() open(fetch_head, "w").close()
def rev_parse(path: Path, revision="HEAD", extra_args: list = []) -> str: def rev_parse(path: Path, revision: str = "HEAD", extra_args: list = []) -> str:
"""Run "git rev-parse" in a specific repository dir. """Run "git rev-parse" in a specific repository dir.
:param path: to the git repository :param path: to the git repository
@ -79,7 +79,7 @@ def rev_parse(path: Path, revision="HEAD", extra_args: list = []) -> str:
return rev.rstrip() return rev.rstrip()
def can_fast_forward(path, branch_upstream, branch="HEAD") -> bool: def can_fast_forward(path: Path, branch_upstream: str, branch: str = "HEAD") -> bool:
command = ["git", "merge-base", "--is-ancestor", branch, branch_upstream] command = ["git", "merge-base", "--is-ancestor", branch, branch_upstream]
ret = pmb.helpers.run.user(command, path, check=False) ret = pmb.helpers.run.user(command, path, check=False)
if ret == 0: if ret == 0:
@ -244,7 +244,7 @@ def parse_channels_cfg(aports: Path) -> dict:
return ret return ret
def branch_looks_official(repo: Path, branch) -> bool: def branch_looks_official(repo: Path, branch: str) -> bool:
"""Check if a given branch follows the patterns of official branches in """Check if a given branch follows the patterns of official branches in
pmaports or aports. pmaports or aports.
@ -344,7 +344,7 @@ def get_topdir(repo: Path) -> Path:
return Path(res.strip()) return Path(res.strip())
def get_files(repo: Path): def get_files(repo: Path) -> list[str]:
"""Get all files inside a git repository, that are either already in the git tree or are not in gitignore. """Get all files inside a git repository, that are either already in the git tree or are not in gitignore.
Do not list deleted files. To be used for creating a tarball of the git repository. Do not list deleted files. To be used for creating a tarball of the git repository.

View file

@ -45,7 +45,7 @@ def get_custom_valid_options() -> list[str]:
# FIXME: dest_paths[repo], repo expected to be a Literal. # FIXME: dest_paths[repo], repo expected to be a Literal.
# We should really make Config.mirrors not a TypedDict. # We should really make Config.mirrors not a TypedDict.
# mypy: disable-error-code="index" # mypy: disable-error-code="index"
def check(pkgnames: Sequence[str]): def check(pkgnames: Sequence[str]) -> None:
"""Run apkbuild-lint on the supplied packages. """Run apkbuild-lint on the supplied packages.
:param pkgnames: Names of the packages to lint :param pkgnames: Names of the packages to lint

View file

@ -4,7 +4,7 @@ import logging
import os import os
from pathlib import Path from pathlib import Path
import sys import sys
from typing import Final, TextIO from typing import Any, Final, TextIO
import pmb.config import pmb.config
from pmb.meta import Cache from pmb.meta import Cache
@ -24,7 +24,7 @@ VERBOSE: Final[int] = 5
class log_handler(logging.StreamHandler): class log_handler(logging.StreamHandler):
"""Write to stdout and to the already opened log file.""" """Write to stdout and to the already opened log file."""
def __init__(self, details_to_stdout: bool = False, quiet: bool = False): def __init__(self, details_to_stdout: bool = False, quiet: bool = False) -> None:
super().__init__() super().__init__()
self.details_to_stdout = details_to_stdout self.details_to_stdout = details_to_stdout
self.quiet = False self.quiet = False
@ -90,7 +90,7 @@ class log_handler(logging.StreamHandler):
self.handleError(record) self.handleError(record)
def add_verbose_log_level(): def add_verbose_log_level() -> None:
"""Add a new log level "verbose", which is below "debug". """Add a new log level "verbose", which is below "debug".
Also monkeypatch logging, so it can be used with logging.verbose(). Also monkeypatch logging, so it can be used with logging.verbose().
@ -112,7 +112,7 @@ def add_verbose_log_level():
) )
def init(logfile: Path, verbose: bool, details_to_stdout: bool = False): def init(logfile: Path, verbose: bool, details_to_stdout: bool = False) -> None:
"""Set log format and add the log file descriptor to logfd, add the verbose log level.""" """Set log format and add the log file descriptor to logfd, add the verbose log level."""
global logfd global logfd
@ -153,7 +153,7 @@ def init(logfile: Path, verbose: bool, details_to_stdout: bool = False):
logging.debug(f"$ pmbootstrap {' '.join(sys.argv)}") logging.debug(f"$ pmbootstrap {' '.join(sys.argv)}")
def disable(): def disable() -> None:
logger = logging.getLogger() logger = logging.getLogger()
logger.disabled = True logger.disabled = True
@ -162,38 +162,38 @@ def disable():
# by not calling the (undefined) logging.verbose() function. # by not calling the (undefined) logging.verbose() function.
def critical(msg: object, *args, **kwargs): def critical(msg: object, *args: str, **kwargs: Any) -> None:
logging.critical(msg, *args, **kwargs) logging.critical(msg, *args, **kwargs)
def fatal(msg: object, *args, **kwargs): def fatal(msg: object, *args: str, **kwargs: Any) -> None:
logging.fatal(msg, *args, **kwargs) logging.fatal(msg, *args, **kwargs)
def error(msg: object, *args, **kwargs): def error(msg: object, *args: str, **kwargs: Any) -> None:
logging.error(msg, *args, **kwargs) logging.error(msg, *args, **kwargs)
def warning(msg: object, *args, **kwargs): def warning(msg: object, *args: str, **kwargs: Any) -> None:
logging.warning(msg, *args, **kwargs) logging.warning(msg, *args, **kwargs)
@Cache("msg") @Cache("msg")
def warn_once(msg: str): def warn_once(msg: str) -> None:
logging.warning(msg) logging.warning(msg)
def info(msg: object, *args, **kwargs): def info(msg: object, *args: str, **kwargs: Any) -> None:
logging.info(msg, *args, **kwargs) logging.info(msg, *args, **kwargs)
def debug(msg: object, *args, **kwargs): def debug(msg: object, *args: str, **kwargs: Any) -> None:
logging.debug(msg, *args, **kwargs) logging.debug(msg, *args, **kwargs)
def verbose(msg: object, *args, **kwargs): def verbose(msg: object, *args: str, **kwargs: Any) -> None:
logging.verbose(msg, *args, **kwargs) # type: ignore[attr-defined] logging.verbose(msg, *args, **kwargs) # type: ignore[attr-defined]
def log(level: int, msg: object, *args, **kwargs): def log(level: int, msg: object, *args: str, **kwargs: Any) -> None:
logging.log(level, msg, *args, **kwargs) logging.log(level, msg, *args, **kwargs)

View file

@ -4,6 +4,7 @@ import os
from pathlib import Path, PurePath from pathlib import Path, PurePath
import pmb.helpers import pmb.helpers
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import PathString
import pmb.helpers.run import pmb.helpers.run
@ -24,7 +25,7 @@ def ismount(folder: Path) -> bool:
def bind( def bind(
source: Path, destination: Path, create_folders: bool = True, umount: bool = False source: PathString, destination: Path, create_folders: bool = True, umount: bool = False
) -> None: ) -> None:
"""Mount --bind a folder and create necessary directory structure. """Mount --bind a folder and create necessary directory structure.

View file

@ -15,7 +15,7 @@ from typing import Any
from pmb.helpers.exceptions import NonBugError from pmb.helpers.exceptions import NonBugError
def folder_size(path: Path): def folder_size(path: Path) -> int:
"""Run `du` to calculate the size of a folder. """Run `du` to calculate the size of a folder.
(this is less code and faster than doing the same task in pure Python) (this is less code and faster than doing the same task in pure Python)
@ -32,7 +32,7 @@ def folder_size(path: Path):
return ret return ret
def check_grsec(): def check_grsec() -> None:
"""Check if the current kernel is based on the grsec patchset. """Check if the current kernel is based on the grsec patchset.
Also check if the chroot_deny_chmod option is enabled. Also check if the chroot_deny_chmod option is enabled.
@ -47,7 +47,7 @@ def check_grsec():
) )
def check_binfmt_misc(): def check_binfmt_misc() -> None:
"""Check if the 'binfmt_misc' module is loaded. """Check if the 'binfmt_misc' module is loaded.
This is done by checking, if /proc/sys/fs/binfmt_misc/ exists. This is done by checking, if /proc/sys/fs/binfmt_misc/ exists.
@ -72,13 +72,13 @@ def check_binfmt_misc():
raise RuntimeError(f"Failed to set up binfmt_misc, see: {link}") raise RuntimeError(f"Failed to set up binfmt_misc, see: {link}")
def migrate_success(work: Path, version): def migrate_success(work: Path, version: int) -> None:
logging.info("Migration to version " + str(version) + " done") logging.info("Migration to version " + str(version) + " done")
with open(work / "version", "w") as handle: with open(work / "version", "w") as handle:
handle.write(str(version) + "\n") handle.write(str(version) + "\n")
def migrate_work_folder(): def migrate_work_folder() -> None:
# Read current version # Read current version
context = get_context() context = get_context()
current = 0 current = 0
@ -182,7 +182,7 @@ def normalize_hostname(hostname: str) -> str:
return hostname return hostname
def validate_hostname(hostname): def validate_hostname(hostname: str) -> bool:
"""Check whether the string is a valid hostname. """Check whether the string is a valid hostname.
Check is performed according to Check is performed according to

View file

@ -20,7 +20,7 @@ class BumpType(Enum):
def package( def package(
pkgname: str, reason="", dry: bool = False, bump_type: BumpType = BumpType.PKGREL pkgname: str, reason: str = "", dry: bool = False, bump_type: BumpType = BumpType.PKGREL
) -> None: ) -> None:
"""Increase the pkgrel or pkgver in the APKBUILD of a specific package. """Increase the pkgrel or pkgver in the APKBUILD of a specific package.

View file

@ -18,7 +18,7 @@ from pmb.meta import Cache
import pmb.parse import pmb.parse
def _find_apkbuilds(skip_extra_repos=False) -> dict[str, Path]: def _find_apkbuilds(skip_extra_repos: bool = False) -> dict[str, Path]:
# Try to get a cached result first (we assume that the aports don't change # Try to get a cached result first (we assume that the aports don't change
# in one pmbootstrap call) # in one pmbootstrap call)
apkbuilds = pmb.helpers.other.cache.get("pmb.helpers.pmaports.apkbuilds") apkbuilds = pmb.helpers.other.cache.get("pmb.helpers.pmaports.apkbuilds")
@ -50,7 +50,7 @@ def get_list() -> list[str]:
return list(_find_apkbuilds().keys()) return list(_find_apkbuilds().keys())
def guess_main_dev(subpkgname) -> Path | None: def guess_main_dev(subpkgname: str) -> Path | None:
"""Check if a package without "-dev" at the end exists in pmaports or not, and log the appropriate message. """Check if a package without "-dev" at the end exists in pmaports or not, and log the appropriate message.
Don't call this function directly, use guess_main() instead. Don't call this function directly, use guess_main() instead.
@ -76,7 +76,7 @@ def guess_main_dev(subpkgname) -> Path | None:
return None return None
def guess_main(subpkgname) -> Path | None: def guess_main(subpkgname: str) -> Path | None:
"""Find the main package by assuming it is a prefix of the subpkgname. """Find the main package by assuming it is a prefix of the subpkgname.
We do that, because in some APKBUILDs the subpkgname="" variable gets We do that, because in some APKBUILDs the subpkgname="" variable gets
@ -243,7 +243,9 @@ def get_with_path(
return None, None return None, None
def get(pkgname, must_exist=True, subpackages=True, skip_extra_repos=False) -> dict[str, Any]: def get(
pkgname: str, must_exist: bool = True, subpackages: bool = True, skip_extra_repos: bool = False
) -> dict[str, Any]:
return get_with_path(pkgname, must_exist, subpackages, skip_extra_repos)[1] return get_with_path(pkgname, must_exist, subpackages, skip_extra_repos)[1]
@ -273,7 +275,7 @@ def find_providers(provide: str, default: list[str]) -> list[tuple[Any, Any]]:
return sorted(providers.items(), reverse=True, key=lambda p: p[1].get("provider_priority", 0)) return sorted(providers.items(), reverse=True, key=lambda p: p[1].get("provider_priority", 0))
def get_repo(pkgname) -> str | None: def get_repo(pkgname: str) -> str | None:
"""Get the repository folder of an aport. """Get the repository folder of an aport.
:pkgname: package name :pkgname: package name
@ -289,7 +291,7 @@ def get_repo(pkgname) -> str | None:
return None return None
def check_arches(arches, arch: Arch): def check_arches(arches: list[str], arch: Arch) -> bool:
"""Check if building for a certain arch is allowed. """Check if building for a certain arch is allowed.
:param arches: list of all supported arches, as it can be found in the :param arches: list of all supported arches, as it can be found in the

View file

@ -54,7 +54,7 @@ def apkindex_hash(url: str, length: int = 8) -> Path:
# FIXME: make config.mirrors a normal dict # FIXME: make config.mirrors a normal dict
# mypy: disable-error-code="literal-required" # mypy: disable-error-code="literal-required"
@Cache("user_repository", "mirrors_exclude") @Cache("user_repository", "mirrors_exclude")
def urls(user_repository=False, mirrors_exclude: list[str] = []): def urls(user_repository: bool = False, mirrors_exclude: list[str] = []) -> list[str]:
"""Get a list of repository URLs, as they are in /etc/apk/repositories. """Get a list of repository URLs, as they are in /etc/apk/repositories.
:param user_repository: add /mnt/pmbootstrap/packages :param user_repository: add /mnt/pmbootstrap/packages
@ -118,7 +118,7 @@ def urls(user_repository=False, mirrors_exclude: list[str] = []):
def apkindex_files( def apkindex_files(
arch: Arch | None = None, user_repository=True, exclude_mirrors: list[str] = [] arch: Arch | None = None, user_repository: bool = True, exclude_mirrors: list[str] = []
) -> list[Path]: ) -> list[Path]:
"""Get a list of outside paths to all resolved APKINDEX.tar.gz files for a specific arch. """Get a list of outside paths to all resolved APKINDEX.tar.gz files for a specific arch.
@ -144,7 +144,7 @@ def apkindex_files(
@Cache("arch", force=False) @Cache("arch", force=False)
def update(arch: Arch | None = None, force=False, existing_only=False): def update(arch: Arch | None = None, force: bool = False, existing_only: bool = False) -> bool:
"""Download the APKINDEX files for all URLs depending on the architectures. """Download the APKINDEX files for all URLs depending on the architectures.
:param arch: * one Alpine architecture name ("x86_64", "armhf", ...) :param arch: * one Alpine architecture name ("x86_64", "armhf", ...)
@ -223,7 +223,7 @@ def update(arch: Arch | None = None, force=False, existing_only=False):
return True return True
def alpine_apkindex_path(repo="main", arch: Arch | None = None): def alpine_apkindex_path(repo: str = "main", arch: Arch | None = None) -> Path:
"""Get the path to a specific Alpine APKINDEX file on disk and download it if necessary. """Get the path to a specific Alpine APKINDEX file on disk and download it if necessary.
:param repo: Alpine repository name (e.g. "main") :param repo: Alpine repository name (e.g. "main")

View file

@ -6,18 +6,26 @@ import subprocess
from pmb.core.arch import Arch from pmb.core.arch import Arch
import pmb.helpers.run_core import pmb.helpers.run_core
from collections.abc import Sequence from collections.abc import Sequence
from pmb.types import Env, PathString from typing import overload, Literal
from pmb.types import (
Env,
PathString,
RunOutputType,
RunOutputTypeDefault,
RunOutputTypePopen,
RunReturnType,
)
def user( def user(
cmd: Sequence[PathString], cmd: Sequence[PathString],
working_dir: Path | None = None, working_dir: Path | None = None,
output: str = "log", output: RunOutputType = "log",
output_return: bool = False, output_return: bool = False,
check: bool | None = None, check: bool | None = None,
env: Env = {}, env: Env = {},
sudo: bool = False, sudo: bool = False,
) -> str | int | subprocess.Popen: ) -> RunReturnType:
""" """
Run a command on the host system as user. Run a command on the host system as user.
@ -56,7 +64,7 @@ def user(
def user_output( def user_output(
cmd: Sequence[PathString], cmd: Sequence[PathString],
working_dir: Path | None = None, working_dir: Path | None = None,
output: str = "log", output: RunOutputType = "log",
check: bool | None = None, check: bool | None = None,
env: Env = {}, env: Env = {},
sudo: bool = False, sudo: bool = False,
@ -68,14 +76,47 @@ def user_output(
return ret return ret
@overload
def root( def root(
cmd: Sequence[PathString], cmd: Sequence[PathString],
working_dir=None, working_dir: Path | None = ...,
output="log", output: RunOutputTypePopen = ...,
output_return=False, output_return: Literal[False] = ...,
check=None, check: bool | None = ...,
env={}, env: Env = ...,
): ) -> subprocess.Popen: ...
@overload
def root(
cmd: Sequence[PathString],
working_dir: Path | None = ...,
output: RunOutputTypeDefault = ...,
output_return: Literal[False] = ...,
check: bool | None = ...,
env: Env = ...,
) -> int: ...
@overload
def root(
cmd: Sequence[PathString],
working_dir: Path | None = ...,
output: RunOutputType = ...,
output_return: Literal[True] = ...,
check: bool | None = ...,
env: Env = ...,
) -> str: ...
def root(
cmd: Sequence[PathString],
working_dir: Path | None = None,
output: RunOutputType = "log",
output_return: bool = False,
check: bool | None = None,
env: Env = {},
) -> RunReturnType:
"""Run a command on the host system as root, with sudo or doas. """Run a command on the host system as root, with sudo or doas.
:param env: dict of environment variables to be passed to the command, e.g. :param env: dict of environment variables to be passed to the command, e.g.

View file

@ -3,7 +3,7 @@
import fcntl import fcntl
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.types import PathString, Env from pmb.types import PathString, Env, RunOutputType
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
@ -22,7 +22,9 @@ import pmb.helpers.run
called by core(). """ called by core(). """
def flat_cmd(cmds: Sequence[Sequence[PathString]], working_dir: Path | None = None, env: Env = {}): def flat_cmd(
cmds: Sequence[Sequence[PathString]], working_dir: Path | None = None, env: Env = {}
) -> str:
"""Convert a shell command passed as list into a flat shell string with proper escaping. """Convert a shell command passed as list into a flat shell string with proper escaping.
:param cmds: list of commands as list, e.g. ["echo", "string with spaces"] :param cmds: list of commands as list, e.g. ["echo", "string with spaces"]
@ -53,7 +55,9 @@ def flat_cmd(cmds: Sequence[Sequence[PathString]], working_dir: Path | None = No
return ret return ret
def sanity_checks(output="log", output_return=False, check=None): def sanity_checks(
output: RunOutputType = "log", output_return: bool = False, check: bool | None = None
) -> None:
"""Raise an exception if the parameters passed to core() don't make sense. """Raise an exception if the parameters passed to core() don't make sense.
(all parameters are described in core() below). (all parameters are described in core() below).

View file

@ -9,7 +9,7 @@ from pmb.types import PmbArgs
from pmb.core.context import get_context from pmb.core.context import get_context
def print_status_line(key: str, value: str): def print_status_line(key: str, value: str) -> None:
styles = pmb.config.styles styles = pmb.config.styles
key = f"{styles['GREEN']}{key}{styles['END']}:" key = f"{styles['GREEN']}{key}{styles['END']}:"
padding = 17 padding = 17

View file

@ -1,5 +1,7 @@
# Copyright 2024 Oliver Smith # Copyright 2024 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path
from pmb.meta import Cache from pmb.meta import Cache
from pmb.helpers.exceptions import NonBugError from pmb.helpers.exceptions import NonBugError
@ -12,7 +14,7 @@ except ImportError:
@Cache("path") @Cache("path")
def load_toml_file(path) -> dict: def load_toml_file(path: Path) -> dict:
"""Read a toml file into a dict and show the path on error.""" """Read a toml file into a dict and show the path on error."""
with open(path, mode="rb") as f: with open(path, mode="rb") as f:
try: try:

View file

@ -38,7 +38,7 @@ get_recommends_visited: list[str] = []
get_selected_providers_visited: list[str] = [] get_selected_providers_visited: list[str] = []
def get_subpartitions_size(chroot: Chroot): def get_subpartitions_size(chroot: Chroot) -> tuple[int, int]:
""" """
Calculate the size of the boot and root subpartition. Calculate the size of the boot and root subpartition.
@ -84,7 +84,7 @@ def get_nonfree_packages(device):
return ret return ret
def get_kernel_package(config: Config): def get_kernel_package(config: Config) -> list[str]:
""" """
Get the device's kernel subpackage based on the user's choice in Get the device's kernel subpackage based on the user's choice in
"pmbootstrap init". "pmbootstrap init".
@ -110,7 +110,7 @@ def get_kernel_package(config: Config):
return ["device-" + config.device + "-kernel-" + config.kernel] return ["device-" + config.device + "-kernel-" + config.kernel]
def copy_files_from_chroot(args: PmbArgs, chroot: Chroot): def copy_files_from_chroot(args: PmbArgs, chroot: Chroot) -> None:
""" """
Copy all files from the rootfs chroot to /mnt/install, except Copy all files from the rootfs chroot to /mnt/install, except
for the home folder (because /home will contain some empty for the home folder (because /home will contain some empty
@ -155,7 +155,7 @@ def copy_files_from_chroot(args: PmbArgs, chroot: Chroot):
pmb.chroot.root(["cp", "-a"] + folders + ["/mnt/install/"], working_dir=mountpoint) pmb.chroot.root(["cp", "-a"] + folders + ["/mnt/install/"], working_dir=mountpoint)
def create_home_from_skel(filesystem: str, user: str): def create_home_from_skel(filesystem: str, user: str) -> None:
""" """
Create /home/{user} from /etc/skel Create /home/{user} from /etc/skel
""" """
@ -172,7 +172,7 @@ def create_home_from_skel(filesystem: str, user: str):
pmb.helpers.run.root(["chown", "-R", "10000", home]) pmb.helpers.run.root(["chown", "-R", "10000", home])
def configure_apk(args: PmbArgs): def configure_apk(args: PmbArgs) -> None:
""" """
Copy over all official keys, and the keys used to compile local packages Copy over all official keys, and the keys used to compile local packages
(unless --no-local-pkgs is set). Then copy the corresponding APKINDEX files (unless --no-local-pkgs is set). Then copy the corresponding APKINDEX files
@ -204,7 +204,7 @@ def configure_apk(args: PmbArgs):
pmb.helpers.run.user(["cat", rootfs / "etc/apk/repositories"]) pmb.helpers.run.user(["cat", rootfs / "etc/apk/repositories"])
def set_user(config: Config): def set_user(config: Config) -> None:
""" """
Create user with UID 10000 if it doesn't exist. Create user with UID 10000 if it doesn't exist.
Usually the ID for the first user created is 1000, but higher ID is Usually the ID for the first user created is 1000, but higher ID is
@ -228,7 +228,7 @@ def set_user(config: Config):
pmb.chroot.root(["addgroup", config.user, group], chroot) pmb.chroot.root(["addgroup", config.user, group], chroot)
def setup_login_chpasswd_user_from_arg(args: PmbArgs, user: str, chroot: Chroot): def setup_login_chpasswd_user_from_arg(args: PmbArgs, user: str, chroot: Chroot) -> None:
""" """
Set the user's password from what the user passed as --password. Make an Set the user's password from what the user passed as --password. Make an
effort to not have the password end up in the log file by writing it to effort to not have the password end up in the log file by writing it to
@ -251,7 +251,7 @@ def setup_login_chpasswd_user_from_arg(args: PmbArgs, user: str, chroot: Chroot)
os.unlink(path_outside) os.unlink(path_outside)
def is_root_locked(chroot: Chroot): def is_root_locked(chroot: Chroot) -> bool:
""" """
Figure out from /etc/shadow if root is already locked. The output of this Figure out from /etc/shadow if root is already locked. The output of this
is stored in the log, so use grep to only log the line for root, not the is stored in the log, so use grep to only log the line for root, not the
@ -265,7 +265,7 @@ def is_root_locked(chroot: Chroot):
return shadow_root.startswith("root:!:") return shadow_root.startswith("root:!:")
def setup_login(args: PmbArgs, config: Config, chroot: Chroot): def setup_login(args: PmbArgs, config: Config, chroot: Chroot) -> None:
""" """
Loop until the password for user has been set successfully, and disable Loop until the password for user has been set successfully, and disable
root login. root login.
@ -294,7 +294,7 @@ def setup_login(args: PmbArgs, config: Config, chroot: Chroot):
pmb.chroot.root(["passwd", "-l", "root"], chroot) pmb.chroot.root(["passwd", "-l", "root"], chroot)
def copy_ssh_keys(config: Config): def copy_ssh_keys(config: Config) -> None:
""" """
If requested, copy user's SSH public keys to the device if they exist If requested, copy user's SSH public keys to the device if they exist
""" """
@ -327,7 +327,7 @@ def copy_ssh_keys(config: Config):
pmb.helpers.run.root(["chown", "-R", "10000:10000", target]) pmb.helpers.run.root(["chown", "-R", "10000:10000", target])
def setup_keymap(config: Config): def setup_keymap(config: Config) -> None:
""" """
Set the keymap with the setup-keymap utility if the device requires it Set the keymap with the setup-keymap utility if the device requires it
""" """
@ -369,7 +369,7 @@ def setup_keymap(config: Config):
logging.info("NOTE: No valid keymap specified for device") logging.info("NOTE: No valid keymap specified for device")
def setup_timezone(chroot: Chroot, timezone: str): def setup_timezone(chroot: Chroot, timezone: str) -> None:
# We don't care about the arch since it's built for all! # We don't care about the arch since it's built for all!
alpine_conf = pmb.helpers.package.get("alpine-conf", Arch.native()) alpine_conf = pmb.helpers.package.get("alpine-conf", Arch.native())
version = alpine_conf.version.split("-r")[0] version = alpine_conf.version.split("-r")[0]
@ -387,7 +387,7 @@ def setup_timezone(chroot: Chroot, timezone: str):
pmb.chroot.root(setup_tz_cmd, chroot) pmb.chroot.root(setup_tz_cmd, chroot)
def setup_hostname(device: str, hostname: str | None): def setup_hostname(device: str, hostname: str | None) -> None:
""" """
Set the hostname and update localhost address in /etc/hosts Set the hostname and update localhost address in /etc/hosts
""" """
@ -417,7 +417,7 @@ def setup_hostname(device: str, hostname: str | None):
pmb.chroot.root(["sed", "-i", "-e", regex, "/etc/hosts"], suffix) pmb.chroot.root(["sed", "-i", "-e", regex, "/etc/hosts"], suffix)
def setup_appstream(offline: bool, chroot: Chroot): def setup_appstream(offline: bool, chroot: Chroot) -> None:
""" """
If alpine-appstream-downloader has been downloaded, execute it to have If alpine-appstream-downloader has been downloaded, execute it to have
update AppStream data on new installs update AppStream data on new installs
@ -444,7 +444,7 @@ def setup_appstream(offline: bool, chroot: Chroot):
) )
def disable_sshd(chroot: Chroot): def disable_sshd(chroot: Chroot) -> None:
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
pmb.chroot.root(["rc-update", "del", "sshd", "default"], chroot, check=False) pmb.chroot.root(["rc-update", "del", "sshd", "default"], chroot, check=False)
@ -456,7 +456,7 @@ def disable_sshd(chroot: Chroot):
raise RuntimeError(f"Failed to disable sshd service: {sshd_files}") raise RuntimeError(f"Failed to disable sshd service: {sshd_files}")
def print_sshd_info(args: PmbArgs): def print_sshd_info(args: PmbArgs) -> None:
logging.info("") # make the note stand out logging.info("") # make the note stand out
logging.info("*** SSH DAEMON INFORMATION ***") logging.info("*** SSH DAEMON INFORMATION ***")
@ -480,7 +480,7 @@ def print_sshd_info(args: PmbArgs):
logging.info("More info: https://postmarketos.org/ondev-debug") logging.info("More info: https://postmarketos.org/ondev-debug")
def disable_firewall(chroot: Chroot): def disable_firewall(chroot: Chroot) -> None:
# check=False: rc-update doesn't exit with 0 if already disabled # check=False: rc-update doesn't exit with 0 if already disabled
pmb.chroot.root(["rc-update", "del", "nftables", "default"], chroot, check=False) pmb.chroot.root(["rc-update", "del", "nftables", "default"], chroot, check=False)
@ -492,7 +492,7 @@ def disable_firewall(chroot: Chroot):
raise RuntimeError(f"Failed to disable firewall: {nftables_files}") raise RuntimeError(f"Failed to disable firewall: {nftables_files}")
def print_firewall_info(disabled: bool, arch: Arch): def print_firewall_info(disabled: bool, arch: Arch) -> None:
pmaports_cfg = pmb.config.pmaports.read_config() pmaports_cfg = pmb.config.pmaports.read_config()
pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables" pmaports_ok = pmaports_cfg.get("supported_firewall", None) == "nftables"
@ -532,7 +532,7 @@ def print_firewall_info(disabled: bool, arch: Arch):
logging.info("For more information: https://postmarketos.org/firewall") logging.info("For more information: https://postmarketos.org/firewall")
def generate_binary_list(args: PmbArgs, chroot: Chroot, step): def generate_binary_list(args: PmbArgs, chroot: Chroot, step: int) -> list[tuple[str, int]]:
""" """
Perform three checks prior to writing binaries to disk: 1) that binaries Perform three checks prior to writing binaries to disk: 1) that binaries
exist, 2) that binaries do not extend into the first partition, 3) that exist, 2) that binaries do not extend into the first partition, 3) that
@ -587,7 +587,7 @@ def generate_binary_list(args: PmbArgs, chroot: Chroot, step):
return binary_list return binary_list
def embed_firmware(args: PmbArgs, suffix: Chroot): def embed_firmware(args: PmbArgs, suffix: Chroot) -> None:
""" """
This method will embed firmware, located at /usr/share, that are specified This method will embed firmware, located at /usr/share, that are specified
by the "sd_embed_firmware" deviceinfo parameter into the SD card image by the "sd_embed_firmware" deviceinfo parameter into the SD card image
@ -626,7 +626,7 @@ def embed_firmware(args: PmbArgs, suffix: Chroot):
) )
def write_cgpt_kpart(args: PmbArgs, layout, suffix: Chroot): def write_cgpt_kpart(args: PmbArgs, layout: PartitionLayout, suffix: Chroot) -> None:
""" """
Write the kernel to the ChromeOS kernel partition. Write the kernel to the ChromeOS kernel partition.
@ -658,7 +658,7 @@ def sanity_check_boot_size():
sys.exit(1) sys.exit(1)
def sanity_check_disk(args: PmbArgs): def sanity_check_disk(args: PmbArgs) -> None:
device = args.disk device = args.disk
device_name = os.path.basename(device) device_name = os.path.basename(device)
if not os.path.exists(device): if not os.path.exists(device):
@ -670,7 +670,7 @@ def sanity_check_disk(args: PmbArgs):
raise RuntimeError(f"{device} is read-only, maybe a locked SD card?") raise RuntimeError(f"{device} is read-only, maybe a locked SD card?")
def sanity_check_disk_size(args: PmbArgs): def sanity_check_disk_size(args: PmbArgs) -> None:
device = args.disk device = args.disk
devpath = os.path.realpath(device) devpath = os.path.realpath(device)
sysfs = "/sys/class/block/{}/size".format(devpath.replace("/dev/", "")) sysfs = "/sys/class/block/{}/size".format(devpath.replace("/dev/", ""))
@ -697,13 +697,13 @@ def sanity_check_disk_size(args: PmbArgs):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
def get_ondev_pkgver(args: PmbArgs): def get_ondev_pkgver(args: PmbArgs) -> str:
arch = pmb.parse.deviceinfo().arch arch = pmb.parse.deviceinfo().arch
package = pmb.helpers.package.get("postmarketos-ondev", arch) package = pmb.helpers.package.get("postmarketos-ondev", arch)
return package.version.split("-r")[0] return package.version.split("-r")[0]
def sanity_check_ondev_version(args: PmbArgs): def sanity_check_ondev_version(args: PmbArgs) -> None:
ver_pkg = get_ondev_pkgver(args) ver_pkg = get_ondev_pkgver(args)
ver_min = pmb.config.ondev_min_version ver_min = pmb.config.ondev_min_version
if pmb.parse.version.compare(ver_pkg, ver_min) == -1: if pmb.parse.version.compare(ver_pkg, ver_min) == -1:
@ -715,7 +715,7 @@ def sanity_check_ondev_version(args: PmbArgs):
) )
def get_partition_layout(reserve, kernel): def get_partition_layout(reserve: bool | int, kernel: bool) -> PartitionLayout:
""" """
:param reserve: create an empty partition between root and boot (pma#463) :param reserve: create an empty partition between root and boot (pma#463)
:param kernel: create a separate kernel partition before all other :param kernel: create a separate kernel partition before all other
@ -741,7 +741,7 @@ def get_partition_layout(reserve, kernel):
return ret return ret
def get_uuid(args: PmbArgs, partition: Path): def get_uuid(args: PmbArgs, partition: Path) -> str:
""" """
Get UUID of a partition Get UUID of a partition
@ -760,7 +760,7 @@ def get_uuid(args: PmbArgs, partition: Path):
).rstrip() ).rstrip()
def create_crypttab(args: PmbArgs, layout, chroot: Chroot): def create_crypttab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None:
""" """
Create /etc/crypttab config Create /etc/crypttab config
@ -776,7 +776,7 @@ def create_crypttab(args: PmbArgs, layout, chroot: Chroot):
pmb.chroot.root(["mv", "/tmp/crypttab", "/etc/crypttab"], chroot) pmb.chroot.root(["mv", "/tmp/crypttab", "/etc/crypttab"], chroot)
def create_fstab(args: PmbArgs, layout, chroot: Chroot): def create_fstab(args: PmbArgs, layout: PartitionLayout, chroot: Chroot) -> None:
""" """
Create /etc/fstab config Create /etc/fstab config
@ -832,15 +832,15 @@ def create_fstab(args: PmbArgs, layout, chroot: Chroot):
def install_system_image( def install_system_image(
args: PmbArgs, args: PmbArgs,
size_reserve, size_reserve: int,
chroot: Chroot, chroot: Chroot,
step, step: int,
steps, steps: int,
boot_label="pmOS_boot", boot_label: str = "pmOS_boot",
root_label="pmOS_root", root_label: str = "pmOS_root",
split=False, split: bool = False,
disk: Path | None = None, disk: Path | None = None,
): ) -> None:
""" """
:param size_reserve: empty partition between root and boot in MiB (pma#463) :param size_reserve: empty partition between root and boot in MiB (pma#463)
:param suffix: the chroot suffix, where the rootfs that will be installed :param suffix: the chroot suffix, where the rootfs that will be installed
@ -944,7 +944,7 @@ def install_system_image(
pmb.chroot.user(["mv", "-f", sys_image_patched, sys_image], working_dir=workdir) pmb.chroot.user(["mv", "-f", sys_image_patched, sys_image], working_dir=workdir)
def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk: bool): def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk: bool) -> None:
"""Print flashing information, based on the deviceinfo data and the """Print flashing information, based on the deviceinfo data and the
pmbootstrap arguments.""" pmbootstrap arguments."""
logging.info("") # make the note stand out logging.info("") # make the note stand out
@ -1042,7 +1042,7 @@ def print_flash_info(device: str, deviceinfo: Deviceinfo, split: bool, have_disk
) )
def install_recovery_zip(args: PmbArgs, device: str, arch: Arch, steps): def install_recovery_zip(args: PmbArgs, device: str, arch: Arch, steps: int) -> None:
logging.info(f"*** ({steps}/{steps}) CREATING RECOVERY-FLASHABLE ZIP ***") logging.info(f"*** ({steps}/{steps}) CREATING RECOVERY-FLASHABLE ZIP ***")
chroot = Chroot(ChrootType.BUILDROOT, arch) chroot = Chroot(ChrootType.BUILDROOT, arch)
mount_device_rootfs(Chroot.rootfs(device), chroot) mount_device_rootfs(Chroot.rootfs(device), chroot)
@ -1054,7 +1054,7 @@ def install_recovery_zip(args: PmbArgs, device: str, arch: Arch, steps):
logging.info("https://postmarketos.org/recoveryzip") logging.info("https://postmarketos.org/recoveryzip")
def install_on_device_installer(args: PmbArgs, step, steps): def install_on_device_installer(args: PmbArgs, step: int, steps: int) -> None:
# Generate the rootfs image # Generate the rootfs image
config = get_context().config config = get_context().config
if not args.ondev_no_rootfs: if not args.ondev_no_rootfs:
@ -1136,7 +1136,7 @@ def install_on_device_installer(args: PmbArgs, step, steps):
) )
def get_selected_providers(args: PmbArgs, packages): def get_selected_providers(args: PmbArgs, packages: list[str]) -> list[str]:
""" """
Look through the specified packages and see which providers were selected Look through the specified packages and see which providers were selected
in "pmbootstrap init". Install those as extra packages to select them in "pmbootstrap init". Install those as extra packages to select them
@ -1182,7 +1182,7 @@ def get_selected_providers(args: PmbArgs, packages):
return ret return ret
def get_recommends(args: PmbArgs, packages) -> Sequence[str]: def get_recommends(args: PmbArgs, packages: list[str]) -> Sequence[str]:
""" """
Look through the specified packages and collect additional packages Look through the specified packages and collect additional packages
specified under _pmb_recommends in them. This is recursive, so it will dive specified under _pmb_recommends in them. This is recursive, so it will dive
@ -1240,7 +1240,7 @@ def get_recommends(args: PmbArgs, packages) -> Sequence[str]:
return ret return ret
def create_device_rootfs(args: PmbArgs, step, steps): def create_device_rootfs(args: PmbArgs, step: int, steps: int) -> None:
# list all packages to be installed (including the ones specified by --add) # list all packages to be installed (including the ones specified by --add)
# and upgrade the installed packages/apkindexes # and upgrade the installed packages/apkindexes
context = get_context() context = get_context()
@ -1348,7 +1348,7 @@ def create_device_rootfs(args: PmbArgs, step, steps):
disable_firewall(chroot) disable_firewall(chroot)
def install(args: PmbArgs): def install(args: PmbArgs) -> None:
device = get_context().config.device device = get_context().config.device
chroot = Chroot(ChrootType.ROOTFS, device) chroot = Chroot(ChrootType.ROOTFS, device)
deviceinfo = pmb.parse.deviceinfo() deviceinfo = pmb.parse.deviceinfo()

View file

@ -12,7 +12,7 @@ from pmb.core import Chroot
from pmb.core.context import get_context from pmb.core.context import get_context
def previous_install(path: Path): def previous_install(path: Path) -> bool:
""" """
Search the disk for possible existence of a previous installation of Search the disk for possible existence of a previous installation of
pmOS. We temporarily mount the possible pmOS_boot partition as pmOS. We temporarily mount the possible pmOS_boot partition as
@ -39,7 +39,7 @@ def previous_install(path: Path):
return "pmOS_boot" in label return "pmOS_boot" in label
def mount_disk(path: Path): def mount_disk(path: Path) -> None:
""" """
:param path: path to disk block device (e.g. /dev/mmcblk0) :param path: path to disk block device (e.g. /dev/mmcblk0)
""" """
@ -61,7 +61,9 @@ def mount_disk(path: Path):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
def create_and_mount_image(args: PmbArgs, size_boot, size_root, size_reserve, split=False): def create_and_mount_image(
args: PmbArgs, size_boot: int, size_root: int, size_reserve: int, split: bool = False
) -> None:
""" """
Create a new image file, and mount it as /dev/install. Create a new image file, and mount it as /dev/install.
@ -121,7 +123,9 @@ def create_and_mount_image(args: PmbArgs, size_boot, size_root, size_reserve, sp
pmb.helpers.mount.bind_file(device, Chroot.native() / mount_point) pmb.helpers.mount.bind_file(device, Chroot.native() / mount_point)
def create(args: PmbArgs, size_boot, size_root, size_reserve, split, disk: Path | None): def create(
args: PmbArgs, size_boot: int, size_root: int, size_reserve: int, split: bool, disk: Path | None
) -> None:
""" """
Create /dev/install (the "install blockdevice"). Create /dev/install (the "install blockdevice").

View file

@ -3,7 +3,7 @@
from pmb.helpers import logging from pmb.helpers import logging
import pmb.chroot import pmb.chroot
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import PmbArgs from pmb.types import PartitionLayout, PmbArgs, PathString
def install_fsprogs(filesystem): def install_fsprogs(filesystem):
@ -14,7 +14,7 @@ def install_fsprogs(filesystem):
pmb.chroot.apk.install([fsprogs], Chroot.native()) pmb.chroot.apk.install([fsprogs], Chroot.native())
def format_and_mount_boot(args: PmbArgs, device, boot_label): def format_and_mount_boot(args: PmbArgs, device: str, boot_label: str) -> None:
""" """
:param device: boot partition on install block device (e.g. /dev/installp1) :param device: boot partition on install block device (e.g. /dev/installp1)
:param boot_label: label of the root partition (e.g. "pmOS_boot") :param boot_label: label of the root partition (e.g. "pmOS_boot")
@ -40,7 +40,7 @@ def format_and_mount_boot(args: PmbArgs, device, boot_label):
pmb.chroot.root(["mount", device, mountpoint]) pmb.chroot.root(["mount", device, mountpoint])
def format_luks_root(args: PmbArgs, device): def format_luks_root(args: PmbArgs, device: str) -> None:
""" """
:param device: root partition on install block device (e.g. /dev/installp2) :param device: root partition on install block device (e.g. /dev/installp2)
""" """
@ -72,7 +72,7 @@ def format_luks_root(args: PmbArgs, device):
raise RuntimeError("Failed to open cryptdevice!") raise RuntimeError("Failed to open cryptdevice!")
def get_root_filesystem(args: PmbArgs): def get_root_filesystem(args: PmbArgs) -> str:
ret = args.filesystem or pmb.parse.deviceinfo().root_filesystem or "ext4" ret = args.filesystem or pmb.parse.deviceinfo().root_filesystem or "ext4"
pmaports_cfg = pmb.config.pmaports.read_config() pmaports_cfg = pmb.config.pmaports.read_config()
@ -90,7 +90,7 @@ def get_root_filesystem(args: PmbArgs):
return ret return ret
def prepare_btrfs_subvolumes(args: PmbArgs, device, mountpoint): def prepare_btrfs_subvolumes(args: PmbArgs, device: str, mountpoint: str) -> None:
""" """
Create separate subvolumes if root filesystem is btrfs. Create separate subvolumes if root filesystem is btrfs.
This lets us do snapshots and rollbacks of relevant parts This lets us do snapshots and rollbacks of relevant parts
@ -143,7 +143,9 @@ def prepare_btrfs_subvolumes(args: PmbArgs, device, mountpoint):
pmb.chroot.root(["chattr", "+C", f"{mountpoint}/var"]) pmb.chroot.root(["chattr", "+C", f"{mountpoint}/var"])
def format_and_mount_root(args: PmbArgs, device, root_label, disk): def format_and_mount_root(
args: PmbArgs, device: str, root_label: str, disk: PathString | None
) -> None:
""" """
:param device: root partition on install block device (e.g. /dev/installp2) :param device: root partition on install block device (e.g. /dev/installp2)
:param root_label: label of the root partition (e.g. "pmOS_root") :param root_label: label of the root partition (e.g. "pmOS_root")
@ -185,7 +187,13 @@ def format_and_mount_root(args: PmbArgs, device, root_label, disk):
prepare_btrfs_subvolumes(args, device, mountpoint) prepare_btrfs_subvolumes(args, device, mountpoint)
def format(args: PmbArgs, layout, boot_label, root_label, disk): def format(
args: PmbArgs,
layout: PartitionLayout,
boot_label: str,
root_label: str,
disk: PathString | None,
) -> None:
""" """
:param layout: partition layout from get_partition_layout() :param layout: partition layout from get_partition_layout()
:param boot_label: label of the boot partition (e.g. "pmOS_boot") :param boot_label: label of the boot partition (e.g. "pmOS_boot")

View file

@ -8,12 +8,13 @@ import pmb.chroot
import pmb.config import pmb.config
import pmb.install.losetup import pmb.install.losetup
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import PartitionLayout
import pmb.core.dps import pmb.core.dps
# FIXME (#2324): this function drops disk to a string because it's easier # FIXME (#2324): this function drops disk to a string because it's easier
# to manipulate, this is probably bad. # to manipulate, this is probably bad.
def partitions_mount(device: str, layout, disk: Path | None): def partitions_mount(device: str, layout: PartitionLayout, disk: Path | None) -> None:
""" """
Mount blockdevices of partitions inside native chroot Mount blockdevices of partitions inside native chroot
:param layout: partition layout from get_partition_layout() :param layout: partition layout from get_partition_layout()
@ -61,7 +62,7 @@ def partitions_mount(device: str, layout, disk: Path | None):
pmb.helpers.mount.bind_file(source, target) pmb.helpers.mount.bind_file(source, target)
def partition(layout, size_boot, size_reserve): def partition(layout: PartitionLayout, size_boot: int, size_reserve: int) -> None:
""" """
Partition /dev/install and create /dev/install{p1,p2,p3}: Partition /dev/install and create /dev/install{p1,p2,p3}:
* /dev/installp1: boot * /dev/installp1: boot
@ -122,7 +123,7 @@ def partition(layout, size_boot, size_reserve):
pmb.chroot.root(["parted", "-s", "/dev/install"] + command, check=False) pmb.chroot.root(["parted", "-s", "/dev/install"] + command, check=False)
def partition_cgpt(layout, size_boot, size_reserve): def partition_cgpt(layout: PartitionLayout, size_boot: int, size_reserve: int) -> None:
""" """
This function does similar functionality to partition(), but this This function does similar functionality to partition(), but this
one is for ChromeOS devices which use special GPT. We don't follow one is for ChromeOS devices which use special GPT. We don't follow

View file

@ -12,7 +12,7 @@ import pmb.flasher
import pmb.helpers.frontend import pmb.helpers.frontend
def create_zip(args: PmbArgs, chroot: Chroot, device: str): def create_zip(args: PmbArgs, chroot: Chroot, device: str) -> None:
""" """
Create android recovery compatible installer zip. Create android recovery compatible installer zip.
""" """
@ -51,7 +51,7 @@ def create_zip(args: PmbArgs, chroot: Chroot, device: str):
raise AssertionError("Partitions should not be None at this point") raise AssertionError("Partitions should not be None at this point")
# Create config file for the recovery installer # Create config file for the recovery installer
options = { options: dict[str, bool | str] = {
"DEVICE": device, "DEVICE": device,
"FLASH_KERNEL": args.recovery_flash_kernel, "FLASH_KERNEL": args.recovery_flash_kernel,
"ISOREC": method == "heimdall-isorec", "ISOREC": method == "heimdall-isorec",

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import copy import copy
from typing import Generic, Optional, TypeVar, overload from typing import Any, Generic, Optional, TypeVar, overload
from collections.abc import Callable from collections.abc import Callable
import inspect import inspect
@ -25,7 +25,7 @@ class Wrapper(Generic[FuncArgs, FuncReturn]):
# actually end up here. We first check if we have a cached # actually end up here. We first check if we have a cached
# result and if not then we do the actual function call and # result and if not then we do the actual function call and
# cache it if applicable # cache it if applicable
def __call__(self, *args, **kwargs) -> FuncReturn: def __call__(self, *args: Any, **kwargs: Any) -> FuncReturn:
if self.disabled: if self.disabled:
return self.func(*args, **kwargs) return self.func(*args, **kwargs)
@ -79,7 +79,7 @@ class Cache:
# Build the cache key, or return None to not cache in the case where # Build the cache key, or return None to not cache in the case where
# we only cache when an argument has a specific value # we only cache when an argument has a specific value
def build_key(self, func: Callable, *args, **kwargs) -> str | None: def build_key(self, func: Callable, *args: Any, **kwargs: Any) -> str | None:
key = "~" key = "~"
# Easy case: cache irrelevant of arguments # Easy case: cache irrelevant of arguments
if not self.params and not self.kwargs: if not self.params and not self.kwargs:

View file

@ -12,7 +12,7 @@ import pmb.helpers.run
from pmb.core import Chroot from pmb.core import Chroot
def start_nbd_server(device: str, replace: bool, ip="172.16.42.2", port=9999): def start_nbd_server(device: str, replace: bool, ip: str = "172.16.42.2", port: int = 9999) -> None:
""" """
Start nbd server in chroot_native with pmOS rootfs. Start nbd server in chroot_native with pmOS rootfs.
:param ip: IP address to serve nbd server for :param ip: IP address to serve nbd server for

View file

@ -3,6 +3,7 @@
# mypy: disable-error-code="attr-defined" # mypy: disable-error-code="attr-defined"
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pmb.types import Apkbuild
import os import os
from pathlib import Path from pathlib import Path
import re import re
@ -31,7 +32,7 @@ revar4 = re.compile(r"\${([a-zA-Z_]+[a-zA-Z0-9_]*)#(.*)}")
revar5 = re.compile(r"([a-zA-Z_]+[a-zA-Z0-9_]*)=") revar5 = re.compile(r"([a-zA-Z_]+[a-zA-Z0-9_]*)=")
def replace_variable(apkbuild, value: str) -> str: def replace_variable(apkbuild: Apkbuild, value: str) -> str:
def log_key_not_found(match): def log_key_not_found(match):
logging.verbose( logging.verbose(
f"{apkbuild['pkgname']}: key '{match.group(1)}' for" f"{apkbuild['pkgname']}: key '{match.group(1)}' for"
@ -95,7 +96,7 @@ def replace_variable(apkbuild, value: str) -> str:
return value return value
def function_body(path: Path, func: str): def function_body(path: Path, func: str) -> list[str]:
""" """
Get the body of a function in an APKBUILD. Get the body of a function in an APKBUILD.
@ -120,7 +121,7 @@ def function_body(path: Path, func: str):
return func_body return func_body
def read_file(path: Path): def read_file(path: Path) -> list[str]:
""" """
Read an APKBUILD file Read an APKBUILD file
@ -321,7 +322,7 @@ def _parse_subpackage(path, lines, apkbuild, subpackages, subpkg):
@Cache("path") @Cache("path")
def apkbuild(path: Path, check_pkgver=True, check_pkgname=True): def apkbuild(path: Path, check_pkgver: bool = True, check_pkgname: bool = True) -> Apkbuild:
""" """
Parse relevant information out of the APKBUILD file. This is not meant Parse relevant information out of the APKBUILD file. This is not meant
to be perfect and catch every edge case (for that, a full shell parser to be perfect and catch every edge case (for that, a full shell parser
@ -371,7 +372,7 @@ def apkbuild(path: Path, check_pkgver=True, check_pkgname=True):
return ret return ret
def kernels(device: str): def kernels(device: str) -> dict[str, str] | None:
""" """
Get the possible kernels from a device-* APKBUILD. Get the possible kernels from a device-* APKBUILD.

View file

@ -1,6 +1,8 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
from typing import TextIO
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
@ -9,10 +11,10 @@ import pmb.chroot
import pmb.chroot.other import pmb.chroot.other
import pmb.chroot.apk import pmb.chroot.apk
from pmb.core import Chroot from pmb.core import Chroot
from pmb.types import Bootimg from pmb.types import Bootimg, PathString
def is_dtb(path) -> bool: def is_dtb(path: PathString) -> bool:
if not os.path.isfile(path): if not os.path.isfile(path):
return False return False
with open(path, "rb") as f: with open(path, "rb") as f:
@ -20,7 +22,7 @@ def is_dtb(path) -> bool:
return f.read(4) == b"\xd0\x0d\xfe\xed" return f.read(4) == b"\xd0\x0d\xfe\xed"
def get_mtk_label(path) -> str | None: def get_mtk_label(path: PathString) -> str | None:
"""Read the label from the MediaTek header of the kernel or ramdisk inside """Read the label from the MediaTek header of the kernel or ramdisk inside
an extracted boot.img. an extracted boot.img.
:param path: to either the kernel or ramdisk extracted from boot.img :param path: to either the kernel or ramdisk extracted from boot.img
@ -52,7 +54,7 @@ def get_mtk_label(path) -> str | None:
return label return label
def get_qcdt_type(path) -> str | None: def get_qcdt_type(path: PathString) -> str | None:
"""Get the dt.img type by reading the first four bytes of the file. """Get the dt.img type by reading the first four bytes of the file.
:param path: to the qcdt image extracted from boot.img :param path: to the qcdt image extracted from boot.img
:returns: * None: dt.img is of unknown type :returns: * None: dt.img is of unknown type
@ -195,5 +197,5 @@ def bootimg(path: Path) -> Bootimg:
) )
def trim_input(f) -> str: def trim_input(f: TextIO) -> str:
return f.read().replace("\n", "") return f.read().replace("\n", "")

View file

@ -10,7 +10,7 @@ from pmb.core.context import get_context
def package_provider( def package_provider(
pkgname, pkgnames_install, suffix: Chroot = Chroot.native() pkgname: str, pkgnames_install: list[str], suffix: Chroot = Chroot.native()
) -> pmb.core.apkindex_block.ApkindexBlock | None: ) -> pmb.core.apkindex_block.ApkindexBlock | None:
""" """
:param pkgnames_install: packages to be installed :param pkgnames_install: packages to be installed

View file

@ -60,7 +60,7 @@ def _parse_kernel_suffix(info, device, kernel):
@Cache("device", "kernel") @Cache("device", "kernel")
def deviceinfo(device=None, kernel=None) -> "Deviceinfo": def deviceinfo(device: str | None = None, kernel: str | None = None) -> "Deviceinfo":
""" """
:param device: defaults to args.device :param device: defaults to args.device
:param kernel: defaults to args.kernel :param kernel: defaults to args.kernel
@ -170,7 +170,7 @@ class Deviceinfo:
keymaps: str | None = "" keymaps: str | None = ""
@staticmethod @staticmethod
def __validate(info: dict[str, str], path: Path): def __validate(info: dict[str, str], path: Path) -> None:
# Resolve path for more readable error messages # Resolve path for more readable error messages
path = path.resolve() path = path.resolve()

View file

@ -11,6 +11,7 @@ import pmb.parse
import pmb.helpers.pmaports import pmb.helpers.pmaports
import pmb.parse.kconfigcheck import pmb.parse.kconfigcheck
from pmb.helpers.exceptions import NonBugError from pmb.helpers.exceptions import NonBugError
from pmb.types import PathString
def is_set(config, option): def is_set(config, option):
@ -158,7 +159,14 @@ def check_config_options_set(
return ret return ret
def check_config(config_path, config_arch, pkgver, categories: list, details=False): # TODO: This should probably use Arch and not str for config_arch
def check_config(
config_path: PathString,
config_arch: str,
pkgver: str,
categories: list[str],
details: bool = False,
) -> bool:
""" """
Check, whether one kernel config passes the rules of multiple components. Check, whether one kernel config passes the rules of multiple components.
@ -295,7 +303,9 @@ def extract_version(config_path):
return "unknown" return "unknown"
def check_file(config_path, components_list: list[str] = [], details=False): def check_file(
config_path: PathString, components_list: list[str] = [], details: bool = False
) -> bool:
""" """
Check for necessary kernel config options in a kconfig file. Check for necessary kernel config options in a kconfig file.

View file

@ -206,7 +206,7 @@ def validate(version):
return True return True
def compare(a_version: str, b_version: str, fuzzy=False): def compare(a_version: str, b_version: str, fuzzy: bool = False) -> int:
""" """
Compare two versions A and B to find out which one is higher, or if Compare two versions A and B to find out which one is higher, or if
both are equal. both are equal.

View file

@ -21,13 +21,13 @@ import pmb.chroot.initfs
import pmb.config import pmb.config
import pmb.config.pmaports import pmb.config.pmaports
import pmb.install.losetup import pmb.install.losetup
from pmb.types import PathString, PmbArgs from pmb.types import Env, PathString, PmbArgs
import pmb.helpers.run import pmb.helpers.run
import pmb.parse.cpuinfo import pmb.parse.cpuinfo
from pmb.core import Chroot, ChrootType from pmb.core import Chroot, ChrootType
def system_image(device: str): def system_image(device: str) -> Path:
""" """
Returns path to rootfs for specified device. In case that it doesn't Returns path to rootfs for specified device. In case that it doesn't
exist, raise and exception explaining how to generate it. exist, raise and exception explaining how to generate it.
@ -41,7 +41,7 @@ def system_image(device: str):
return path return path
def create_second_storage(args: PmbArgs, device: str): def create_second_storage(args: PmbArgs, device: str) -> Path:
""" """
Generate a second storage image if it does not exist. Generate a second storage image if it does not exist.
@ -55,7 +55,7 @@ def create_second_storage(args: PmbArgs, device: str):
return path return path
def which_qemu(arch: Arch): def which_qemu(arch: Arch) -> str:
""" """
Finds the qemu executable or raises an exception otherwise Finds the qemu executable or raises an exception otherwise
""" """
@ -97,7 +97,13 @@ def create_gdk_loader_cache(args: PmbArgs) -> Path:
return chroot_native / custom_cache_path return chroot_native / custom_cache_path
def command_qemu(args: PmbArgs, config: Config, arch: Arch, img_path, img_path_2nd=None): def command_qemu(
args: PmbArgs,
config: Config,
arch: Arch,
img_path: Path,
img_path_2nd: Path | None = None,
) -> tuple[list[str | Path], Env]:
""" """
Generate the full qemu command with arguments to run postmarketOS Generate the full qemu command with arguments to run postmarketOS
""" """
@ -139,6 +145,11 @@ def command_qemu(args: PmbArgs, config: Config, arch: Arch, img_path, img_path_2
if not arch.is_native() and ncpus > 8: if not arch.is_native() and ncpus > 8:
ncpus = 8 ncpus = 8
env: Env
# It might be tempting to use PathString here, but I don't think it makes sense semantically as
# this is not just a list of paths.
command: list[str | Path]
if args.host_qemu: if args.host_qemu:
qemu_bin = which_qemu(arch) qemu_bin = which_qemu(arch)
env = {} env = {}
@ -319,7 +330,7 @@ def sigterm_handler(number, frame):
) )
def install_depends(args: PmbArgs, arch: Arch): def install_depends(args: PmbArgs, arch: Arch) -> None:
""" """
Install any necessary qemu dependencies in native chroot Install any necessary qemu dependencies in native chroot
""" """
@ -358,7 +369,7 @@ def install_depends(args: PmbArgs, arch: Arch):
pmb.chroot.apk.install(depends, chroot) pmb.chroot.apk.install(depends, chroot)
def run(args: PmbArgs): def run(args: PmbArgs) -> None:
""" """
Run a postmarketOS image in qemu Run a postmarketOS image in qemu
""" """

View file

@ -1,6 +1,7 @@
# Copyright 2023 Martijn Braam # Copyright 2023 Martijn Braam
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
from pathlib import Path
from typing import Optional from typing import Optional
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.helpers import logging from pmb.helpers import logging
@ -63,7 +64,7 @@ def ssh_find_arch(args: PmbArgs, user: str, host: str, port: str) -> Arch:
return alpine_architecture return alpine_architecture
def ssh_install_apks(args: PmbArgs, user, host, port, paths: list) -> None: def ssh_install_apks(args: PmbArgs, user: str, host: str, port: str, paths: list[Path]) -> None:
"""Copy binary packages via SCP and install them via SSH. """Copy binary packages via SCP and install them via SSH.
:param user: target device ssh username :param user: target device ssh username
:param host: target device ssh hostname :param host: target device ssh hostname
@ -95,7 +96,13 @@ def ssh_install_apks(args: PmbArgs, user, host, port, paths: list) -> None:
def sideload( def sideload(
args: PmbArgs, user: str, host: str, port: str, arch: Arch | None, copy_key: bool, pkgnames args: PmbArgs,
user: str,
host: str,
port: str,
arch: Arch | None,
copy_key: bool,
pkgnames: list[str],
) -> None: ) -> None:
"""Build packages if necessary and install them via SSH. """Build packages if necessary and install them via SSH.

View file

@ -1,6 +1,7 @@
# Copyright 2024 Caleb Connolly # Copyright 2024 Caleb Connolly
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import subprocess
from argparse import Namespace from argparse import Namespace
from pathlib import Path from pathlib import Path
from typing import Any, Literal, TypedDict from typing import Any, Literal, TypedDict
@ -8,6 +9,10 @@ from typing import Any, Literal, TypedDict
from pmb.core.arch import Arch from pmb.core.arch import Arch
CrossCompileType = Literal["native", "crossdirect"] | None CrossCompileType = Literal["native", "crossdirect"] | None
RunOutputTypeDefault = Literal["log", "stdout", "interactive", "tui", "null"]
RunOutputTypePopen = Literal["background", "pipe"]
RunOutputType = RunOutputTypeDefault | RunOutputTypePopen
RunReturnType = str | int | subprocess.Popen
PathString = Path | str PathString = Path | str
Env = dict[str, PathString] Env = dict[str, PathString]
Apkbuild = dict[str, Any] Apkbuild = dict[str, Any]