name: CI on: push: branches: [main] pull_request: branches: [main] permissions: contents: read jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v5 - name: Set up Python run: uv python install 3.14 - name: Install dependencies run: | uv venv .venv uv pip install --python .venv/bin/python -e '.[dev]' # Mirrors the no-noqa pre-commit hook. A suppression must not be able # to reach main just because someone committed without hooks installed. - name: block suppressions run: | if grep -rnEi '#\s*(noqa|type:\s*ignore|ruff:\s*noqa)' \ --include='*.py' signal_bot fake_signal tests; then echo '::error::suppressions are not allowed; fix the finding or ask first' exit 1 fi # The name half of this check needs the operator's private data and so # can only run locally; the shape half -- phone numbers, uuids, Signal # group ids -- works anywhere and is what CI enforces on every push. - name: no personal data run: ./scripts/check_personal_data.sh --all - name: ruff format run: .venv/bin/ruff format --check . - name: ruff check (ALL rules) run: .venv/bin/ruff check . - name: mypy --strict run: .venv/bin/mypy signal_bot fake_signal - name: shellcheck # --severity=style is the floor CI enforces; the local pre-commit hook # uses the same flag so a version skew cannot pass locally and fail here. run: shellcheck -x --severity=style run.sh scripts/*.sh - name: pytest (100% branch coverage) run: .venv/bin/python -m pytest -q # The real bot against the fake Signal server. No account, no network. - name: end-to-end run: ./scripts/e2e.sh # Boot order reversed: the bot starts first and Signal appears later, # which is what a reboot looks like and what put the unit in `failed` # for eight hours on 2026-08-30. - name: cold start run: ./scripts/cold_start.sh