-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
296 lines (242 loc) · 14.1 KB
/
Copy pathMakefile
File metadata and controls
296 lines (242 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
.PHONY: dev hooks test lint typecheck format clean build up update restart down logs try try-down setup docs-dev docs-build docs-check-llms docs-lint docs-format validate render-demo render-long-demos render-long-demo render-pinned-demos plan-gif render-emoji-previews derive-phoenix derive-pride derive-heart-tunnel setup-demo-fonts panel-test panel-test-docker panel-map-reveal panel-map-verify panel-map-derive panel-map-reveal-docker panel-map-verify-docker panel-map-derive-docker
# --- Developer Setup ---
dev: ## Install package with dev dependencies and pre-commit hooks
@command -v uv >/dev/null 2>&1 || { \
echo "uv is required for development but was not found."; \
echo "Install it: curl -LsSf https://astral.sh/uv/install.sh | sh"; \
echo "(or 'brew install uv' — see https://docs.astral.sh/uv/getting-started/installation/)"; \
exit 1; }
uv sync --extra dev
uv run pre-commit install
uv run pre-commit install --hook-type pre-push
hooks: ## Install pre-commit hooks
uv run pre-commit install
uv run pre-commit install --hook-type pre-push
# --- Testing ---
test: ## Run pytest with coverage (no Docker needed)
uv run pytest -s --cov=src/ --cov-report=term-missing
# --- Quality ---
lint: ## Run ruff linter
uv run ruff check src/ tests/ tools/
typecheck: ## Run pyright type checker
uv run pyright src/
format: ## Run ruff formatter
uv run ruff format src/ tests/ tools/
# --- Config validation ---
# Validate a config TOML against the loader (no hardware, no display).
# CONFIG defaults to config/config.toml; override for example/test configs.
CONFIG ?= config/config.toml
validate: ## Validate a config TOML. Usage: make validate [CONFIG=path/to.toml]
uv run led-ticker validate $(CONFIG)
# --- Panel diagnostics ---
# Cycle full panel through R/G/B/White/Black for hardware-layer diagnostics.
# Use this when widgets render wrong but you don't know if it's a config or
# wiring/driver issue. Reuses [display] from the given config TOML.
HOLD ?= 2
LAYOUT ?=
panel-test: ## Cycle full panel through R/G/B/W/B. Usage: make panel-test [CONFIG=config/config.toml] [HOLD=2]
uv run python scripts/panel_color_test.py \
--config $(CONFIG) \
--hold $(HOLD)
# Run the panel-test inside the production Docker image — this is what you'll
# run on longboi/bigsign/smallsign over SSH. Requires `make build` to
# have run at least once.
#
# IMPORTANT: stop the running ticker first or the diagnostic will fight it for
# the matrix:
# make down
# make panel-test-docker
# make up
#
# --privileged + --network host match compose.yaml so behavior is identical to
# prod. -it gives the script a TTY so Ctrl-C reaches Python and the black-
# frame cleanup runs. -v scripts:ro means script edits don't require rebuilding
# the image.
panel-test-docker: ## Cycle R/G/B/W/B inside Docker. Stop the running ticker first.
docker run --rm -it --privileged --network host \
-v $(PWD)/config:/code/config:ro \
-v $(PWD)/scripts:/code/scripts:ro \
led-ticker \
python /code/scripts/panel_color_test.py \
--config /code/$(CONFIG) \
--hold $(HOLD)
# --- Panel mapping helpers ---
#
# Four-step workflow to build a pixel_mapper_config Remap string. On a deployed
# sign use the -docker targets (run inside the production image — needs
# `make build` once). The bare targets are for a dev host with the repo
# + `uv` installed.
# 1. make panel-map-reveal-docker — photograph the lit numbered pattern
# 2. Transcribe the grid into a file (e.g. LAYOUT=/tmp/grid.txt)
# 3. make panel-map-derive-docker — prints the Remap string; paste into config
# 4. make panel-map-verify-docker — paint a self-diagnosing pattern with it
#
# Stop the running ticker first (`docker compose stop`) so the diagnostic isn't
# fighting it for the matrix. CONFIG and HOLD are shared with panel-test above.
panel-map-reveal: ## Reveal physical panel layout (no mapper). Usage: make panel-map-reveal [CONFIG=...] [HOLD=2]
uv run python scripts/panel_map.py reveal --config $(CONFIG) --hold $(HOLD)
panel-map-verify: ## Verify a candidate mapper. Usage: make panel-map-verify [CONFIG=...] [HOLD=2] [MAPPER="Remap:..."]
uv run python scripts/panel_map.py verify --config $(CONFIG) --hold $(HOLD) $(if $(MAPPER),--mapper '$(MAPPER)')
panel-map-derive: ## Derive a Remap string from a transcribed grid. Usage: make panel-map-derive CONFIG=... LAYOUT=/tmp/grid.txt
uv run python scripts/panel_map.py derive --config $(CONFIG) $(if $(LAYOUT),--layout $(LAYOUT))
panel-map-reveal-docker: ## Reveal layout inside Docker. Stop the running ticker first.
docker run --rm -it --privileged --network host \
-v $(PWD)/config:/code/config:ro \
-v $(PWD)/scripts:/code/scripts:ro \
led-ticker \
python /code/scripts/panel_map.py reveal \
--config /code/$(CONFIG) \
--hold $(HOLD)
panel-map-verify-docker: ## Verify mapper inside Docker. Stop the running ticker first. Usage: make panel-map-verify-docker [CONFIG=...] [HOLD=2] [MAPPER="Remap:..."]
docker run --rm -it --privileged --network host \
-v $(PWD)/config:/code/config:ro \
-v $(PWD)/scripts:/code/scripts:ro \
led-ticker \
python /code/scripts/panel_map.py verify \
--config /code/$(CONFIG) \
--hold $(HOLD) $(if $(MAPPER),--mapper '$(MAPPER)')
# derive needs no hardware (pure compute) — no --privileged/--network. The host
# LAYOUT file is piped into the container's stdin, which derive reads.
panel-map-derive-docker: ## Derive a Remap string inside Docker. Usage: make panel-map-derive-docker CONFIG=... LAYOUT=/tmp/grid.txt
docker run --rm -i \
-v $(PWD)/config:/code/config:ro \
-v $(PWD)/scripts:/code/scripts:ro \
led-ticker \
python /code/scripts/panel_map.py derive --config /code/$(CONFIG) < $(LAYOUT)
# --- Docker (production image only) ---
# rgbmatrix fork is hardcoded in the Dockerfile (jamesawesome/main).
# Validated to run on both the Pi 4 sign and the Pi 5 bigsign.
# branch@shortsha(+dirty) — baked into the image as LED_TICKER_BUILD_REF.
BUILD_REF ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)@$(shell git rev-parse --short HEAD 2>/dev/null)$(shell git diff --quiet HEAD 2>/dev/null || echo +dirty)
# Package version (PEP 440) is computed per-recipe from git by
# scripts/compute-version.sh — no uv required. It's passed to the build as
# SETUPTOOLS_SCM_PRETEND_VERSION so the image (which has no
# .git) bakes a real version instead of the 0.0.0 scm fallback. Computing it
# inside each recipe means a missing version aborts that build loudly.
build: ## Build the production image only (no start; prerequisite for the *-docker diagnostics)
@VER="$$(sh scripts/compute-version.sh)" || exit 1; \
docker build -t led-ticker \
--build-arg BUILD_REF="$(BUILD_REF)" \
--build-arg SETUPTOOLS_SCM_PRETEND_VERSION="$$VER" .
up: ## Start the sign without rebuilding (set COMPOSE_PROFILES=webui in .env to include the web UI)
docker compose up -d
update: ## Update a running deploy after 'git pull': rebuild the image (real version) + recreate services
@git fetch --tags --quiet 2>/dev/null || true; \
VER="$$(sh scripts/compute-version.sh)" || exit 1; \
BUILD_REF="$(BUILD_REF)" SETUPTOOLS_SCM_PRETEND_VERSION="$$VER" docker compose up -d --build --force-recreate
restart: ## Restart the sign without rebuilding
docker compose restart
down: ## Stop and remove the sign's containers
docker compose down
logs: ## Follow the sign's logs (Ctrl-C to stop)
docker compose logs -f
try: ## Try led-ticker with NO hardware: headless engine + webui preview at http://localhost:8080
@echo "building + starting (first build takes a minute)... then open http://localhost:8080 and click the live preview (stop: Ctrl-C, then make try-down)"
bash scripts/setup.sh try
try-down: ## Stop + remove the try-it containers (and its tmpfs volume)
docker compose -f compose.try.yaml -p led-ticker-try down -v
# Default mode for make setup (override with MODE=try).
MODE ?= deploy
setup: ## One-command setup: check Docker, seed config/.env, bring up. Usage: make setup [MODE=try|deploy]
bash scripts/setup.sh $(MODE)
# --- Cleanup ---
clean: ## Remove build artifacts and caches (does NOT stop containers — use 'make down' for that)
rm -rf .venv/ .pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage htmlcov/ dist/ *.egg-info src/*.egg-info
# --- Docs site ---
docs-dev: ## Run the Astro Starlight dev server (http://localhost:4321/)
cd docs/site && (corepack enable 2>/dev/null || true) && pnpm install && node scripts/build-demos.mjs && pnpm run dev
docs-build: ## Build the docs site to docs/site/dist/
cd docs/site && (corepack enable 2>/dev/null || true) && pnpm install --frozen-lockfile && pnpm run build
docs-check-llms: ## Build the docs + verify the llms.txt Markdown export
cd docs/site && (corepack enable 2>/dev/null || true) && pnpm install --frozen-lockfile && pnpm run build && pnpm run check:llms
docs-lint: ## Lint the docs site (prettier --check + astro check)
cd docs/site && (corepack enable 2>/dev/null || true) && pnpm install --frozen-lockfile && pnpm run lint
docs-format: ## Auto-format the docs site with prettier
cd docs/site && (corepack enable 2>/dev/null || true) && pnpm install --frozen-lockfile && pnpm run format
render-demo: ## Render a single demo gif. Usage: make render-demo CONFIG=path/to.toml OUT=out.gif
uv run python tools/render_demo/render.py $(CONFIG) -o $(OUT)
plan-gif: ## Recommended render --duration for a demo (+ cutoff guard). Usage: make plan-gif CONFIG=path/to.toml
uv run python tools/gif_plan/plan.py $(CONFIG)
render-emoji-previews: ## Re-generate per-slug emoji preview PNGs in docs/site/public/emoji/
uv run python tools/render_emoji_previews.py
derive-phoenix: ## Re-derive config/assets/phoenix.* from the vendored CC0 source
uv run python tools/derive_phoenix_assets.py
derive-pride: ## Re-generate config/assets/pride.* (CC0 rainbow flag)
uv run python tools/derive_pride_assets.py
derive-heart-tunnel: ## Re-generate config/assets/heart-tunnel-opaque.jpg (CC0 trans heart tunnel)
uv run python tools/derive_heart_tunnel.py
# Long-running widget demos (data-fetch widgets — coinbase, mlb, rss_feed, …).
# Source TOMLs in docs/site/demos-long/, output to docs/site/public/demos-long/
# which IS committed to git (vs the auto-render path under public/demos/ which
# is gitignored and regenerates on every Cloudflare build). Cloudflare can't run
# these — they make live HTTP calls and some need API keys not in CI.
render-long-demos: ## Render every long-running widget demo (~30 sec each); local only
@if [ -f .env ]; then set -a; . ./.env; set +a; fi; \
failures=""; \
for toml in docs/site/demos-long/*.toml; do \
[ -f "$$toml" ] || continue; \
name=$$(basename "$$toml" .toml); \
out="docs/site/public/demos-long/$$name.gif"; \
req=$$(grep -E '^# requires-env:' "$$toml" | head -1 | awk '{print $$3}'); \
if [ -n "$$req" ] && [ -z "$$(printenv $$req)" ]; then \
echo "[render-long-demos] SKIP $$name (needs $$req — add it to .env or export it to render)"; \
continue; \
fi; \
dur=$$(grep -E '^# render-duration:' "$$toml" | head -1 | awk '{print $$3}'); \
dur=$${dur:-30}; \
echo "[render-long-demos] $$toml -> $$out ($${dur}s)"; \
if ! uv run python tools/render_demo/render.py "$$toml" -o "$$out" --duration $$dur; then \
failures="$$failures $$name"; \
fi; \
done; \
if [ -n "$$failures" ]; then \
echo "[render-long-demos] FAILED demos:$$failures"; \
exit 1; \
fi
render-long-demo: ## Render one long-running demo. Usage: make render-long-demo NAME=widget-coinbase
@if [ -f .env ]; then set -a; . ./.env; set +a; fi; \
toml="docs/site/demos-long/$(NAME).toml"; \
dur=$$(grep -E '^# render-duration:' "$$toml" | head -1 | awk '{print $$3}'); \
dur=$${dur:-30}; \
echo "[render-long-demo] $$toml ($${dur}s)"; \
uv run python tools/render_demo/render.py "$$toml" \
-o docs/site/public/demos-long/$(NAME).gif --duration $$dur
# Pinned short-render demos. Source TOMLs in docs/site/demos-pinned/, output
# to docs/site/public/demos-pinned/ which IS committed. Same not-in-CI
# semantics as demos-long/ but for fast renders we want to control by hand
# (e.g. Common-pattern showcases on widget pages — we want a stable look,
# not a CI re-render every deploy).
render-pinned-demos: ## Render every pinned short-render demo; local only, output committed
@failures=""; \
for toml in docs/site/demos-pinned/*.toml; do \
[ -f "$$toml" ] || continue; \
name=$$(basename "$$toml" .toml); \
out="docs/site/public/demos-pinned/$$name.gif"; \
dur=$$(grep -E '^# render-duration:' "$$toml" | head -1 | awk '{print $$3}'); \
dur=$${dur:-5}; \
echo "[render-pinned-demos] $$toml -> $$out ($${dur}s)"; \
if ! uv run python tools/render_demo/render.py "$$toml" -o "$$out" --duration $$dur; then \
failures="$$failures $$name"; \
fi; \
done; \
if [ -n "$$failures" ]; then \
echo "[render-pinned-demos] FAILED demos:$$failures"; \
exit 1; \
fi
# Download Atkinson Hyperlegible TTFs into the gitignored fonts dir used by the
# tutorial Chapter 4 demos (tutorial-04a-font, tutorial-04c-image-with-text).
# Idempotent — skips files already present. Gitignored destination, so the
# binaries never enter the repo; readers download their own copy per Chapter 4.
setup-demo-fonts: ## Download Atkinson Hyperlegible to docs/site/demos-long/fonts/
@mkdir -p docs/site/demos-long/fonts
@if [ ! -f docs/site/demos-long/fonts/AtkinsonHyperlegible-Bold.ttf ]; then \
echo "[setup-demo-fonts] downloading AtkinsonHyperlegible-Bold.ttf"; \
curl -sL "https://fonts.gstatic.com/s/atkinsonhyperlegible/v12/9Bt73C1KxNDXMspQ1lPyU89-1h6ONRlW45G8WbcNcw.ttf" \
-o docs/site/demos-long/fonts/AtkinsonHyperlegible-Bold.ttf; \
else echo "[setup-demo-fonts] AtkinsonHyperlegible-Bold.ttf already present"; fi
@if [ ! -f docs/site/demos-long/fonts/AtkinsonHyperlegible-Regular.ttf ]; then \
echo "[setup-demo-fonts] downloading AtkinsonHyperlegible-Regular.ttf"; \
curl -sL "https://fonts.gstatic.com/s/atkinsonhyperlegible/v12/9Bt23C1KxNDXMspQ1lPyU89-1h6ONRlW45GE5Q.ttf" \
-o docs/site/demos-long/fonts/AtkinsonHyperlegible-Regular.ttf; \
else echo "[setup-demo-fonts] AtkinsonHyperlegible-Regular.ttf already present"; fi
@echo "[setup-demo-fonts] ready — tutorial-04* demos can render with the polished font"