mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-10 18:07:29 +01:00
./web.sh serves health, the configuration and the photo album on 127.0.0.1:8140 -- the port tool/tagger.py vacates. React 19, Vite 8, TypeScript 6.0.3, ESLint 10 with strictTypeChecked + stylisticTypeChecked + unicorn + sonarjs + perfectionist, Vitest 4 at 100% on all four metrics. No coverage exclude list, including for main.tsx. This repository treats a coverage omit as a suppression, and "the DOM bootstrap is awkward to assert" is the same sentence as "this code is untested" -- so the bootstrap is a shim thin enough to test in jsdom, and the album view holds its file list apart from the album so the update path stopped carrying a "what if it is null" branch that cannot happen. An untestable branch is an untested one. TypeScript stays at 6.0.3: typescript-eslint 8.68.0 declares typescript >=4.8.4 <6.1.0, and the fleet-wide allowlist entry says the compiler waits for the linter. The freshness gate then caught two pins the reference repo was carrying stale -- eslint-plugin-unicorn 73 -> 74 and a floating node-version: "24" in CI -- both bumped rather than allowlisted. Traps the gates found, all of them real: - .gitignore has a bare lib/, which matches at any depth, so web/src/lib/ would have been silently untracked. The source lives in src/api/. - CI's shellcheck glob was `run.sh scripts/*.sh`, so tag.sh was unlinted in CI for its whole life and web.sh would have inherited that. Now ./*.sh. - tsconfig.json is strict JSON, not JSONC: check-json is generic and tsconfig being commentable is a convention, not a requirement. - The personal-data gate refused an invented phone number in a test, which is exactly what it is for. The album's warning that names a command moved with the command, and every path that pointed at tool/tagger.py now points at ./web.sh. The historical mentions stay: they explain why the code is shaped as it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01416TegJ3sa8xL5qGGgrygP
92 lines
2.8 KiB
YAML
92 lines
2.8 KiB
YAML
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 ./*.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
|
|
|
|
# The management page. Its own job rather than more steps on `check`: it
|
|
# needs a Node toolchain that the Python job has no use for, and the two
|
|
# halves fail for unrelated reasons.
|
|
web:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: web
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "24.20.0"
|
|
- run: corepack enable
|
|
- name: install (exact pins, from the lockfile)
|
|
run: pnpm install --frozen-lockfile
|
|
- name: tsc --noEmit and eslint
|
|
run: pnpm lint
|
|
- name: vitest (100% coverage, no excludes)
|
|
run: pnpm coverage
|