Make lint report instead of rewriting, and stop verify guessing the domain
`make lint-frontend` was `biome check --write --unsafe ./` — a lint target that reformatted the whole tree rather than checking it, which is why every parallel change in this repo has had to work around it. `lint` checks now and a new `format` writes. The pre-commit hook and CI needed no edit at all: both call `bun run lint`, so they became checks the moment its meaning changed. `app/Makefile` assigned DOMAIN from .env, and a plain assignment beats an inherited environment variable and is not exported — so `cd app && make dev-local` served localhost while the same checkout's tests targeted the configured domain. `export DOMAIN ?=` gives the lattice that was intended: command line, then environment, then .env. Alongside: the backend's htmlcov bind mount created that directory as root, so `make test-backend` died on the coverage step after every test had passed, which reads like a test failure and is not one. The alerts screen's copy of ALERTING_EVENTS is now checked by a test rather than trusted. And the shard comment claimed two spec files where there are nine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uq8mtNb97A7praJLyeEYgs
This commit is contained in:
@@ -37,9 +37,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Two spec files (tests/flows.spec.ts, tests/admin.spec.ts) plus
|
||||
# tests/auth.setup.ts, which every shard reruns as its setup project.
|
||||
# More shards than that would only pay container startup twice over.
|
||||
# Nine spec files under tests/, plus tests/auth.setup.ts, which every
|
||||
# shard reruns as its setup project. Split two ways: each shard pays a
|
||||
# full compose stack's startup, so more of them would spend more time
|
||||
# booting than testing.
|
||||
shardIndex: [1, 2]
|
||||
shardTotal: [2]
|
||||
env:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
.PHONY: dev-utils dev dev-local up down update install dev-backend dev-frontend \
|
||||
generate-client seed-example seed-demo seed-house seed-aircon seed-hosted-demo test test-backend test-frontend soak bench-startup lint lint-backend \
|
||||
lint-frontend umami clean help
|
||||
lint-frontend format-frontend umami clean help
|
||||
|
||||
COMPOSE_ROOT := $(CURDIR)
|
||||
# Explicit project name keeps this stack isolated from the sibling website
|
||||
@@ -133,7 +133,7 @@ test-backend: ## Run backend tests (pytest + coverage)
|
||||
# deployment carries the deployment's domain, and *.fluksio.com resolves to the
|
||||
# live instance from here — which is why the run below maps both names onto the
|
||||
# local Traefik by address and never lets DNS decide.
|
||||
DOMAIN = $(shell sed -n 's/^DOMAIN=//p' $(COMPOSE_ROOT)/.env | head -1)
|
||||
export DOMAIN ?= $(shell sed -n 's/^DOMAIN=//p' $(COMPOSE_ROOT)/.env | head -1)
|
||||
PW_VERSION = $(shell sed -n 's/.*"@playwright\/test": "[^0-9]*\([0-9.]*\)".*/\1/p' frontend/package.json | head -1)
|
||||
|
||||
test-frontend: ## Run frontend tests (Playwright e2e) against the local stack
|
||||
@@ -174,6 +174,9 @@ lint-backend: ## Lint backend with ruff + mypy
|
||||
lint-frontend: ## Lint frontend with biome
|
||||
cd frontend && bun run lint
|
||||
|
||||
format-frontend: ## Apply biome's fixes to the frontend (what `lint-frontend` only reports)
|
||||
cd frontend && bun run format
|
||||
|
||||
# ── Cleanup ───────────────────────────────────────────────────────
|
||||
|
||||
clean: ## Remove build artifacts and caches
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
"""Alerting: failing loudly once, not thirty-six thousand times."""
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.flow.alerts import (
|
||||
ALERTING_EVENTS,
|
||||
FLAP_THRESHOLD,
|
||||
RATE_LIMIT,
|
||||
Alert,
|
||||
@@ -230,3 +233,17 @@ def test_a_dashboard_channel_without_a_message_says_so():
|
||||
asyncio.run(
|
||||
alerts.send(channel, Alert(title="t", body="b"), raise_on_error=True)
|
||||
)
|
||||
|
||||
|
||||
ALERTS_ROUTE = Path(__file__).parents[3] / "frontend/src/routes/_layout/alerts.tsx"
|
||||
|
||||
|
||||
@pytest.mark.skipif(not ALERTS_ROUTE.exists(), reason="no frontend in this checkout")
|
||||
def test_the_alerts_screen_offers_every_alerting_event():
|
||||
"""The chooser lists the events by hand, so it can drift out of this set.
|
||||
|
||||
Only the ids have to agree — the labels beside them are UI copy.
|
||||
"""
|
||||
block = re.search(r"const EVENTS[^=]*= \[(.*?)\n\]", ALERTS_ROUTE.read_text(), re.S)
|
||||
assert block, "the EVENTS list moved — this test needs following"
|
||||
assert set(re.findall(r'\["(\w+)"', block.group(1))) == ALERTING_EVENTS
|
||||
|
||||
+12
-9
@@ -14,9 +14,9 @@ SPA, the API and the marketing site answer on one port. `make status` lists the
|
||||
`make down` stops everything.
|
||||
|
||||
The hostname comes from `DOMAIN` in `app/.env`, which `scripts/setup.sh` copies out of the
|
||||
root `.env` — change it there and re-run `make init`. `make dev DOMAIN=…` at the root does
|
||||
*not* reach this stack: `app/Makefile` assigns `DOMAIN` from `app/.env`, and a makefile
|
||||
assignment overrides an inherited environment variable.
|
||||
root `.env` — change it there and re-run `make init`. `app/Makefile` takes that value as a
|
||||
default only (`export DOMAIN ?= …`), so `make dev DOMAIN=…` overrides it and the override
|
||||
reaches the recipes.
|
||||
|
||||
**App only.** `cd app && make dev` starts this stack on its own Traefik with all host ports
|
||||
published, in the foreground. `make dev-utils` starts just db, adminer, proxy, mailcatcher
|
||||
@@ -145,15 +145,18 @@ run when either origin resolves outside loopback or the private ranges, because
|
||||
creates and deletes flows, dashboards and users. `PLAYWRIGHT_ALLOW_PUBLIC=1` overrides it.
|
||||
|
||||
**Visual check.** From the root, `make verify` logs in and screenshots both themes into
|
||||
`app/frontend/screenshots/{light,dark}/`; `make verify-docker` does the same from the
|
||||
Playwright container when the host lacks the browser libraries. Both default to `localhost`
|
||||
rather than reading `.env`, so on a checkout configured for a deployment name the domain the
|
||||
stack actually serves: `make verify DOMAIN=example.com`.
|
||||
`app/frontend/screenshots/{light,dark}/`. It is an alias for `make verify-docker`: the run
|
||||
always happens in the Playwright container, which pins the browser by reading
|
||||
`frontend/package.json`. Running Playwright on the host is unsupported — the workspace pins a
|
||||
browser revision whose install is incomplete. The domain comes from `.env`, and
|
||||
`make verify DOMAIN=example.com` still wins.
|
||||
|
||||
## Lint, hooks and the generated client
|
||||
|
||||
`make lint` runs ruff, mypy and biome. `make hooks` at the root installs the pre-commit
|
||||
hooks in both stacks; `.pre-commit-config.yaml` holds them. One is worth knowing about:
|
||||
`make lint` runs ruff, mypy and biome, and only reports — it is what the pre-commit hook and
|
||||
CI run, so neither rewrites the tree. `make format-frontend` is the writing half, applying
|
||||
biome's fixes. `make hooks` at the root installs the pre-commit hooks in both stacks;
|
||||
`.pre-commit-config.yaml` holds them. One is worth knowing about:
|
||||
`generate-frontend-sdk` regenerates the SPA's API client from the backend's OpenAPI schema
|
||||
whenever anything under `backend/` changes, so a route change reaches the frontend without
|
||||
being asked. `make generate-client` forces it.
|
||||
|
||||
@@ -105,7 +105,6 @@ services:
|
||||
- path: ../backend/pyproject.toml
|
||||
action: rebuild
|
||||
volumes:
|
||||
- ../backend/htmlcov:/app/backend/htmlcov
|
||||
# What makes --reload above mean anything in the detached flow (`make dev`,
|
||||
# `make dev-local`): it starts the stack with `up -d`, which never runs the
|
||||
# sync above — only `docker compose watch` does. Without the mount the
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -p tsconfig.build.json && vite build",
|
||||
"lint": "biome check --write --unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
|
||||
"lint": "biome check --no-errors-on-unmatched --files-ignore-unknown=true ./",
|
||||
"format": "biome check --write --unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
|
||||
"preview": "vite preview",
|
||||
"generate-client": "openapi-ts",
|
||||
"test": "bunx playwright test",
|
||||
|
||||
@@ -35,9 +35,10 @@ const alertsKey = ["alerts", "config"]
|
||||
const SECTION =
|
||||
"text-xs font-medium uppercase tracking-[0.5px] text-muted-foreground"
|
||||
|
||||
/** What the engine can alert on. Mirrors `ALERTING_EVENTS` in the backend; a
|
||||
* rule with none of them ticked still covers everything, including any event
|
||||
* added there later. */
|
||||
/** What the engine can alert on. Mirrors `ALERTING_EVENTS` in the backend, and
|
||||
* `backend/tests/flow/test_alerts.py` reads this list to fail when the two
|
||||
* drift apart. A rule with none of them ticked still covers everything,
|
||||
* including any event added there later. */
|
||||
const EVENTS: [string, string][] = [
|
||||
["node_error", "A node failed"],
|
||||
["node_health", "A connection dropped"],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"scripts": {
|
||||
"dev": "bun run --filter frontend dev",
|
||||
"lint": "bun run --filter frontend lint",
|
||||
"format": "bun run --filter frontend format",
|
||||
"test": "bun run --filter frontend test",
|
||||
"test:ui": "bun run --filter frontend test:ui"
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@ uv run python -c "import app.main; import json; print(json.dumps(app.main.app.op
|
||||
cd ..
|
||||
mv openapi.json frontend/
|
||||
bun run --filter frontend generate-client
|
||||
bun run lint
|
||||
bun run format
|
||||
|
||||
Reference in New Issue
Block a user