diff --git a/pmb/config/__init__.py b/pmb/config/__init__.py index 7cd45729..61b15bdc 100644 --- a/pmb/config/__init__.py +++ b/pmb/config/__init__.py @@ -1109,10 +1109,6 @@ git_repos = { "pmaports": "https://gitlab.com/postmarketOS/pmaports.git", } -# When a git repository is considered outdated (in seconds) -# (Measuring timestamp of FETCH_HEAD: https://stackoverflow.com/a/9229377) -git_repo_outdated = 3600 * 24 * 2 - # # APORTGEN # diff --git a/pmb/config/other.py b/pmb/config/other.py index f8d122bd..a10a2eb6 100644 --- a/pmb/config/other.py +++ b/pmb/config/other.py @@ -14,3 +14,17 @@ def is_systemd_selected(args): if args.systemd == "never": return False return pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd") + + +def systemd_selected_str(args): + if "systemd" not in pmb.config.pmaports.read_config_repos(args): + return "no", "not supported by pmaports branch" + if pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd-never"): + return "no", "not supported by selected UI" + if args.systemd == "always": + return "yes", "'always' selected in 'pmbootstrap init'" + if args.systemd == "never": + return "no", "'never' selected in 'pmbootstrap init'" + if pmb.helpers.ui.check_option(args, args.ui, "pmb:systemd"): + return "yes", "default for selected UI" + return "no", "default for selected UI" diff --git a/pmb/helpers/frontend.py b/pmb/helpers/frontend.py index bb2d164a..87b6ba9f 100644 --- a/pmb/helpers/frontend.py +++ b/pmb/helpers/frontend.py @@ -624,8 +624,10 @@ def lint(args): def status(args: Namespace) -> None: - if not pmb.helpers.status.print_status(args, args.details): - sys.exit(1) + pmb.helpers.status.print_status(args) + + # Do not print the DONE! line + sys.exit(0) def ci(args): diff --git a/pmb/helpers/git.py b/pmb/helpers/git.py index f04901b7..dab1b1a9 100644 --- a/pmb/helpers/git.py +++ b/pmb/helpers/git.py @@ -3,7 +3,6 @@ import configparser import logging import os -import time import pmb.build import pmb.chroot.apk @@ -229,25 +228,6 @@ def pull(args, name_repo): return 0 -def is_outdated(path): - # FETCH_HEAD always exists in repositories cloned by pmbootstrap. - # Usually it does not (before first git fetch/pull), but there is no good - # fallback. For exampe, getting the _creation_ date of .git/HEAD is non- - # trivial with python on linux (https://stackoverflow.com/a/39501288). - # Note that we have to assume here that the user had fetched the "origin" - # repository. If the user fetched another repository, FETCH_HEAD would also - # get updated, even though "origin" may be outdated. For pmbootstrap status - # it is good enough, because it should help the users that are not doing - # much with pmaports.git to know when it is outdated. People who manually - # fetch other repos should usually know that and how to handle that - # situation. - path_head = path + "/.git/FETCH_HEAD" - date_head = os.path.getmtime(path_head) - - date_outdated = time.time() - pmb.config.git_repo_outdated - return date_head <= date_outdated - - def get_topdir(args, path): """ :returns: a string with the top dir of the git repository, or an empty string if it's not a git repository. """ diff --git a/pmb/helpers/status.py b/pmb/helpers/status.py index c2f1b922..87107dca 100644 --- a/pmb/helpers/status.py +++ b/pmb/helpers/status.py @@ -1,165 +1,58 @@ -# Copyright 2023 Oliver Smith +# Copyright 2024 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later -import os -import logging - import pmb.config import pmb.config.workdir import pmb.helpers.git from argparse import Namespace -from typing import List, Tuple -def print_config(args: Namespace) -> None: - """ Print an overview of what was set in "pmbootstrap init". """ - logging.info("*** CONFIG ***") - info = args.deviceinfo - logging.info("Device: {} ({}, \"{}\")" - .format(args.device, info["arch"], info["name"])) +def print_status_line(key: str, value: str): + styles = pmb.config.styles + key = f"{styles['GREEN']}{key}{styles['END']}:" + padding = 17 - if pmb.parse._apkbuild.kernels(args, args.device): - logging.info("Kernel: " + args.kernel) - - if args.extra_packages != "none": - logging.info("Extra packages: {}".format(args.extra_packages)) - - logging.info("User Interface: {}".format(args.ui)) + print(f"{key.ljust(padding)} {value}") -def print_git_repos(args: Namespace) -> None: - logging.info("*** GIT REPOS ***") - logging.info("Path: {}/cache_git".format(args.work)) - for repo in pmb.config.git_repos.keys(): - path = pmb.helpers.git.get_path(args, repo) - if not os.path.exists(path): - continue +def print_channel(args: Namespace) -> None: + pmaports_cfg = pmb.config.pmaports.read_config(args) + channel = pmaports_cfg["channel"] - # Get branch name (if on branch) or current commit - ref = pmb.helpers.git.rev_parse(args, path, - extra_args=["--abbrev-ref"]) - if ref == "HEAD": - ref = pmb.helpers.git.rev_parse(args, path)[0:8] - - logging.info("- {} ({})".format(repo, ref)) - - -def print_checks_git_repo(args: Namespace, repo: str, details: bool=True) -> Tuple[int, str]: - """ Perform various checks on one checked out git repo. - :param details: if True, print each passing check (this is True by - default for the testsuite) - :returns: status, todo_msg - - status: integer, 0 if all passed, < 0 on failure - - msg_todo: message to help the user resolve the failure """ - def log_ok(msg_ok): - if details: - logging.info("[OK ] {}: {}".format(repo, msg_ok)) - - def log_nok_ret(status, msg_nok, msg_todo): - logging.warning("[NOK] {}: {}".format(repo, msg_nok)) - return (status, msg_todo) - - # On official branch - path = pmb.helpers.git.get_path(args, repo) - branches = pmb.helpers.git.get_branches_official(args, repo) + # Get branch name (if on branch) or current commit + path = pmb.helpers.git.get_path(args, "pmaports") ref = pmb.helpers.git.rev_parse(args, path, extra_args=["--abbrev-ref"]) - if ref not in branches: - return log_nok_ret(-1, "not on official channel branch", - "consider checking out: " + ", ".join(branches)) - log_ok("on official channel branch") + if ref == "HEAD": + ref = pmb.helpers.git.rev_parse(args, path)[0:8] - # Workdir clean if not pmb.helpers.git.clean_worktree(args, path): - return log_nok_ret(-2, "workdir is not clean", - "consider cleaning your workdir") - log_ok("workdir is clean") + ref += ", dirty" - # Tracking proper remote - remote_upstream = pmb.helpers.git.get_upstream_remote(args, repo) - branch_upstream = remote_upstream + "/" + ref - remote_ref = pmb.helpers.git.rev_parse(args, path, ref + "@{u}", - ["--abbrev-ref"]) - if remote_ref != branch_upstream: - return log_nok_ret(-3, "tracking unexpected remote branch", - "consider tracking remote branch '{}' instead of" - " '{}'".format(branch_upstream, remote_ref)) - log_ok("tracking proper remote branch '{}'".format(branch_upstream)) - - # Up to date - ref_branch = pmb.helpers.git.rev_parse(args, path, ref) - ref_branch_upstream = pmb.helpers.git.rev_parse(args, path, - branch_upstream) - if ref_branch != ref_branch_upstream: - return log_nok_ret(-4, "not up to date with remote branch", - "update with 'pmbootstrap pull'") - log_ok("up to date with remote branch") - - # Outdated remote information - if pmb.helpers.git.is_outdated(path): - return log_nok_ret(-5, "outdated remote information", - "update with 'pmbootstrap pull'") - log_ok("remote information updated recently (via git fetch/pull)") - - return (0, "") + value = f"{channel} (pmaports: {ref})" + print_status_line("Channel", value) -def print_checks_git_repos(args: Namespace, details: bool) -> List[str]: - """ Perform various checks on the checked out git repos. - :param details: if True, print each passing check - :returns: list of unresolved checklist items """ - ret = [] - for repo in pmb.config.git_repos.keys(): - path = pmb.helpers.git.get_path(args, repo) - if not os.path.exists(path): - continue - status, todo_msg = print_checks_git_repo(args, repo, details) - if status: - ret += ["{}: {}".format(repo, todo_msg)] - return ret +def print_device(args: Namespace) -> None: + kernel = "" + if pmb.parse._apkbuild.kernels(args, args.device): + kernel = f", kernel: {args.kernel}" + + value = f"{args.device} ({args.deviceinfo['arch']}{kernel})" + print_status_line("Device", value) -def print_checks_chroots_outdated(args: Namespace, details: bool) -> List[str]: - """ Check if chroots were zapped recently. - :param details: if True, print each passing check instead of a summary - :returns: list of unresolved checklist items """ - if pmb.config.workdir.chroots_outdated(args): - logging.info("[NOK] Chroots not zapped recently") - return ["Run 'pmbootstrap zap' to delete possibly outdated chroots"] - elif details: - logging.info("[OK ] Chroots zapped recently (or non-existing)") - return [] +def print_ui(args: Namespace) -> None: + print_status_line("UI", args.ui) -def print_checks(args: Namespace, details: bool) -> bool: +def print_systemd(args: Namespace) -> None: + yesno, reason = pmb.config.other.systemd_selected_str(args) + print_status_line("systemd", f"{yesno} ({reason})") + + +def print_status(args: Namespace) -> None: """ :param details: if True, print each passing check instead of a summary :returns: True if all checks passed, False otherwise """ - logging.info("*** CHECKS ***") - checklist = [] - checklist += print_checks_chroots_outdated(args, details) - checklist += print_checks_git_repos(args, details) - - # All OK - if not checklist: - if not details: - logging.info("All checks passed! \\o/") - logging.info("") - return True - - # Some NOK: print checklist - logging.info("") - logging.info("*** CHECKLIST ***") - for item in checklist: - logging.info("- " + item) - logging.info("- Run 'pmbootstrap status' to verify that all is resolved") - return False - - -def print_status(args: Namespace, details: bool=False) -> bool: - """ :param details: if True, print each passing check instead of a summary - :returns: True if all checks passed, False otherwise """ - print_config(args) - logging.info("") - print_git_repos(args) - logging.info("") - ret = print_checks(args, details) - - return ret + print_channel(args) + print_device(args) + print_ui(args) + print_systemd(args) diff --git a/pmb/parse/arguments.py b/pmb/parse/arguments.py index 2c5c7586..01bc0538 100644 --- a/pmb/parse/arguments.py +++ b/pmb/parse/arguments.py @@ -551,9 +551,7 @@ def arguments_lint(subparser): def arguments_status(subparser): ret = subparser.add_parser("status", - help="quick health check for the work dir") - ret.add_argument("--details", action="store_true", - help="list passing checks in detail, not as summary") + help="show a config and pmaports overview") return ret diff --git a/test/test_helpers_git.py b/test/test_helpers_git.py index 3f5488d3..46d62e5f 100644 --- a/test/test_helpers_git.py +++ b/test/test_helpers_git.py @@ -4,7 +4,6 @@ import os import sys import pytest import shutil -import time import pmb_test # noqa import pmb_test.const @@ -166,29 +165,3 @@ def test_pull(args, monkeypatch, tmpdir): run_git(["reset", "--hard", "origin/master"]) run_git(["commit", "--allow-empty", "-m", "new"], "remote") assert func(args, name_repo) == 0 - - -def test_is_outdated(tmpdir, monkeypatch): - func = pmb.helpers.git.is_outdated - - # Override time.time(): now is "100" - def fake_time(): - return 100.0 - monkeypatch.setattr(time, "time", fake_time) - - # Create .git/FETCH_HEAD - path = str(tmpdir) - os.mkdir(path + "/.git") - fetch_head = path + "/.git/FETCH_HEAD" - open(fetch_head, "w").close() - - # Set mtime to 90 - os.utime(fetch_head, times=(0, 90)) - - # Outdated (date_outdated: 90) - monkeypatch.setattr(pmb.config, "git_repo_outdated", 10) - assert func(path) is True - - # Not outdated (date_outdated: 89) - monkeypatch.setattr(pmb.config, "git_repo_outdated", 11) - assert func(path) is False diff --git a/test/test_helpers_status.py b/test/test_helpers_status.py deleted file mode 100644 index 7dbd95f4..00000000 --- a/test/test_helpers_status.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2023 Oliver Smith -# SPDX-License-Identifier: GPL-3.0-or-later -""" Test pmb/helpers/status.py """ -import os -import pytest -import shutil -import sys - -import pmb_test -import pmb_test.git -import pmb.config -import pmb.config.workdir - - -@pytest.fixture -def args(request): - import pmb.parse - sys.argv = ["pmbootstrap", "init"] - args = pmb.parse.arguments() - args.log = args.work + "/log_testsuite.txt" - pmb.helpers.logging.init(args) - request.addfinalizer(pmb.helpers.logging.logfd.close) - return args - - -def test_pmbootstrap_status(args, tmpdir): - """ High level testing of 'pmbootstrap status': run it twice, once with - a fine workdir, and once where one check is failing. """ - # Prepare empty workdir - work = str(tmpdir) - with open(work + "/version", "w") as handle: - handle.write(str(pmb.config.work_version)) - - # "pmbootstrap status" succeeds (pmb.helpers.run.user verifies exit 0) - pmbootstrap = pmb.config.pmb_src + "/pmbootstrap.py" - pmb.helpers.run.user(args, [pmbootstrap, "-w", work, "status", - "--details"]) - - # Mark chroot_native as outdated - with open(work + "/workdir.cfg", "w") as handle: - handle.write("[chroot-init-dates]\nnative = 1234\n") - - # "pmbootstrap status" fails - ret = pmb.helpers.run.user(args, [pmbootstrap, "-w", work, "status"], - check=False) - assert ret == 1 - - -def test_print_checks_git_repo(args, monkeypatch, tmpdir): - """ Test pmb.helpers.status.print_checks_git_repo """ - path, run_git = pmb_test.git.prepare_tmpdir(args, monkeypatch, tmpdir) - - # Not on official branch - func = pmb.helpers.status.print_checks_git_repo - name_repo = "test" - run_git(["checkout", "-b", "inofficial-branch"]) - status, _ = func(args, name_repo) - assert status == -1 - - # Workdir is not clean - run_git(["checkout", "master"]) - shutil.copy(__file__, path + "/test.py") - status, _ = func(args, name_repo) - assert status == -2 - os.unlink(path + "/test.py") - - # Tracking different remote - status, _ = func(args, name_repo) - assert status == -3 - - # Let master track origin/master - run_git(["checkout", "-b", "temp"]) - run_git(["branch", "-D", "master"]) - run_git(["checkout", "-b", "master", "--track", "origin/master"]) - - # Not up to date - run_git(["commit", "--allow-empty", "-m", "new"], "remote") - run_git(["fetch"]) - status, _ = func(args, name_repo) - assert status == -4 - - # Up to date - run_git(["pull"]) - status, _ = func(args, name_repo) - assert status == 0 - - # Outdated remote information - def is_outdated(path): - return True - monkeypatch.setattr(pmb.helpers.git, "is_outdated", is_outdated) - status, _ = func(args, name_repo) - assert status == -5