pmbootstrap status: rework (MR 2294)

Reimplement "pmbootstrap status" to be just a simple and useful status
overview. The previous version ran a bunch of checks every time, and
would fail on these even if pmaports was used for normal development:
  * "non-official" branch checked out in pmaports
  * pmaports.git is not clean

The information about aports.git was also considered not so useful upon
revisiting this command, since it is only used for "pmbootstrap
aportgen". Most users don't need this, and if the user runs this
command, it will tell if aports.git is outdated.

All of the above made the previous version unpleasant to use and I
suspect most people stopped using the command after trying it out a few
times and seeing the irrelevant but loud NOK complaints.

New version:

  $ pmbootstrap status
  Channel: edge (pmaports: master_staging_systemd)
  Device:  qemu-amd64 (x86_64, kernel: virt)
  UI:      console
  systemd: no (default for selected UI)

Old version (without --details it only shows NOK checks):

  $ pmbootstrap status --details
  [00:55:20] *** CONFIG ***
  [00:55:20] Device: qemu-amd64 (x86_64, "QEMU amd64")
  [00:55:20] Kernel: virt
  [00:55:20] User Interface: console
  [00:55:20]
  [00:55:20] *** GIT REPOS ***
  [00:55:20] Path: /home/user/.local/var/pmbootstrap/cache_git
  [00:55:20] - aports_upstream (master)
  [00:55:20] - pmaports (master)
  [00:55:20]
  [00:55:20] *** CHECKS ***
  [00:55:20] [OK ] Chroots zapped recently (or non-existing)
  [00:55:20] [OK ] aports_upstream: on official channel branch
  [00:55:20] [OK ] aports_upstream: workdir is clean
  [00:55:20] [OK ] aports_upstream: tracking proper remote branch 'origin/master'
  [00:55:20] [OK ] aports_upstream: up to date with remote branch
  [00:55:20] [OK ] aports_upstream: remote information updated recently (via git fetch/pull)
  [00:55:20] [OK ] pmaports: on official channel branch
  [00:55:20] [OK ] pmaports: workdir is clean
  [00:55:20] [OK ] pmaports: tracking proper remote branch 'origin/master'
  [00:55:20] [OK ] pmaports: up to date with remote branch
  [00:55:20] [OK ] pmaports: remote information updated recently (via git fetch/pull)
  [00:55:20]
  [00:55:20] NOTE: chroot is still active (use 'pmbootstrap shutdown' as necessary)
  [00:55:20] DONE!
This commit is contained in:
Oliver Smith 2024-04-12 00:09:55 +02:00
parent 56dfdd4ad3
commit 0d320d0613
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
8 changed files with 54 additions and 290 deletions

View file

@ -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)