From 30e4f8b61ec6971854b86dba724d4515f92a5297 Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 22:32:56 +0200 Subject: [PATCH 1/6] Add .scrutinizer.yml to fix the coverage step Scrutinizer's stored build config predates the uv migration: it installs a stale, hardcoded dependency list via raw pip (coverage feedparser beautifulsoup4 chardet requests discord, none of which match pyproject.toml) and runs `unittest discover -b` without `-s tests`, so it finds zero tests and the coverage step exits with code 5. Version-controlling the build config with the project's actual tooling (uv sync + pytest --cov) keeps it in sync with pyproject.toml going forward. Related to #88. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .gitignore | 1 + .scrutinizer.yml | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 .scrutinizer.yml diff --git a/.gitignore b/.gitignore index aae5829..dc01013 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,6 @@ build/ __pycache__/ data/marvinMorning_date.txt .coverage +coverage.xml dist htmlcov/ diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..18357ae --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,15 @@ +build: + environment: + python: '3.12' + + dependencies: + before: + - curl -LsSf https://astral.sh/uv/install.sh | sh + - uv sync + + tests: + override: + - command: uv run pytest --cov=irc2phpbb --cov-report=xml + coverage: + file: coverage.xml + format: py-cc From 693219ebdab831a59a62c84866de93650070e7a0 Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 22:42:25 +0200 Subject: [PATCH 2/6] Install uv via pip instead of the astral.sh curl script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The curl|sh installer failed with exit code 127 in Scrutinizer's build environment (curl isn't present in its Python runtime image), before uv sync ever got a chance to run. pip is guaranteed to exist since it's part of the Python runtime Scrutinizer just built, so install uv through it instead — also avoids depending on network access to astral.sh. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .scrutinizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 18357ae..169ffd1 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -4,7 +4,7 @@ build: dependencies: before: - - curl -LsSf https://astral.sh/uv/install.sh | sh + - pip install uv - uv sync tests: From 14e50bde8b4bc91b0f42b19aacac570f110a6415 Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 22:52:44 +0200 Subject: [PATCH 3/6] Fix Python-version-dependent test and expose coverage CLI to Scrutinizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test_main.py: argparse's error message for an invalid choice quotes the choices list differently between Python patch releases ("'irc', 'discord'" on 3.12.3, "irc, discord" on 3.12.14) — normalize that before comparing so the test doesn't depend on the exact patch version. - .scrutinizer.yml: Scrutinizer's own coverage-report post-processing shells out to a bare `coverage` command, which only existed inside the uv-managed .venv ("bash: coverage: command not found"). Install it globally via pip alongside uv so it's on PATH. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .scrutinizer.yml | 2 +- tests/test_main.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 169ffd1..64848e1 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -4,7 +4,7 @@ build: dependencies: before: - - pip install uv + - pip install uv coverage - uv sync tests: diff --git a/tests/test_main.py b/tests/test_main.py index 82dc62f..c94a299 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -199,12 +199,15 @@ def testUnhandledArgument(self): with self.assertRaises(SystemExit) as e: s = io.StringIO() expectedError = (f"{self.USAGE}main.py: error: argument protocol: " - "invalid choice: 'arg' (choose from 'irc', 'discord')\n") + "invalid choice: 'arg' (choose from irc, discord)\n") with contextlib.redirect_stderr(s): sys.argv = ["./main.py", "arg"] parseOptions(ConfigParseTest.SAMPLE_CONFIG) self.assertEqual(e.exception.code, 2) - self.assertEqual(s.getvalue(), expectedError) + # argparse's quoting of the choices list in this message varies between Python + # patch releases ("'irc', 'discord'" vs "irc, discord"); normalize it away. + actualError = s.getvalue().replace("'irc'", "irc").replace("'discord'", "discord") + self.assertEqual(actualError, expectedError) class TestArgumentParsing(TestCase): """Test parsing argument to determine whether to launch as irc or discord bot """ From 8a4374912713824793e4732d8efb9da2776c13ff Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 22:56:04 +0200 Subject: [PATCH 4/6] Point Scrutinizer at the raw .coverage data file, not coverage.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit py-cc is coverage.py's own SQLite data file format, not the Cobertura-style XML report — Scrutinizer's coverage step was trying to open coverage.xml as that database and failing ("file is not a database"). Drop --cov-report=xml (pytest-cov already writes .coverage by default) and point the coverage config at .coverage instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .scrutinizer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 64848e1..6baf2a0 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -9,7 +9,7 @@ build: tests: override: - - command: uv run pytest --cov=irc2phpbb --cov-report=xml + - command: uv run pytest --cov=irc2phpbb coverage: - file: coverage.xml + file: .coverage format: py-cc From 13e407d9cae67f6a004bb677adc32a1be492a2ed Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 23:03:15 +0200 Subject: [PATCH 5/6] Override the separate Scrutinizer "coverage" build node too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scrutinizer runs coverage collection as its own build node, independent of the "build" node — confirmed via the inspection's normalized Config view, which showed "coverage" still running its legacy stored command (python3 -m pip install coverage feedparser beautifulsoup4 chardet requests discord; coverage run --source=. -m unittest discover -b) even after .scrutinizer.yml's "build" section was correctly picked up by the "analysis"/"tests" nodes. Add an explicit top-level "coverage" section (reusing the same dependencies/test command via YAML anchors) so this node uses the project's real tooling too. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .scrutinizer.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 6baf2a0..f1538f9 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -3,13 +3,23 @@ build: python: '3.12' dependencies: - before: + before: &dependencies - pip install uv coverage - uv sync tests: - override: + override: &tests_override - command: uv run pytest --cov=irc2phpbb coverage: file: .coverage format: py-cc + +# Scrutinizer runs "coverage" as its own separate build node, independent of the +# "build" node above. Without an explicit override here it falls back to its +# legacy stored config, which predates the uv migration. +coverage: + dependencies: + before: *dependencies + + tests: + override: *tests_override From 1ab5a2fffebbd8afee286212a747a97753c0673e Mon Sep 17 00:00:00 2001 From: Mikael Roos Date: Sun, 23 Aug 2026 23:07:37 +0200 Subject: [PATCH 6/6] Use build.nodes explicitly, matching Scrutinizer's documented schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bare top-level "coverage:" key isn't valid config (confirmed by Scrutinizer's own "Unrecognized option" error) — nodes are only configurable nested under build.nodes., per https://scrutinizer-ci.com/docs/guides/python/continuous-integration-deployment. The previous bare "build:" (without "nodes") happened to reach the "analysis" and "tests" nodes via some backward-compat shorthand, but never touched the separate legacy "coverage" node. Explicitly define all three of this repo's existing nodes (analysis, tests, coverage) so none of them fall back to the old stored web-UI config. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ax5CKcNH5hwbYNJoRGLyAC --- .scrutinizer.yml | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index f1538f9..8a5bac2 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -2,24 +2,25 @@ build: environment: python: '3.12' - dependencies: - before: &dependencies - - pip install uv coverage - - uv sync + nodes: + analysis: + dependencies: + before: &dependencies + - pip install uv coverage + - uv sync - tests: - override: &tests_override - - command: uv run pytest --cov=irc2phpbb - coverage: - file: .coverage - format: py-cc + tests: + dependencies: + before: *dependencies + tests: + override: &tests_override + - command: uv run pytest --cov=irc2phpbb + coverage: + file: .coverage + format: py-cc -# Scrutinizer runs "coverage" as its own separate build node, independent of the -# "build" node above. Without an explicit override here it falls back to its -# legacy stored config, which predates the uv migration. -coverage: - dependencies: - before: *dependencies - - tests: - override: *tests_override + coverage: + dependencies: + before: *dependencies + tests: + override: *tests_override