From be6ba31d7d6cf6ff6fbcdbb9287915c07bd5b558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Sun, 26 Jul 2026 14:53:49 +0200 Subject: [PATCH] feat(server-setup): add EPEL repository support for CentOS Stream --- server-setup.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/server-setup.py b/server-setup.py index 74b6045..eeb65f5 100644 --- a/server-setup.py +++ b/server-setup.py @@ -13,6 +13,7 @@ Usage: server-setup.py --dry-run # preview without changes server-setup.py --skip-update # skip system update server-setup.py --skip-packages # skip package installation + server-setup.py --skip-epel # skip EPEL setup on CentOS server-setup.py --skip-firewall # skip firewall setup server-setup.py --skip-selinux # skip SELinux server-setup.py --skip-podman # skip Podman @@ -38,7 +39,7 @@ GREEN = "\033[32m" YELLOW = "\033[33m" DIM = "\033[2m" RESET = "\033[0m" -VERSION = "1.3.0" +VERSION = "1.4.0" # Timeout in seconds for dnf operations — metadata downloads and package # transactions on a fresh or slow system routinely exceed the 60 s default @@ -307,6 +308,62 @@ def system_update(dry_run: bool = False) -> dict[str, Any]: return info +def ensure_epel(dry_run: bool = False) -> dict[str, Any]: + """Ensure the EPEL repository is enabled on CentOS Stream. + + EPEL (Extra Packages for Enterprise Linux) is required for several + packages on CentOS. This function is idempotent — if EPEL is already + enabled it returns immediately. + """ + info: dict[str, Any] = { + "installed": False, + "already_enabled": False, + "failed": False, + } + + _status("Checking EPEL repository") + + # Quick check: is epel-release already installed and repo enabled? + repo_check = run(["dnf", "repolist", "--enabled"], timeout=60) + if "epel" in repo_check.stdout.lower(): + _status_done("already enabled") + info["already_enabled"] = True + return info + + if _rpm_installed("epel-release"): + _status_done("package installed but repo disabled") + else: + _status_done("not installed") + + if dry_run: + _info("Would enable CRB repository") + _info("Would install: epel-release") + info["installed"] = True + return info + + # Enable CodeReady Linux Builder (CRB) — some EPEL packages depend on it. + # This repo ships with CentOS but is disabled by default. Non-fatal if + # it's already enabled or not available. + _status("Enabling CRB repository") + crb_result = run(["dnf", "config-manager", "--set-enabled", "crb"], timeout=60) + if crb_result.returncode == 0: + _status_done() + else: + _status_done("not available (non-fatal)") + + _status("Installing epel-release") + if _dnf_install(["epel-release"]): + _status_done() + _ok("EPEL repository enabled") + info["installed"] = True + else: + _status_done("failed") + _fail("Failed to install epel-release") + info["failed"] = True + + return info + + # --------------------------------------------------------------------------- # 2. Base packages # --------------------------------------------------------------------------- @@ -1240,6 +1297,11 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace: action="store_true", help="Skip base package installation", ) + parser.add_argument( + "--skip-epel", + action="store_true", + help="skip EPEL repository setup on CentOS", + ) parser.add_argument( "--skip-firewall", action="store_true", @@ -1309,6 +1371,18 @@ def main() -> None: else: _info("System update: skipped (--skip-update)") + # 2. EPEL repository (CentOS Stream only) + if os_id == "centos": + print(f"\n{BOLD}── EPEL Repository ──{RESET}", file=sys.stderr) + if not args.skip_epel: + results["epel"] = ensure_epel(dry_run=args.dry_run) + if results["epel"].get("failed"): + warnings.append( + "EPEL repository setup failed — some packages may be unavailable" + ) + else: + _info("EPEL repository: skipped (--skip-epel)") + # 2. Base packages print(f"\n{BOLD}── Base Packages ──{RESET}", file=sys.stderr) if not args.skip_packages: