peterbe: Can you rebase this so you get the latest circleci stuff?
diff --git a/main.py b/main.py
index 37605f8..b9f4811 100644
--- a/main.py
+++ b/main.py
@@ -184,18 +184,19 @@ def run(verbose=False, dry_run=False):
                 good += row[status]
             elif not is_neutral(status):
                 bad += row[status]
-        if not (good + bad):
+        total = good + bad
+        if not total:
             log(f"Skipping {source!r} because exactly 0 good+bad statuses")
             continue
 
-        if good + bad < MIN_TOTAL_ENTRIES:
+        if total < MIN_TOTAL_ENTRIES:
             log(
                 f"Skipping {source!r} because exactly too few good+bad statuses "
-                f"({good + bad} < {MIN_TOTAL_ENTRIES})"
+                f"({total} < {MIN_TOTAL_ENTRIES})"
             )
             continue
 
-        percent = 100 * bad / (good + bad)
+        percent = 100 * bad / total
         stats = f"(good:{good:>10,} bad:{bad:>10,})"
         is_bad = percent > error_threshold_percent
         click.secho(
@@ -207,7 +208,7 @@ def run(verbose=False, dry_run=False):
                 for s, v in row.items()
                 if s not in GOOD_STATUSES + NEUTRAL_STATUSES
             ]
-            bad_rows.append((source, bad_statuses))
+            bad_rows.append((source, total, bad_statuses))
 
     return bad_rows
 
@@ -221,10 +222,16 @@ def cli(dry_run, verbose):
         click.secho(
             f"\n{len(bads)} settings have a bad ratio over threshold.", fg="red"
         )
-        for source, statuses in bads:
+        for source, total, statuses in bads:
             statuses_desc = sorted(statuses, key=lambda e: e[1], reverse=True)
-            stats = " ".join([f"{s}:{v:,}" for s, v in statuses_desc if v > 0])
-            click.secho(f"{source:40} ({stats})")
+            stats = "\n".join(
+                [
+                    f"\t{s:<16} {v/total*100:.2f}% ({v:,})"
+                    for s, v in statuses_desc
+                    if v > 0
+                ]
+            )
+            click.secho(f"\n{source}\n{stats}")
 
         raise click.Abort
 

Show percentage for distribution of errors
In the current distribution of errors, only the number of occurrences is shown.
I'd like to be able to also see «*that for collection A we have X% of network-errors*» 
diff --git a/test_main.py b/test_main.py
index ae71878..15e6406 100644
--- a/test_main.py
+++ b/test_main.py
@@ -66,6 +66,7 @@ def test_run_problems(capsys):
                         "up_to_date": 234_567,
                         "network_error": 12340,
                         "sync_error": 123,
+                        "pref_disabled": 1_000_000,  # remember, neutral and ignored
                     }
                 ]
             }
@@ -77,7 +78,8 @@ def test_run_problems(capsys):
     assert bad_rows
     bad_row, = bad_rows
     assert bad_row[0] == "foo/bar"
-    bad_keys = [x[0] for x in bad_row[1]]
+    assert bad_row[1] == 123_456 + 234_567 + 12340 + 123
+    bad_keys = [x[0] for x in bad_row[2]]
     assert bad_keys == ["network_error", "sync_error"]
 
     captured = capsys.readouterr()

pip install -e .[dev,test]
parse_log_pytest
pytest
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==25.3.0
backports.tarfile==1.2.0
black==25.1.0
cachetools==5.5.2
cattrs==24.1.3
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
colorama==0.4.6
cryptography==44.0.2
distlib==0.3.9
docutils==0.21.2
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
filelock==3.18.0
flake8==7.2.0
id==1.5.0
idna==3.10
importlib_metadata==8.6.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
keyring==25.6.0
markdown-it-py==3.0.0
mccabe==0.7.0
mdurl==0.1.2
more-itertools==10.6.0
mypy-extensions==1.0.0
nh3==0.2.21
packaging @ file:///croot/packaging_1734472117206/work
pathspec==0.12.1
platformdirs==4.3.7
pluggy @ file:///croot/pluggy_1733169602837/work
pycodestyle==2.13.0
pycparser==2.22
pyflakes==3.3.1
Pygments==2.19.1
pyproject-api==1.9.0
pytest @ file:///croot/pytest_1738938843180/work
pytest-env==1.1.5
python-decouple==3.8
PyYAML==6.0.2
readme_renderer==44.0
-e git+https://github.com/mozilla/remote-settings-uptake-health.git@3e0d9035968f7f42a479e85a7cb3fe299da04e42#egg=remote_settings_uptake_health
requests==2.32.3
requests-cache==1.2.1
requests-toolbelt==1.0.0
responses==0.25.7
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
therapist==2.2.0
tomli==2.2.1
tox==4.25.0
twine==6.1.0
typing_extensions==4.13.0
url-normalize==2.2.0
urllib3==2.3.0
virtualenv==20.29.3
zipp==3.21.0

name: remote-settings-uptake-health
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==25.3.0
      - backports-tarfile==1.2.0
      - black==25.1.0
      - cachetools==5.5.2
      - cattrs==24.1.3
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - colorama==0.4.6
      - cryptography==44.0.2
      - distlib==0.3.9
      - docutils==0.21.2
      - filelock==3.18.0
      - flake8==7.2.0
      - id==1.5.0
      - idna==3.10
      - importlib-metadata==8.6.1
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - mypy-extensions==1.0.0
      - nh3==0.2.21
      - pathspec==0.12.1
      - platformdirs==4.3.7
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pyproject-api==1.9.0
      - pytest-env==1.1.5
      - python-decouple==3.8
      - pyyaml==6.0.2
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-cache==1.2.1
      - requests-toolbelt==1.0.0
      - responses==0.25.7
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - therapist==2.2.0
      - tomli==2.2.1
      - tox==4.25.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - url-normalize==2.2.0
      - urllib3==2.3.0
      - virtualenv==20.29.3
      - zipp==3.21.0
prefix: /opt/conda/envs/remote-settings-uptake-health

test_main.py::test_run_problems
test_main.py::test_downloader test_main.py::test_run_no_problems
main.py
main.py