Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
464 lines (412 loc) · 22 KB
/
Copy pathMakefile
File metadata and controls
464 lines (412 loc) · 22 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
SHELL := /bin/bash
PROJECT_DIR := $(CURDIR)
SYSTEMD_DIR := $(HOME)/.config/systemd/user
# Dedicated account the production services run under (rootless user-level
# systemd, rooted at ~/danbyte). Its home lives under /opt so the app lands at
# /opt/danbyte/danbyte. Override: `make service-user SERVICE_HOME=/var/lib/danbyte`.
SERVICE_USER ?= danbyte
SERVICE_HOME ?= /opt/danbyte
# Where the app writes danbyte.log + gunicorn logs (settings.LOGGING reads
# DANBYTE_LOG_DIR from .env; systemd still mirrors process output to journald).
LOG_DIR ?= /var/log/danbyte
# Dev-only units. `danbyte-infra` runs Postgres + Redis in docker compose for a
# workstation; a production install provisions them natively (scripts/install.sh
# creates the role/database with psql), so these must NEVER be linked there -
# doing so left an idle, empty Postgres container on hosts that already had one.
DEV_SERVICES := danbyte-mockups danbyte-infra danbyte-backend
# Units both dev and production run.
SHARED_SERVICES := danbyte-workers danbyte-fastlane danbyte-docs
SERVICES := $(DEV_SERVICES) $(SHARED_SERVICES)
# Timer-driven oneshots (monitoring beat). Each has a .service + a .timer; the
# timer is what gets enabled. Not part of `up`/`down` (they're not long-running).
TIMERS := danbyte-dispatch danbyte-materialise danbyte-prune danbyte-utilization danbyte-alert-maintenance danbyte-discover danbyte-cleanup danbyte-drift-dispatch danbyte-auto-upgrade danbyte-drive-outposts danbyte-digest danbyte-hardware danbyte-certificate-expiry danbyte-acme-renew danbyte-document-linkcheck danbyte-task-reminders danbyte-external-sync danbyte-zabbix-sync danbyte-search-reindex danbyte-backups danbyte-scripts danbyte-rollups danbyte-sla danbyte-sla-burn
PY := $(PROJECT_DIR)/.venv/bin/python
.PHONY: help install-services link-units print-timers uninstall-services reload admin-link install-tls-unit uninstall-tls-unit host-sync root-tree-only \
up down restart status logs logs-file \
mockups-up mockups-down mockups-restart mockups-logs \
docs-up docs-down docs-restart docs-logs docs-build schema \
infra-up infra-down infra-restart infra-logs \
backend-up backend-down backend-restart backend-logs \
workers-up workers-down workers-restart workers-logs \
fastlane-up fastlane-down fastlane-restart fastlane-logs \
migrate makemigrations superuser bootstrap seed-demo seed-fabric shell test check \
collectstatic install-prod-services prod-up prod-down prod-restart prod-logs \
proxy-cert proxy-install proxy-reload proxy-uninstall \
linger service-user
help:
@echo "Danbyte dev shortcuts"
@echo ""
@echo " Setup"
@echo " make install-services Symlink unit files into $(SYSTEMD_DIR)"
@echo " make uninstall-services Remove the symlinks"
@echo " make reload systemctl --user daemon-reload"
@echo " make admin-link Put danbyte-admin on PATH as \`danbyte\`"
@echo " make install-tls-unit Root path unit that applies a dropped site certificate"
@echo " make linger Enable user-linger so services run when logged out"
@echo " make service-user Create the '$(SERVICE_USER)' service user in $(SERVICE_HOME) (+linger, +your group)"
@echo ""
@echo " Per service: mockups-{up|down|restart|logs} (same for infra, backend, workers)"
@echo ""
@echo " All at once"
@echo " make up Start mockups + infra + backend + workers"
@echo " make down Stop them all"
@echo " make restart Restart them all"
@echo " make status Show systemctl status"
@echo " make logs Tail journalctl for all"
@echo ""
@echo " Django"
@echo " make migrate / makemigrations / superuser / shell / test / check"
@echo ""
@echo " Reverse proxy (everything on https/443)"
@echo " make proxy-install One-time: nginx + self-signed cert + config"
@echo " make proxy-reload Re-apply after editing the template"
@echo " make proxy-uninstall Remove the danbyte nginx site"
# ---- service installation ----------------------------------------------------
# scripts/install.sh does this for a packaged install; a source checkout has
# no installer, so the same one-liner lives here rather than being a step
# everyone rediscovers. /usr/local/bin needs root - the rest of the Makefile
# does not, so this is its own target and not folded into install-services.
admin-link:
@sudo ln -sfn $(PROJECT_DIR)/scripts/danbyte-admin /usr/local/bin/danbyte
@echo " danbyte -> $(PROJECT_DIR)/scripts/danbyte-admin"
# A production host (the installer linked danbyte-web) never gets the dev
# units: the runserver, the compose infra, the mockups. Linking them is how
# an upgrade's "restart everything" ends with two processes on :8000.
DEV_ONLY_SERVICES := danbyte-mockups danbyte-infra danbyte-backend danbyte-frontend
install-services:
@mkdir -p $(SYSTEMD_DIR)
@for s in $(SERVICES); do \
if [ -e $(SYSTEMD_DIR)/danbyte-web.service ] && echo " $(DEV_ONLY_SERVICES) " | grep -q " $$s " ; then \
continue ; \
fi ; \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
echo " linked $$s.service" ; \
done
@for s in $(TIMERS); do \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
ln -sfn $(PROJECT_DIR)/services/$$s.timer $(SYSTEMD_DIR)/$$s.timer ; \
echo " linked $$s.service + $$s.timer" ; \
done
@systemctl --user daemon-reload
@for s in $(TIMERS); do \
systemctl --user enable --now $$s.timer ; \
done
@# A long-running service added after an install is linked above but
@# nothing starts it - the upgrader before 0.16 restarts only the units it
@# knew. The fast lane runs on every install, so start it here.
@systemctl --user enable --now danbyte-fastlane 2>/dev/null || true
@echo ""
@echo "Installed. Try:"
@echo " make mockups-up # http://localhost:8080"
# What an upgrade links (scripts/upgrade/stage.sh): the same set as
# install-services, or install-prod-services on a host that runs gunicorn,
# but nothing is enabled or started - the stage decides what runs, and when.
# install-services keeps its behaviour: releases before 0.17 call it.
link-units:
@mkdir -p $(SYSTEMD_DIR)
@if [ -e $(SYSTEMD_DIR)/danbyte-web.service ]; then set="$(PROD_SERVICES)"; else set="$(SERVICES)"; fi; \
for s in $$set; do \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
done
@for s in $(TIMERS); do \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
ln -sfn $(PROJECT_DIR)/services/$$s.timer $(SYSTEMD_DIR)/$$s.timer ; \
done
@systemctl --user daemon-reload
print-timers:
@echo $(TIMERS)
uninstall-services:
@for s in $(SERVICES); do \
rm -f $(SYSTEMD_DIR)/$$s.service ; \
done
@for s in $(TIMERS); do \
systemctl --user disable --now $$s.timer 2>/dev/null || true ; \
rm -f $(SYSTEMD_DIR)/$$s.service $(SYSTEMD_DIR)/$$s.timer ; \
done
@systemctl --user daemon-reload
@echo "Removed."
reload:
@systemctl --user daemon-reload
linger:
@sudo loginctl enable-linger $(USER)
@echo "User-linger enabled. Services will keep running after logout."
# One-shot: provision the dedicated service account the prod units run under.
# Its home is $(SERVICE_HOME) so the app lives at $(SERVICE_HOME)/danbyte.
# Run WITHOUT sudo (it calls sudo per-command, like `linger`). Idempotent.
service-user:
@id -u $(SERVICE_USER) >/dev/null 2>&1 || \
sudo adduser --disabled-password --gecos "Danbyte service" --home $(SERVICE_HOME) $(SERVICE_USER)
@sudo loginctl enable-linger $(SERVICE_USER)
@sudo usermod -aG $(SERVICE_USER) $(USER)
@sudo install -d -o $(SERVICE_USER) -g $(SERVICE_USER) -m 755 $(LOG_DIR)
@echo "Service user '$(SERVICE_USER)' ready in $(SERVICE_HOME): linger on, '$(USER)' added to the group."
@echo "Log dir $(LOG_DIR) created (owned by $(SERVICE_USER))."
@echo "Next: sudo machinectl shell $(SERVICE_USER)@ then clone into ~/danbyte ($(SERVICE_HOME)/danbyte)."
# ---- per-service shortcuts ---------------------------------------------------
mockups-up: ; systemctl --user start danbyte-mockups
mockups-down: ; systemctl --user stop danbyte-mockups
mockups-restart: ; systemctl --user restart danbyte-mockups
mockups-logs: ; journalctl --user -fu danbyte-mockups
infra-up: ; systemctl --user start danbyte-infra
infra-down: ; systemctl --user stop danbyte-infra
infra-restart: ; systemctl --user restart danbyte-infra
infra-logs: ; journalctl --user -fu danbyte-infra
backend-up: ; systemctl --user start danbyte-backend
backend-down: ; systemctl --user stop danbyte-backend
backend-restart: ; systemctl --user restart danbyte-backend
backend-logs: ; journalctl --user -fu danbyte-backend
workers-up: ; systemctl --user start danbyte-workers
workers-down: ; systemctl --user stop danbyte-workers
workers-restart: ; systemctl --user restart danbyte-workers
workers-logs: ; journalctl --user -fu danbyte-workers
fastlane-up: ; systemctl --user start danbyte-fastlane
fastlane-down: ; systemctl --user stop danbyte-fastlane
fastlane-restart: ; systemctl --user restart danbyte-fastlane
fastlane-logs: ; journalctl --user -fu danbyte-fastlane
docs-up: ; systemctl --user start danbyte-docs
docs-down: ; systemctl --user stop danbyte-docs
docs-restart: ; systemctl --user restart danbyte-docs
docs-logs: ; journalctl --user -fu danbyte-docs
docs-build: ; .venv/bin/zensical build
# ---- OpenAPI schema ----------------------------------------------------------
# Generate the OpenAPI 3 schema to repo-root openapi.yaml - the committed source
# of truth the API reference site (api.danbyte.net) tracks, the same "repo is
# the source of truth" pattern as the docs. Regenerated + committed on each
# release (see .github/workflows/release.yml). Works airgapped; no live endpoint.
schema: ; .venv/bin/python manage.py spectacular --file openapi.yaml --validate
# ---- tailwind ----------------------------------------------------------------
# Static Tailwind v3 build → design/tailwind.css, served from /static/.
# Replaces the Play CDN which JIT-compiled in the browser on every htmx
# swap and trashed INP. Run `make css` once after `npm install`; use
# `make css-watch` while editing templates.
css-install: ; npm install
css: ; npm run build:css
css-watch: ; npm run watch:css
# ---- frontend (Vite + React + TanStack Start SSR) ---------------------------
# Lives in frontend/. In dev, `frontend-dev` runs the Vite dev server (:3000)
# which proxies /api to Django. In production, `frontend-build` emits the SSR
# bundle to frontend/dist and danbyte-frontend-prod serves it (vite preview);
# nginx routes /api, /ws, /static, /admin to Django and / to the frontend.
frontend-install: ; cd frontend && npm install
frontend-dev: ; cd frontend && npm run dev
frontend-build: ; cd frontend && npx vite build
# ---- nginx reverse proxy (single-port 443) ----------------------------------
# Puts SPA (/), API (/api), admin (/admin) and docs (/docs) behind one HTTPS
# origin. Needs sudo (system nginx + bind 443). Self-signed cert by default;
# for a browser-trusted LAN cert install `mkcert` and point CERT/KEY at it.
#
# make proxy-install # one-time: install nginx, cert, config, reload
# make proxy-reload # re-apply the config after editing the template
# make proxy-uninstall # remove the danbyte site + reload
#
# Override the hostname/IP the cert + server_name use:
# make proxy-install PROXY_HOST=danbyte.lan
PROXY_HOST ?= $(shell hostname -I 2>/dev/null | awk '{print $$1}')
NGINX_TMPL := $(PROJECT_DIR)/deploy/nginx/danbyte.conf.template
NGINX_SITE := /etc/nginx/sites-available/danbyte.conf
STATIC_ROOT := $(PROJECT_DIR)/staticfiles
MEDIA_ROOT := $(PROJECT_DIR)/media
CERT_DIR := $(PROJECT_DIR)/deploy/nginx/certs
CERT := /etc/ssl/danbyte/danbyte.crt
KEY := /etc/ssl/danbyte/danbyte.key
proxy-cert:
@mkdir -p $(CERT_DIR)
@if [ ! -f $(CERT_DIR)/danbyte.crt ]; then \
echo "Generating self-signed cert for $(PROXY_HOST) ..." ; \
if echo "$(PROXY_HOST)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$$'; then \
san="IP:$(PROXY_HOST),DNS:localhost" ; \
else \
san="DNS:$(PROXY_HOST),DNS:localhost" ; \
fi ; \
openssl req -x509 -nodes -newkey rsa:2048 -days 825 \
-keyout $(CERT_DIR)/danbyte.key -out $(CERT_DIR)/danbyte.crt \
-subj "/CN=$(PROXY_HOST)" -addext "subjectAltName=$$san" ; \
else echo "Cert already exists in $(CERT_DIR) (delete to regenerate)." ; fi
# Render $(NGINX_TMPL) → $(NGINX_SITE), choosing the HTTP/2 form from the
# INSTALLED nginx version (detected at recipe time, so nginx is already present):
# 1.25.1+ gets a bare `listen … ssl` + standalone `http2 on;`; older nginx gets
# `listen … ssl http2`, where the standalone directive is a hard error. One
# template, no deprecation warning on new nginx, no breakage on old. Shared by
# proxy-install and proxy-reload so the two can't drift.
define RENDER_NGINX
ver=$$(nginx -v 2>&1 | sed -n 's|.*/\([0-9][0-9.]*\).*|\1|p'); \
if [ -n "$$ver" ] && [ "$$(printf '1.25.1\n%s\n' "$$ver" | sort -V | head -n1)" = "1.25.1" ]; then \
h2l=""; h2d=" http2 on;"; \
else h2l=" http2"; h2d=""; fi; \
sed -e "s|@@SERVER_NAME@@|$(PROXY_HOST)|g" \
-e "s|@@CERT@@|$(CERT)|g" \
-e "s|@@KEY@@|$(KEY)|g" \
-e "s|@@STATIC_ROOT@@|$(STATIC_ROOT)|g" \
-e "s|@@MEDIA_ROOT@@|$(MEDIA_ROOT)|g" \
-e "s|@@MAINTENANCE_ROOT@@|$(PROJECT_DIR)/deploy|g" \
-e "s|@@H2_LISTEN@@|$$h2l|g" \
-e "s|@@H2_DIRECTIVE@@|$$h2d|g" \
$(NGINX_TMPL) | sudo tee $(NGINX_SITE) >/dev/null
endef
proxy-install: root-tree-only proxy-cert
@command -v nginx >/dev/null || { echo "Installing nginx ..."; sudo apt-get update -qq && sudo apt-get install -y nginx; }
@echo "Installing cert to /etc/ssl/danbyte ..."
@sudo mkdir -p /etc/ssl/danbyte
@sudo chmod 755 /etc/ssl/danbyte # the certificate is public; root's umask must not hide it
@sudo install -m 644 $(CERT_DIR)/danbyte.crt $(CERT)
@sudo install -m 600 $(CERT_DIR)/danbyte.key $(KEY)
@echo "Writing nginx site for host '$(PROXY_HOST)' ..."
@$(RENDER_NGINX)
@sudo mkdir -p /etc/nginx/sites-enabled
@sudo ln -sfn $(NGINX_SITE) /etc/nginx/sites-enabled/danbyte.conf
@sudo rm -f /etc/nginx/sites-enabled/default
@sudo nginx -t && sudo systemctl enable --now nginx && sudo systemctl reload nginx
@echo ""
@echo "Proxy live → https://$(PROXY_HOST)/ (docs at /docs/, api at /api/)"
@echo "Self-signed cert: your browser will warn once; accept it for the LAN."
@echo "Make sure the dev servers are up: make docs-up backend-up + make frontend-dev"
# ---- root steps: only from a tree root owns ----------------------------------
# install-tls-unit, host-sync and proxy-install/-reload run this tree's files
# as root or make them root's: the certificate unit's script, the nginx site,
# logrotate. Root runs only files that root owns, or that belong to whoever
# runs make on a checkout of their own. A production install's app directory
# belongs to the service account, and a file changed there would run as root
# (#287), so from such a tree they refuse. Use the unpacked bundle of the
# release that runs (sudo ./install.sh --host-only), or a checkout root made,
# with APP naming the install:
# sudo git clone --depth 1 --branch vX.Y.Z <repository> /root/danbyte-vX.Y.Z
# sudo make -C /root/danbyte-vX.Y.Z host-sync APP=/opt/danbyte/danbyte
# APP and HOST count only on the command line: a shell's own (zsh sets HOST)
# must not point the root steps somewhere else.
ROOT_APP := $(if $(filter command line,$(origin APP)),$(APP),$(PROJECT_DIR))
ROOT_HOST := $(if $(filter command line,$(origin HOST)),$(HOST))
root-tree-only:
@me=$${SUDO_UID:-$$(id -u)}; \
bad=$$(find $(PROJECT_DIR)/Makefile $(PROJECT_DIR)/scripts $(PROJECT_DIR)/deploy \
! -uid 0 ! -uid "$$me" -print -quit 2>/dev/null); \
d=$(PROJECT_DIR); \
while [ -z "$$bad" ] && [ "$$d" != / ]; do \
o=$$(stat -c %u "$$d"); [ "$$o" = 0 ] || [ "$$o" = "$$me" ] || bad=$$d; \
d=$$(dirname "$$d"); \
done; \
[ -z "$$bad" ] || { \
echo "make $(MAKECMDGOALS): $$bad belongs to $$(stat -c %U "$$bad"), not to root or you - root runs nothing from it." >&2; \
echo " From the unpacked bundle of this release: sudo ./install.sh --host-only" >&2; \
echo " Or from a checkout root made: sudo make -C <checkout> $(MAKECMDGOALS) APP=$(ROOT_APP)" >&2; \
exit 1; }
# The root path unit that applies a certificate pair the app drops in
# deploy/nginx/certs/ (Settings → Updates → Site certificate), for the
# install in APP. Needs sudo; install.sh does the same from the bundle. The
# unit runs a root-owned copy of the apply script, from this tree.
TLS_LIBEXEC := /usr/local/libexec/danbyte
install-tls-unit: root-tree-only
@sudo install -d -o root -g root -m 755 $(TLS_LIBEXEC) /var/lib/danbyte-tls
@sudo install -o root -g root -m 755 $(PROJECT_DIR)/scripts/danbyte-tls-apply.sh $(TLS_LIBEXEC)/danbyte-tls-apply.sh
@owner=$$(stat -c %U $(ROOT_APP)); for u in path service; do \
sed -e "s|@@APP@@|$(ROOT_APP)|g" -e "s|@@USER@@|$$owner|g" $(PROJECT_DIR)/deploy/systemd/danbyte-tls.$$u.template \
| sudo tee /etc/systemd/system/danbyte-tls.$$u >/dev/null ; \
done
@sudo systemctl daemon-reload
@sudo systemctl enable --now danbyte-tls.path
@# The drop folder is made by the app's user, as host-sync makes it: root
@# never creates, chowns or chmods in the app's tree. One that user does
@# not own (a proxy-install run as root) is that user's to move aside.
@owner=$$(stat -c %U $(ROOT_APP)); certs=$(ROOT_APP)/deploy/nginx/certs; \
if [ -e "$$certs" ] && [ "$$(stat -c %U "$$certs")" != "$$owner" ]; then \
echo "$$certs is not $$owner's: as $$owner, move it aside (mv $$certs $$certs.old) and run this again" >&2; \
exit 1; \
fi; \
sudo -u "$$owner" mkdir -p "$$certs" && sudo -u "$$owner" chmod 750 "$$certs"
@echo " danbyte-tls.path watches $(ROOT_APP)/deploy/nginx/certs/danbyte.apply"
uninstall-tls-unit:
@sudo systemctl disable --now danbyte-tls.path 2>/dev/null || true
@sudo rm -f /etc/systemd/system/danbyte-tls.path /etc/systemd/system/danbyte-tls.service
@sudo rm -f $(TLS_LIBEXEC)/danbyte-tls-apply.sh
@sudo systemctl daemon-reload
# What install.sh does as root after an upgrade - logrotate, the nginx site
# (re-rendered only while it is still what Danbyte rendered, with its live
# certificate and name, and put back if nginx -t refuses it) and the
# certificate unit - from this tree, for the install in APP. HOST names a
# new site (an existing one keeps its name unless given); ADOPT=1 takes the
# new render over a site edited by hand, keeping a backup.
host-sync: root-tree-only
@sudo bash $(PROJECT_DIR)/scripts/host-sync.sh --app $(ROOT_APP) --user $$(stat -c %U $(ROOT_APP)) \
--log-dir $(LOG_DIR) $(if $(ROOT_HOST),--host $(ROOT_HOST)) $(if $(ADOPT),--adopt)
proxy-reload: root-tree-only
@$(RENDER_NGINX)
@sudo nginx -t && sudo systemctl reload nginx && echo "Reloaded."
proxy-uninstall:
@sudo rm -f /etc/nginx/sites-enabled/danbyte.conf $(NGINX_SITE)
@sudo nginx -t && sudo systemctl reload nginx
@echo "Removed the danbyte nginx site."
# ---- bulk --------------------------------------------------------------------
up:
@systemctl --user start $(SERVICES)
down:
@systemctl --user stop $(SERVICES)
restart:
@systemctl --user restart $(SERVICES)
status:
@systemctl --user status --no-pager $(SERVICES) 2>&1 | head -80
logs:
@journalctl --user -f $(addprefix -u ,$(SERVICES))
# Tail the on-disk application logs (production; $(LOG_DIR)). journald still has
# per-service process output via `make logs`.
logs-file:
@tail -n 100 -f $(LOG_DIR)/*.log
# ---- Django ------------------------------------------------------------------
migrate:
@$(PY) manage.py migrate
makemigrations:
@$(PY) manage.py makemigrations
superuser:
@$(PY) manage.py createsuperuser
# Idempotent first-run setup: default Organization + Tenant, built-in Status
# catalog, and (if DJANGO_SUPERUSER_* are set) an admin user. Safe to re-run.
bootstrap:
@$(PY) manage.py bootstrap
# Opt-in demo data (run `make bootstrap` first). Idempotent.
seed-demo:
@$(PY) manage.py seed_demo
@$(PY) manage.py seed_demo_172
# Opt-in demo leaf/spine EVPN fabric for the routing pages. Idempotent.
seed-fabric:
@$(PY) manage.py seed_fabric
shell:
@$(PY) manage.py shell
test:
@$(PY) manage.py test
coverage:
@$(PY) -m coverage run --source='.' manage.py test
@$(PY) -m coverage report
check:
@$(PY) manage.py check
# Gather Django static (admin + DRF assets) into STATIC_ROOT so nginx serves
# them from disk in production (gunicorn doesn't serve static with DEBUG off).
collectstatic:
@$(PY) manage.py collectstatic --noinput
# ---- production processes ----------------------------------------------------
# Production replaces the dev runserver/Vite-dev units with gunicorn (HTTP),
# daphne (websockets) and the built SSR frontend. Install + enable them with:
# make install-prod-services
# systemctl --user stop danbyte-backend danbyte-frontend # the dev units
# systemctl --user enable --now danbyte-web danbyte-ws danbyte-frontend-prod
# Everything a production host runs - web/ws/frontend plus the shared units.
# scripts/install.sh links ONLY these; it must not pull in DEV_SERVICES.
PROD_SERVICES := danbyte-web danbyte-ws danbyte-frontend-prod $(SHARED_SERVICES)
install-prod-services:
@mkdir -p $(SYSTEMD_DIR)
@for s in $(PROD_SERVICES); do \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
echo " linked $$s.service" ; \
done
@for s in $(TIMERS); do \
ln -sfn $(PROJECT_DIR)/services/$$s.service $(SYSTEMD_DIR)/$$s.service ; \
ln -sfn $(PROJECT_DIR)/services/$$s.timer $(SYSTEMD_DIR)/$$s.timer ; \
done
@systemctl --user daemon-reload
@for s in $(TIMERS); do \
systemctl --user enable --now $$s.timer >/dev/null 2>&1 || true ; \
done
@echo "Linked. Build the frontend + collect static, then enable:"
@echo " make frontend-build collectstatic"
@echo " systemctl --user enable --now $(PROD_SERVICES)"
prod-up: ; systemctl --user start $(PROD_SERVICES)
prod-down: ; systemctl --user stop $(PROD_SERVICES)
prod-restart: ; systemctl --user restart $(PROD_SERVICES)
prod-logs: ; journalctl --user -f -u danbyte-web -u danbyte-ws -u danbyte-frontend-prod