Compare commits

..

3 Commits

Author SHA1 Message Date
didericis c6c507679c test: tune coverage exclusions
lint / lint (push) Failing after 1m47s
test / unit (pull_request) Successful in 41s
test / integration (pull_request) Successful in 18s
2026-06-25 05:10:13 -04:00
didericis a8f69ef6d5 ci: add coverage.py reporting 2026-06-25 05:10:13 -04:00
Quality Badge Bot ca1b4afaea chore: update quality badges
- Pylint: 9.93/10
- Pyright: 1 errors

[skip ci]
2026-06-25 09:06:44 +00:00
5 changed files with 12 additions and 34 deletions
+2
View File
@@ -4,4 +4,6 @@ source = .
[report] [report]
omit = omit =
bot_bottle/egress_addon.py
bot_bottle/cli/tui.py
tests/* tests/*
+1 -7
View File
@@ -68,11 +68,5 @@ jobs:
echo "docker not on PATH — integration tests will skip" echo "docker not on PATH — integration tests will skip"
fi fi
- name: Install dev requirements
run: python3 -m pip install -r requirements-dev.txt
- name: Run integration tests - name: Run integration tests
run: python3 -m coverage run -m unittest discover -t . -s tests/integration -v run: python3 -m unittest discover -t . -s tests/integration -v
- name: Report integration coverage
run: python3 -m coverage report -m
+2 -19
View File
@@ -7,11 +7,7 @@ on:
paths: paths:
- '**.py' - '**.py'
- '.pylintrc' - '.pylintrc'
- '.coveragerc'
- '.gitea/workflows/update-badges.yml'
- 'pyrightconfig.json' - 'pyrightconfig.json'
- 'requirements-dev.txt'
- 'tests/**'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -41,15 +37,6 @@ jobs:
echo "score=$SCORE" >> $GITHUB_OUTPUT echo "score=$SCORE" >> $GITHUB_OUTPUT
echo "Pylint score: $SCORE" echo "Pylint score: $SCORE"
- name: Run tests and extract coverage
id: coverage
run: |
python -m coverage run -m unittest discover -t . -s tests/unit -v
python -m coverage run -a -m unittest discover -t . -s tests/integration -v
TOTAL=$(python -m coverage report --format=total)
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "Coverage total: $TOTAL"
- name: Run pyright and check errors - name: Run pyright and check errors
id: pyright id: pyright
run: | run: |
@@ -62,13 +49,9 @@ jobs:
run: | run: |
PYLINT_SCORE="${{ steps.pylint.outputs.score }}" PYLINT_SCORE="${{ steps.pylint.outputs.score }}"
PYRIGHT_ERRORS="${{ steps.pyright.outputs.errors }}" PYRIGHT_ERRORS="${{ steps.pyright.outputs.errors }}"
COVERAGE_TOTAL="${{ steps.coverage.outputs.total }}"
PYLINT_SCORE_ENCODED=$(echo "$PYLINT_SCORE" | sed 's|/|%2F|g') PYLINT_SCORE_ENCODED=$(echo "$PYLINT_SCORE" | sed 's|/|%2F|g')
if [ -n "$COVERAGE_TOTAL" ]; then
sed -i "s|/badge/coverage-[^)]*|/badge/coverage-${COVERAGE_TOTAL}%25-brightgreen|" README.md
fi
if [ -n "$PYLINT_SCORE_ENCODED" ]; then if [ -n "$PYLINT_SCORE_ENCODED" ]; then
sed -i "s|/badge/pylint-[^)]*|/badge/pylint-${PYLINT_SCORE_ENCODED}-brightgreen|" README.md sed -i "s|/badge/pylint-[^)]*|/badge/pylint-${PYLINT_SCORE_ENCODED}-brightgreen|" README.md
fi fi
@@ -77,7 +60,7 @@ jobs:
fi fi
echo "Updated badges:" echo "Updated badges:"
grep -E "coverage|pylint|pyright" README.md | head -3 grep -E "pylint|pyright" README.md | head -2
- name: Commit and push badge updates - name: Commit and push badge updates
run: | run: |
@@ -90,7 +73,7 @@ jobs:
else else
echo "Badge changes detected, committing..." echo "Badge changes detected, committing..."
git add README.md git add README.md
MSG="chore: update quality badges"$'\n\n'"- Coverage: ${{ steps.coverage.outputs.total }}"$'\n'"- Pylint: ${{ steps.pylint.outputs.score }}"$'\n'"- Pyright: ${{ steps.pyright.outputs.errors }} errors"$'\n\n'"[skip ci]" MSG="chore: update quality badges"$'\n\n'"- Pylint: ${{ steps.pylint.outputs.score }}"$'\n'"- Pyright: ${{ steps.pyright.outputs.errors }} errors"$'\n\n'"[skip ci]"
git commit -m "$MSG" git commit -m "$MSG"
git push git push
fi fi
+1 -2
View File
@@ -5,9 +5,8 @@
# bot-bottle # bot-bottle
[![test](https://gitea.dideric.is/didericis/bot-bottle/actions/workflows/test.yml/badge.svg?branch=main)](https://gitea.dideric.is/didericis/bot-bottle/actions?workflow=test.yml) [![test](https://gitea.dideric.is/didericis/bot-bottle/actions/workflows/test.yml/badge.svg?branch=main)](https://gitea.dideric.is/didericis/bot-bottle/actions?workflow=test.yml)
[![coverage](https://img.shields.io/badge/coverage-0%25-brightgreen)](https://coverage.readthedocs.io/)
[![pylint](https://img.shields.io/badge/pylint-9.93%2F10-brightgreen)](https://github.com/PyCQA/pylint) [![pylint](https://img.shields.io/badge/pylint-9.93%2F10-brightgreen)](https://github.com/PyCQA/pylint)
[![pyright](https://img.shields.io/badge/pyright-0%20errors-brightgreen)](https://github.com/microsoft/pyright) [![pyright](https://img.shields.io/badge/pyright-1%20errors-brightgreen)](https://github.com/microsoft/pyright)
**Problem:** Developer wants to run a coding agent without supervision, but they don't want a prompt injected or misbehaving agent wrecking their environment or exfiltrating sensitive data. **Problem:** Developer wants to run a coding agent without supervision, but they don't want a prompt injected or misbehaving agent wrecking their environment or exfiltrating sensitive data.
+6 -6
View File
@@ -320,7 +320,7 @@ def _list_once() -> int:
return 0 return 0
def _try_init_green() -> int: def _try_init_green() -> int: # pragma: no cover
"""Initialise a green color pair and return its attr, or 0.""" """Initialise a green color pair and return its attr, or 0."""
try: try:
curses.start_color() curses.start_color()
@@ -331,7 +331,7 @@ def _try_init_green() -> int:
return 0 return 0
def _main_loop(stdscr: "curses._CursesWindow") -> None: # type: ignore def _main_loop(stdscr: "curses._CursesWindow") -> None: # type: ignore # pragma: no cover
curses.curs_set(0) curses.curs_set(0)
stdscr.timeout(_REFRESH_INTERVAL_MS) stdscr.timeout(_REFRESH_INTERVAL_MS)
green_attr = _try_init_green() green_attr = _try_init_green()
@@ -421,7 +421,7 @@ def _render(
status_line: str, status_line: str,
*, *,
green_attr: int = 0, # noqa: F841 — unused, but required by interface green_attr: int = 0, # noqa: F841 — unused, but required by interface
) -> None: ) -> None: # pragma: no cover
stdscr.erase() stdscr.erase()
h, w = stdscr.getmaxyx() h, w = stdscr.getmaxyx()
header = f"bot-bottle supervise ({len(pending)} pending)" header = f"bot-bottle supervise ({len(pending)} pending)"
@@ -472,7 +472,7 @@ def _detail_view(
qp: QueuedProposal, qp: QueuedProposal,
*, *,
green_attr: int = 0, green_attr: int = 0,
) -> None: ) -> None: # pragma: no cover
"""Render the full proposal. Scrollable. Press q to return.""" """Render the full proposal. Scrollable. Press q to return."""
lines = _detail_lines(qp, green_attr=green_attr) lines = _detail_lines(qp, green_attr=green_attr)
offset = 0 offset = 0
@@ -524,7 +524,7 @@ def _detail_view(
return return
def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None: # type: ignore def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None: # type: ignore # pragma: no cover
"""Suspend curses, open $EDITOR on the proposed file, return edited content.""" """Suspend curses, open $EDITOR on the proposed file, return edited content."""
suffix = _suffix_for_tool(qp.proposal.tool) suffix = _suffix_for_tool(qp.proposal.tool)
curses.endwin() curses.endwin()
@@ -535,7 +535,7 @@ def _modify(stdscr: "curses._CursesWindow", qp: QueuedProposal) -> str | None:
return edited return edited
def _prompt(stdscr: "curses._CursesWindow", label: str) -> str: # type: ignore def _prompt(stdscr: "curses._CursesWindow", label: str) -> str: # type: ignore # pragma: no cover
"""One-line input at the bottom of the screen.""" """One-line input at the bottom of the screen."""
curses.curs_set(1) curses.curs_set(1)
h, _ = stdscr.getmaxyx() h, _ = stdscr.getmaxyx()