updated output

This commit is contained in:
Florian Schroegendorfer 2024-06-12 01:45:25 +02:00
parent 2d75643625
commit b703ed2605
Signed by: root
GPG Key ID: 17625E28D4D6E339
7 changed files with 97 additions and 82 deletions

View File

@ -21,7 +21,9 @@ jobs = "4"
# gentoo's fancy terminal output functions
out = output.EOutput()
out.print = lambda s: print(s) if not out.quiet else None
out.emph = lambda s: colorize("HILITE", str(s))
out.green = lambda s: colorize("green", s if isinstance(s, str) else str(s))
out.red = lambda s: colorize("red", s if isinstance(s, str) else str(s))
out.teal = lambda s: colorize("teal", s if isinstance(s, str) else str(s))
# disable colorization for pipes and redirects
if not sys.stdout.isatty():
@ -49,11 +51,11 @@ class Kernel:
"""Construct a Kernel based on a given source path."""
self.src = pathlib.Path(src)
if not self.src.exists():
raise ValueError(f"missing source: {src}")
raise ValueError(f"error: missing source {src}")
try:
self.version = version(self.src.name)
except Exception as e:
raise ValueError(f"illegal source: {src}") from e
raise ValueError(f"error: illegal source {src}") from e
self.config = self.src / ".config"
self.bzImage = self.src / "arch/x86_64/boot/bzImage"
self.bkp = efi.img.parent / f"gentoo-{self.version.base_version}.efi"
@ -150,7 +152,7 @@ def efi (f):
# find currently running entry/image
def loader (line, start=9):
i = line.find("File", start)
if i < 0: raise RuntimeError(f"error locating boot image:\n{line}")
if i < 0: raise RuntimeError(f"error: missing boot image:\n{line}")
i += 6
return pathlib.Path(l[i:line.find(")", i)].replace("\\", "/"))
for l in lines:
@ -183,8 +185,7 @@ def efi (f):
break
else: continue
break
else:
raise RuntimeError("error finding ESP mountpoint")
else: raise RuntimeError("error: missing mountpoint of ESP")
try:
subprocess.run(
["mount", str(efi.esp)],
@ -305,25 +306,25 @@ def configure (argv):
# make oldconfig
if not kernel.config.exists() and oldconfig.exists():
# copy oldconfig
out.einfo(f"copying {out.emph(oldconfig)}")
out.einfo(f"copying {out.teal(oldconfig)}")
shutil.copy(oldconfig, kernel.config)
# store newly added options
out.einfo(f"running {out.emph('make listnewconfig')}")
out.einfo(f"running {out.teal('make listnewconfig')}")
make = subprocess.run(["make", "listnewconfig"], capture_output=True)
newoptions.write_text(make.stdout.decode())
# configure
if not args.list:
out.einfo(f"running {out.emph('make oldconfig')}")
out.einfo(f"running {out.teal('make oldconfig')}")
subprocess.run(["make", "oldconfig"], check=True)
# make menuconfig
elif not args.list:
out.einfo(f"running {out.emph('make menuconfig')}")
out.einfo(f"running {out.teal('make menuconfig')}")
subprocess.run(["make", "menuconfig"], check=True)
# check if we should print new options
if args.list:
if not newoptions.exists():
raise FileNotFoundError(f"missing {newoptions}")
raise FileNotFoundError(f"error: missing {newoptions}")
for l in newoptions.read_text().splitlines():
opt, val = l.split("=", maxsplit=1)
out.print(f" {opt} = {val}")
@ -392,15 +393,15 @@ def build (argv):
# check if config exists
if not kernel.config.exists():
raise FileNotFoundError(f"missing config: {kernel.config}")
raise FileNotFoundError(f"error: missing config {kernel.config}")
# change directory
os.chdir(kernel.src)
# build and install modules
out.einfo(f"building {out.emph(kernel.src.name)}")
out.einfo(f"building {out.teal(kernel.src)}")
subprocess.run(["make", "-j", str(args.jobs)], check=True)
out.einfo("installing modules")
out.einfo(f"installing modules {out.teal(kernel.modules)}")
subprocess.run(["make", "modules_install"], check=True)
@cli
@ -469,16 +470,44 @@ def install (argv):
kernel = Kernel(args.src)
out.quiet = args.quiet
# store running image for latter comparison
if args.bkp:
boot_bytes = efi.img.read_bytes()
# check if bzImage exists
if not kernel.bzImage.exists():
raise FileNotFoundError(f"missing bzImage {kernel.bzImage}")
raise FileNotFoundError(f"error: missing bzImage {kernel.bzImage}")
# update symlink to the new source directory
out.einfo(
"updating symlink "
f"{out.teal(kernel.linux)}{out.teal(kernel.src)}"
)
subprocess.run(
["eselect", "kernel", "set", kernel.src.name],
check=True
)
# copy boot image
out.einfo(f"creating boot image {out.teal(efi.img)}")
shutil.copy(kernel.bzImage, efi.img)
# create backup
out.einfo(f"creating backup image {out.teal(kernel.bkp)}")
shutil.copy(kernel.bzImage, kernel.bkp)
# rebuild external modules
eargs = ["emerge", "@module-rebuild"]
if args.quiet:
eargs.insert(1, "-q")
out.einfo(f"running {out.teal(' '.join(eargs))}")
subprocess.run(eargs, check=True)
# create fallback boot entry
if args.bkp:
# path to backup image
bkp = None
# find the currently running kernel's backup image
boot_bytes = efi.img.read_bytes()
for f in efi.img.parent.glob("gentoo*.efi"):
if f.read_bytes() == boot_bytes:
bkp = f
@ -494,12 +523,10 @@ def install (argv):
capture_output=True,
check=True
)
disk, part = filter(
None,
re.search(r"([/a-z]+)(\d+)", dev.stdout.decode()).groups()
)
disk, part = re.search(r"([/a-z]+)(\d+)", dev.stdout.decode()).groups()
# remove previous entry
if "num" in efi.bkp:
out.einfo(f"deleting boot entry {out.teal(efi.bkp['label'])}")
subprocess.run([
"efibootmgr",
"-q",
@ -507,6 +534,7 @@ def install (argv):
"-B"
], check=True)
# create entry
out.einfo(f"creating boot entry {out.teal(efi.bkp['label'])}")
subprocess.run([
"efibootmgr",
"-q",
@ -518,28 +546,6 @@ def install (argv):
], check=True)
efi.bkp["img"] = bkp
# update symlink to the new source directory
out.einfo(
"updating symlink "
f"{out.emph(kernel.linux)}{out.emph(kernel.src)}"
)
subprocess.run(
["eselect", "kernel", "set", kernel.src.name],
check=True
)
# copy boot image
out.einfo(f"creating boot image {out.emph(efi.img)}")
shutil.copy(kernel.bzImage, efi.img)
# create backup
out.einfo(f"creating backup image {out.emph(kernel.bkp)}")
shutil.copy(kernel.bzImage, kernel.bkp)
# rebuild external modules
out.einfo(f"rebuilding external kernel modules")
subprocess.run(["emerge", "@module-rebuild"], check=True)
@cli
@efi
def clean (argv):
@ -593,7 +599,7 @@ def clean (argv):
args = parser.parse_args(argv)
out.quiet = args.quiet
if args.keep < 1:
raise ValueError("at least one bootable kernel must be kept")
raise ValueError("error: at least one bootable kernel must be kept")
# retained kernels
keep = {"kernels": []}
@ -627,15 +633,18 @@ def clean (argv):
]
# run depclean
out.einfo(f"running {out.emph('emerge -cq gentoo-sources')}")
if not args.dry:
subprocess.run(["emerge", "-cq", "gentoo-sources"])
eargs = ["emerge", "-c", "gentoo-sources"]
if args.quiet:
eargs.insert(1, "-q")
out.einfo(f"running {out.teal(' '.join(eargs))}")
subprocess.run(eargs, check=True)
# remove files
for k, v in rm.items():
out.einfo(f"deleting {k}:")
for p in v:
out.print(f" {colorize('BAD', '')} {out.emph(p)}")
out.print(f" {out.red('')} {out.teal(p)}")
if args.dry: continue
if p.is_dir():
shutil.rmtree(p)
@ -646,7 +655,7 @@ def clean (argv):
if efi.bkp and "img" in efi.bkp:
bkp = efi.bkp["img"]
if not bkp.exists() or bkp in rm["images"]:
out.einfo(f"deleting boot entry {out.emph(efi.bkp['label'])}")
out.einfo(f"deleting boot entry {out.teal(efi.bkp['label'])}")
if not args.dry:
subprocess.run([
"efibootmgr",
@ -689,14 +698,14 @@ def commit (argv):
def summarize (diff: list[str]):
"""Generate the summary of changed options."""
def changes (s):
def startswith (ch):
return dict([
x[1:].split("=", maxsplit=1)
for x in diff
if x.startswith(s + "CONFIG") and "CC_VERSION" not in x
if x.startswith(ch + "CONFIG") and "CC_VERSION" not in x
])
additions = changes("+")
deletions = changes("-")
additions = startswith("+")
deletions = startswith("-")
changes = {
k: (deletions[k], additions[k])
for k in additions.keys() & deletions.keys()
@ -749,7 +758,7 @@ def commit (argv):
# ensure that a config exists
if not kernel.config.exists():
raise FileNotFoundError(f"missing config: {kernel.config}")
raise FileNotFoundError(f"error: missing config {kernel.config}")
# change to source directory
os.chdir(kernel.src)
@ -856,15 +865,15 @@ def commit (argv):
if removals or config_changed:
out.einfo("changes to be committed:")
for l in removals:
out.print(f" {colorize('QAWARN', '-')} {out.emph(l)}")
out.print(f" {out.red('')} {out.teal(l)}")
if config_changed:
out.print(f" {colorize('INFO', '+')} {out.emph(kernel.config)}")
out.print(f" {out.green('')} {out.teal(kernel.config)}")
# print message
if msg:
out.einfo("commit message:")
for l in msg.getvalue().splitlines():
out.print(f" {l}" if l else "")
for l in msg.getvalue().splitlines():
out.print(f" {out.teal(l)}" if l else "")
# dry run: revert staged changes
if args.dry:
@ -874,7 +883,7 @@ def commit (argv):
# commit
try:
out.ebegin("committing")
git(["commit", "-m", msg.getvalue()])
ret = git(["commit", "-m", msg.getvalue()])
out.eend(0)
except subprocess.CalledProcessError as e:
out.eend(1)

View File

@ -57,8 +57,8 @@ class Tests (unittest.TestCase):
# emerge -cq gentoo-sources
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(args, (["emerge", "-cq", "gentoo-sources"],))
self.assertEqual(kwargs, {})
self.assertEqual(args, (["emerge", "-q", "-c", "gentoo-sources"],))
self.assertEqual(kwargs, {"check": True})
if keep < 3:
# efibootmgr -b 0003 -B
tracer, (args, kwargs) = next(trace_it)
@ -128,7 +128,6 @@ class Tests (unittest.TestCase):
"images": [k.bkp for k in kernels]
}
expected = io.StringIO()
expected.write(" * running emerge -cq gentoo-sources\n")
for k, v in rm.items():
expected.write(f" * deleting {k}:\n")
for p in v:

View File

@ -154,7 +154,7 @@ class Tests (unittest.TestCase):
run()
self.assertEqual(
sys.stderr.getvalue(),
f" * missing config: {self.latest.config}\n"
f" * error: missing config {self.latest.config}\n"
)
@colorless
@ -177,8 +177,8 @@ class Tests (unittest.TestCase):
self.assertEqual(run("-n"), 0)
self.assertEqual(sys.stdout.getvalue(),
" * changes to be committed:\n"
f" - {self.current.config}\n"
f" + {self.latest.config}\n"
f" {self.current.config}\n"
f" {self.latest.config}\n"
" * commit message:\n"
" kernel update: "
f"{self.current.version}{self.latest.version}\n"
@ -214,7 +214,7 @@ class Tests (unittest.TestCase):
self.assertEqual(run("-n", "-m", "details"), 0)
self.assertEqual(sys.stdout.getvalue(),
" * changes to be committed:\n"
f" - {self.current.config}\n"
f" {self.current.config}\n"
" * commit message:\n"
" removed old kernel leftovers\n\n"
" details\n"

View File

@ -112,7 +112,7 @@ class Tests (unittest.TestCase):
with self.assertRaises(SystemExit):
run("-l")
self.assertEqual(sys.stderr.getvalue(),
f" * missing {self.kernel.newoptions}\n"
f" * error: missing {self.kernel.newoptions}\n"
)
@colorless
@ -122,7 +122,7 @@ class Tests (unittest.TestCase):
with self.assertRaises(SystemExit):
run("-l")
self.assertEqual(sys.stderr.getvalue(),
f" * missing {self.kernel.newoptions}\n"
f" * error: missing {self.kernel.newoptions}\n"
)
@colorless

View File

@ -51,6 +51,20 @@ class Tests (unittest.TestCase):
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(args, (["mount", "/boot"],))
self.assertEqual(kwargs, {"capture_output": True, "check": True})
# eselect kernel set <name>
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(
args,
(["eselect", "kernel", "set", self.kernel.src.name],)
)
self.assertEqual(kwargs, {"check": True})
self.assertEqual(str(data.linux.readlink()), self.kernel.src.name)
# emerge @module-rebuild
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(args, (["emerge", "-q", "@module-rebuild"],))
self.assertEqual(kwargs, {"check": True})
if backup:
if data.boot.read_bytes() == b"missing image":
# platform.release
@ -79,20 +93,6 @@ class Tests (unittest.TestCase):
"-l", str(self.current.bkp)
],))
self.assertEqual(kwargs, {"check": True})
# eselect kernel set <name>
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(
args,
(["eselect", "kernel", "set", self.kernel.src.name],)
)
self.assertEqual(kwargs, {"check": True})
self.assertEqual(str(data.linux.readlink()), self.kernel.src.name)
# emerge @module-rebuild
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")
self.assertEqual(args, (["emerge", "@module-rebuild"],))
self.assertEqual(kwargs, {"check": True})
# umount <boot>
tracer, (args, kwargs) = next(trace_it)
self.assertEqual(tracer.name, "subprocess.run")

View File

@ -72,4 +72,4 @@ class Tests (unittest.TestCase):
shutil.rmtree(kernel.src)
with self.assertRaises(ValueError) as e:
ekernel.Kernel.current()
self.assertEqual(str(e.exception), f"missing source: {kernel.src}")
self.assertEqual(str(e.exception), f"error: missing source {kernel.src}")

View File

@ -29,7 +29,9 @@ class Tests (unittest.TestCase):
# start interceptor
@data.efi
def run (tracer, *args, **kwargs):
if args[0][0] == "make":
if args[0][0] == "findmnt":
return subprocess.CompletedProcess("", 0, b"/dev/sda1")
elif args[0][0] == "make":
if args[0][1] == "listnewconfig":
make = subprocess.CompletedProcess("", 0)
make.stdout = data.newoptions.encode()
@ -95,7 +97,12 @@ class Tests (unittest.TestCase):
self.assertEqual(run("-q", "-s", str(data.latest)), 0)
self.check_update()
def test_update_backup (self):
self.assertEqual(run("-q", "-b"), 0)
self.check_update()
def test_update_keep (self):
print()
current = Kernel.current()
self.assertEqual(run("-q", "-k", "1"), 0)
self.check_update()