Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/release-notes/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ https://keepachangelog.com/en/1.1.0/
## Fixed

- **`4.0.0-B1` — `build --failed` no longer rebuilds a package you removed.** Replacing a failed `ethtool-git` with `pacman -S ethtool` left its failure recorded, so `build --failed` queued `ethtool-git` again. A removal cleared the failure only when sysforge read the pacman hook's record of that transaction, and that record can be gone by then. A failed package that sysforge tracks but that is no longer installed now counts as removed too, in `update`, `state failed`, `doctor --state` and `build --failed`; `update` checks it before pruning the entry. A package whose first build failed was never installed and is still retried. A failure left over from before this fix is cleared with `sysforge state failed --clear PKGBASE`.

---

- **`4.0.0-B2` — bare `sysforge doctor` runs the full sweep again, and `doctor system` reads your config.** The `--config` axis flag and the loaded config shared one attribute. Bare `sysforge doctor` took the loaded config for the flag and checked only the live config settings, so it printed one section and stopped. `sysforge doctor system` took the flag's unset value for a loaded config and ran every axis against an empty one, so the toolchain, cache, graphics and storage checks ignored your settings. The flag now has its own attribute; its spelling is unchanged.
9 changes: 5 additions & 4 deletions sysforge/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,7 @@ def _collect_integrity_findings(args) -> diag.AxisResult:
"gfxperf": "gfxperf",
"pacman": "pacman",
"state": "state",
"config": "config",
"config_axis": "config",
"boot": "boot",
"restart": "restart",
"storage": "storage",
Expand Down Expand Up @@ -1708,7 +1708,8 @@ def _add_system_axis_flags(p):
help="Check sysforge's own state integrity: recorded build failures, an "
"interrupted stage sentinel from a prior run, and build_state drift "
"vs the live pacman database. Read-only (does not recover).")
p.add_argument("--config", action="store_true",
# dest: args.config holds the loaded config (4.0.0-B2).
p.add_argument("--config", action="store_true", dest="config_axis",
help="Check the live config files for settings sysforge retired or does "
"not know (the same report as `sysforge config check`).")
p.add_argument("--boot", action="store_true",
Expand Down Expand Up @@ -1791,7 +1792,7 @@ def add_parser(cls, sub):

def pre_check(self, args) -> PreCheckResult:
# cmd_doctor_system reads args.config; the cli.py wrapper used to set it.
if not hasattr(args, "config") or args.config is None:
if getattr(args, "config", None) is None:
args.config = _load_config() or {}
return PreCheckResult()

Expand Down Expand Up @@ -1890,7 +1891,7 @@ def pre_check(self, args) -> PreCheckResult:
return PreCheckResult(blocker=(
"--json is a read-only report; it cannot be combined with "
"--apply, which rebuilds packages"), exit_code=2)
if not hasattr(args, "config") or args.config is None:
if getattr(args, "config", None) is None:
args.config = _load_config() or {}
# Split targets by provenance so the rust axis can collapse
# broad-selector targets into one survey finding (2.6.1-F1).
Expand Down
35 changes: 35 additions & 0 deletions tests/test_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2215,3 +2215,38 @@ def test_toolchain_axis_reports_non_graphics_consumer_symbols(monkeypatch):
assert not any("gallium" in m for m in messages)
assert "libllvm_consumers" in res.roster.ran



# ---------------------------------------------------------------------------
# The --config axis flag vs the loaded config (4.0.0-B2)
# ---------------------------------------------------------------------------
# Both lived on ``args.config``: bare `doctor` read the loaded dict as the flag
# and ran only the config axis, and `doctor system` read the flag's False as
# "already loaded" and ran every axis against an empty config.

_LIVE_CONFIG = {"defaults": {"toolchain": "llvm"}}


def _pre_checked(monkeypatch, *argv):
from cli_args import cli_args
monkeypatch.setattr(doctor, "_load_config", lambda: dict(_LIVE_CONFIG))
args = cli_args(*argv)
args.verb_cls().pre_check(args)
return args


@pytest.mark.parametrize("argv", [("doctor",), ("doctor", "system")])
def test_full_sweep_runs_every_default_axis_with_the_loaded_config(monkeypatch, argv):
args = _pre_checked(monkeypatch, *argv)

assert doctor._resolve_axis_names(args) == [
n for n in doctor._SYSTEM_AXIS_ORDER
if n not in doctor._OPT_IN_AXES and n != "abi"]
assert args.config == _LIVE_CONFIG


def test_config_flag_selects_only_the_config_axis(monkeypatch):
args = _pre_checked(monkeypatch, "doctor", "system", "--config")

assert doctor._resolve_axis_names(args) == ["config"]
assert args.config == _LIVE_CONFIG
Loading