From f5e4427903512d9d9f4843762137635806a84d21 Mon Sep 17 00:00:00 2001 From: martino <32328813+f3rs3n@users.noreply.github.com> Date: Sat, 19 Sep 2026 12:05:10 +0200 Subject: [PATCH] fix: restore Fastfetch config generation --- .../post_install/customizable_post_install.sh | 30 +++++-- tests/test_fastfetch_config_generation.py | 90 +++++++++++++++++++ 2 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/test_fastfetch_config_generation.py diff --git a/scripts/post_install/customizable_post_install.sh b/scripts/post_install/customizable_post_install.sh index 056ac8a1..3dae2527 100644 --- a/scripts/post_install/customizable_post_install.sh +++ b/scripts/post_install/customizable_post_install.sh @@ -2521,8 +2521,28 @@ enable_ha() { +_generate_fastfetch_config() { + local target="$1" + local temp_dir generated_config + + temp_dir="$(mktemp -d)" || return 1 + generated_config="$temp_dir/config.jsonc" + + if ! fastfetch --gen-config "$generated_config" > /dev/null 2>&1; then + rm -rf "$temp_dir" + return 1 + fi + + if ! pmx_write_file "$target" < "$generated_config"; then + rm -rf "$temp_dir" + return 1 + fi + + rm -rf "$temp_dir" +} + configure_fastfetch() { - local FUNC_VERSION="1.1" + local FUNC_VERSION="1.2" pmx_journal_context "configure_fastfetch" "$FUNC_VERSION" # description: Install Fastfetch system summary tool with the ProxMenux logo + status block as the SSH login banner. msg_info2 "$(translate "Installing and configuring Fastfetch...")" @@ -2608,13 +2628,11 @@ configure_fastfetch() { return 1 fi - - if [ ! -f "$fastfetch_config" ]; then - echo '{"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", "modules": []}' | pmx_write_file "$fastfetch_config" + if ! _generate_fastfetch_config "$fastfetch_config"; then + msg_error "$(translate "Failed to generate Fastfetch configuration.")" + return 1 fi - fastfetch --gen-config-force > /dev/null 2>&1 - while true; do # Define logo options local logo_options=("ProxMenux" "Proxmox (default)" "JC Channel" "Comunidad Helper-Scripts" "Home-Labs-Club" "Proxmology" "Custom") diff --git a/tests/test_fastfetch_config_generation.py b/tests/test_fastfetch_config_generation.py new file mode 100644 index 00000000..63aeb494 --- /dev/null +++ b/tests/test_fastfetch_config_generation.py @@ -0,0 +1,90 @@ +"""Regression tests for Fastfetch config generation. + +Run: python3 -m unittest discover -s tests -p test_fastfetch_config_generation.py -v +""" +from pathlib import Path +import re +import shlex +import subprocess +import tempfile +import unittest + +SCRIPT = ( + Path(__file__).resolve().parents[1] + / "scripts/post_install/customizable_post_install.sh" +) + + +def extract_function(name: str) -> str: + text = SCRIPT.read_text() + match = re.search(rf"^{re.escape(name)}\(\) \{{\n.*?^\}}", text, re.M | re.S) + if match is None: + raise AssertionError(f"function {name} not found") + return match.group(0) + + +class FastfetchConfigGeneration(unittest.TestCase): + def test_script_does_not_use_retired_force_option(self): + self.assertNotIn("--gen-config-force", SCRIPT.read_text()) + + def run_case(self, fastfetch_exit: int = 0): + function = extract_function("_generate_fastfetch_config") + with tempfile.TemporaryDirectory(prefix="fastfetch-config-test-") as td: + root = Path(td) + fake_bin = root / "bin" + fake_bin.mkdir() + target = root / "config.jsonc" + target.write_text('{"modules":["preserved"]}\n') + calls = root / "calls" + fake = fake_bin / "fastfetch" + fake.write_text( + "#!/bin/bash\n" + "printf '%s\\n' \"$*\" >> \"$CALLS\"\n" + f"if (( {fastfetch_exit} != 0 )); then exit {fastfetch_exit}; fi\n" + "[[ \"$1\" == --gen-config && -n \"$2\" ]] || exit 90\n" + "[[ ! -e \"$2\" ]] || exit 91\n" + "printf '%s\\n' '{\"modules\":[\"os\",\"kernel\"]}' > \"$2\"\n" + ) + fake.chmod(0o755) + q = shlex.quote + harness = function + f""" +pmx_write_file() {{ cat > "$1"; }} +_generate_fastfetch_config {q(str(target))} +printf 'status=%s\n' "$?" +printf 'target=%s\n' "$(cat {q(str(target))})" +""" + result = subprocess.run( + ["/bin/bash", "--noprofile", "--norc"], + input=harness, + text=True, + capture_output=True, + timeout=10, + env={ + "PATH": f"{fake_bin}:/usr/bin:/bin", + "CALLS": str(calls), + "LC_ALL": "C", + }, + ) + return result, target.read_text(), calls.read_text() if calls.exists() else "" + + def test_generates_to_temporary_path_before_replacing_target(self): + result, target, calls = self.run_case() + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("status=0\n", result.stdout) + self.assertEqual(target, '{"modules":["os","kernel"]}\n') + self.assertRegex(calls, r"^--gen-config /tmp/.+/config\.jsonc\n$") + self.assertNotIn("--gen-config-force", calls) + generated_config = Path(calls.removeprefix("--gen-config ").strip()) + self.assertFalse(generated_config.parent.exists()) + + def test_generation_failure_preserves_existing_config(self): + result, target, calls = self.run_case(fastfetch_exit=17) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("status=1\n", result.stdout) + self.assertEqual(target, '{"modules":["preserved"]}\n') + generated_config = Path(calls.removeprefix("--gen-config ").strip()) + self.assertFalse(generated_config.parent.exists()) + + +if __name__ == "__main__": + unittest.main()