Euler source The code that runs the solver shell and this site, version 4.34.2 Repository on GitHub

Target verbs, by kind.

429 lines of Makefile Raw file Open on GitHub

# Target verbs, by kind:
#   local  (terminal solver, incl. system packages) — install / uninstall
#   system (the web stack, sudo + systemd)         — deploy / remove / redeploy [/ upgrade]
# Each system target's verb matches the action its script in scripts/setup/ takes.

.PHONY: install-all install-minimal install-system install-chrome install-primesieve-numpy \
        install-hooks uninstall-hooks install-completions uninstall-completions \
        install-credentials install-claude uninstall-claude install-nodejs uninstall-nodejs \
        deploy-system-venv remove-system-venv redeploy-system-venv \
        deploy-frontend remove-frontend upgrade-frontend redeploy-frontend \
        deploy-egress remove-egress upgrade-egress \
        deploy-ddns remove-ddns \
        deploy-firewall remove-firewall \
        deploy-smtp remove-smtp upgrade-smtp \
        deploy-auth remove-auth upgrade-auth redeploy-auth \
        deploy-msg remove-msg upgrade-msg redeploy-msg \
        deploy-site remove-site upgrade-site redeploy-site \
        deploy-user remove-user upgrade-user redeploy-user \
        deploy-web remove-web upgrade-web redeploy-web logout-web status-web \
        test run audit verify-solutions uninstall version release check-version

VENV   := .venv
PYTHON := $(VENV)/bin/python
PIP    := $(VENV)/bin/pip

# ── The terminal solver: install / uninstall (local) ───────────────────────────────

## Full developer install: system deps → venv → all dependency groups → git hooks → completions
install-all: install-system install-chrome $(VENV)
	$(PIP) install --upgrade pip
	$(PIP) uninstall -y -q solver 2>/dev/null || true   # the single-package dist before the split
	$(PIP) install --upgrade -e "packages/solver[ai,dev,show,solutions]" -e packages/site -e packages/webshell
	rm -rf build packages/*/build packages/*/*.egg-info
	$(MAKE) install-primesieve-numpy
	$(MAKE) install-hooks
	$(MAKE) install-completions
	$(MAKE) install-credentials
	$(MAKE) install-claude
	@printf "✓ install-all complete: full developer environment ready\n"

## Minimal install: base + solutions + show (no dev tools)
install-minimal: install-system install-chrome $(VENV)
	$(PIP) install --upgrade pip
	$(PIP) uninstall -y -q solver 2>/dev/null || true   # the single-package dist before the split
	$(PIP) install --upgrade -e "packages/solver[show,solutions]" -e packages/site -e packages/webshell
	rm -rf build packages/*/build packages/*/*.egg-info
	$(MAKE) install-primesieve-numpy
	@printf "✓ install-minimal complete: base environment ready\n"

## Install system packages (apt) via setup script
install-system:
	./scripts/setup/dev_env.sh install python primesieve c
	@printf "✓ install-system complete: apt packages installed\n"

## Install Chrome browser
install-chrome:
	./scripts/setup/chrome.sh install
	@printf "✓ install-chrome complete: Chrome installed\n"

## Build & install the optional primesieve.numpy extension from source (NumPy 2.x)
install-primesieve-numpy: $(VENV)
	./scripts/setup/primesieve_numpy_extension.sh install
	@printf "✓ install-primesieve-numpy complete: primesieve.numpy extension ready\n"

## Install bash tab-completions for all project scripts
install-completions:
	./scripts/bash_completions.sh install
	@printf "✓ install-completions complete: bash completions installed\n"

## Remove bash tab-completions
uninstall-completions:
	./scripts/bash_completions.sh uninstall
	@printf "✓ uninstall-completions complete: bash completions removed\n"

## Install git hooks into .git/hooks/ and reset hooksPath to default
install-hooks:
	git config --unset core.hooksPath || true
	./scripts/setup/githooks.sh install --force
	@printf "✓ install-hooks complete: git hooks installed\n"

## Remove git hooks from .git/hooks/
uninstall-hooks:
	./scripts/setup/githooks.sh uninstall
	@printf "✓ uninstall-hooks complete: git hooks removed\n"

## Configure gh as the git credential helper (logging in first if needed) and set the git identity
install-credentials:
	./scripts/git/configure-identity.sh
	@printf "✓ install-credentials complete: gh configured as git credential helper, git identity set\n"

## Install the Claude Code CLI (driven by the AI commands claude-solve / claude-blog)
install-claude:
	./scripts/setup/claude_code.sh install
	@printf "✓ install-claude complete: Claude Code CLI installed\n"

## Remove the Claude Code CLI
uninstall-claude:
	./scripts/setup/claude_code.sh uninstall
	@printf "✓ uninstall-claude complete: Claude Code CLI removed\n"

## Install a standalone Node.js under ~/.local (dev-only, no sudo; drives the SRP interop test)
install-nodejs:
	./scripts/setup/nodejs.sh install
	@printf "✓ install-nodejs complete\n"

## Remove the standalone Node.js installed by install-nodejs
uninstall-nodejs:
	./scripts/setup/nodejs.sh uninstall
	@printf "✓ uninstall-nodejs complete\n"

## Create venv if it doesn't exist
$(VENV):
	python3.14 -m venv $(VENV)
	@printf "✓ venv created at $(VENV)\n"

## Remove the venv (system deps must be removed separately)
uninstall: uninstall-hooks uninstall-completions
	rm -rf $(VENV) build packages/*/build packages/*/*.egg-info
	@printf "✓ uninstall complete: venv removed\n"
	@printf "To remove system dependencies run:\n\t./scripts/setup/dev_env.sh uninstall python primesieve c\n"
	@printf "To remove the Claude Code CLI run:\n\tmake uninstall-claude\n"

# ── Day to day: tests, the shell, versions, the audit ──────────────────────────────

## Run the unit test suite (tests/)
test: $(VENV)
	$(PYTHON) -m unittest discover -s tests -t .

## Run the interactive solver shell
run: $(VENV)
	$(VENV)/bin/solver

## Show the running build's version (packages/solver/solver/version.py, plus how far HEAD is past its tag)
version: $(VENV)
	@$(VENV)/bin/solver version

## Cut a release: bump packages/solver/solver/version.py from Conventional Commits, commit,
## tag, and push the commit + tag to origin. ARGS=--dry-run previews; ARGS=--no-push
## stops before publishing.
release:
	@./scripts/version/release.sh $(ARGS)

## Guard: the version redeploy-web would ship (packages/solver/solver/version.py) must have its
## vX.Y.Z tag on origin, so a redeploy never runs a release collaborator clones
## cannot reach (a locally-tagged-but-unpushed release runs the venv ahead of every
## clone). Queries origin — needs network. A prerequisite of redeploy-web.
check-version:
	@ver=$$(sed -nE "s/^__version__ = '([^']*)'.*/\1/p" packages/solver/solver/version.py); \
	if [ -z "$$ver" ]; then \
		printf "✗ check-version: could not read the version from packages/solver/solver/version.py\n" >&2; exit 1; \
	fi; \
	tag="v$$ver"; \
	if git ls-remote --tags origin "$$tag" 2>/dev/null | grep -q "refs/tags/$$tag"; then \
		printf "✓ check-version: %s is published on origin\n" "$$tag"; \
	else \
		printf "✗ check-version: %s is NOT on origin — publish the release before deploying.\n" "$$tag" >&2; \
		printf "  run 'make release' (bump + push) or 'git push origin HEAD %s'\n" "$$tag" >&2; \
		exit 1; \
	fi

## Audit what git stores across the WHOLE tracked tree (~25s): every file under
## solutions/private is encrypted at rest, and no file anywhere is a compiled binary.
## The git hooks only audit what you actually stage or push, so this is the periodic
## sweep that also covers blobs committed before the hooks were in place.
## Counts only; offenders are always listed. Per-file detail: scripts/git/audit.sh
audit: $(VENV)
	@PYTHON=$(PYTHON) ./scripts/git/audit.sh --summary

## Re-check every solved problem against its test cases (eval in a loop), with a live
## progress line; the full output goes to .verify-solutions.log. LIST='{solved}[0:10]'
## checks part of the list, LIST='14 76 478' just those problems. Takes a while: it runs
## every solution on its main case.
verify-solutions: $(VENV)
	@PATH="$(VENV)/bin:$$PATH" ./scripts/ops/verify-solutions.sh $(if $(LIST),'$(LIST)')

# ── The web stack: deploy / remove / redeploy (sudo + systemd) ─────────────────────

## Deploy the root-owned /opt/euler system venv the euler-* app services run from (sudo)
deploy-system-venv:
	./scripts/setup/venv.sh deploy
	@printf "✓ deploy-system-venv complete: /opt/euler venv deployed\n"

## Remove the /opt/euler system venv (leaves service identities, state, and units intact) (sudo)
remove-system-venv:
	./scripts/setup/venv.sh remove
	@printf "✓ remove-system-venv complete: /opt/euler venv removed\n"

## Rebuild the /opt/euler system venv from scratch: remove, then deploy (sudo)
redeploy-system-venv:
	./scripts/setup/venv.sh redeploy
	@printf "✓ redeploy-system-venv complete: /opt/euler venv rebuilt\n"

## Deploy the web edge: Caddy + acme.sh + euler-caddy.service (see docs/web-server-guide.md)
deploy-frontend:
	./scripts/setup/frontend.sh deploy
	@printf "✓ deploy-frontend complete: web edge deployed (sudo systemctl for lifecycle)\n"

## Remove the web edge (prompts before deleting /etc/euler, acme.sh, and the service users)
remove-frontend:
	./scripts/setup/frontend.sh remove
	@printf "✓ remove-frontend complete: web edge removed\n"

## Upgrade the web edge packages (Caddy + acme.sh; regenerate the Caddyfile + unit)
upgrade-frontend:
	./scripts/setup/frontend.sh upgrade
	@printf "✓ upgrade-frontend complete: web edge upgraded\n"

## Refresh the static web-content + Caddyfile and reload the edge
redeploy-frontend:
	./scripts/setup/frontend.sh redeploy
	@printf "✓ redeploy-frontend complete: web-content + Caddyfile reloaded\n"

## Deploy the egress proxy: Squid domain-allowlist + euler-proxy.service (see docs/web-server-guide.md)
deploy-egress:
	./scripts/setup/egress.sh deploy
	@printf "✓ deploy-egress complete: egress proxy deployed (sudo systemctl for lifecycle)\n"

## Remove the egress proxy (prompts before deleting /etc/euler-proxy and the euler-proxy user)
remove-egress:
	./scripts/setup/egress.sh remove
	@printf "✓ remove-egress complete: egress proxy removed\n"

## Upgrade the egress proxy package (Squid; regenerate the config + unit)
upgrade-egress:
	./scripts/setup/egress.sh upgrade
	@printf "✓ upgrade-egress complete: egress proxy upgraded\n"

## Deploy the dynamic-DNS updater timer (public access only — keeps the A record current)
deploy-ddns:
	./scripts/setup/ddns.sh deploy
	@printf "✓ deploy-ddns complete: DDNS timer deployed\n"

## Remove the dynamic-DNS updater timer (name.com records left untouched)
remove-ddns:
	./scripts/setup/ddns.sh remove
	@printf "✓ remove-ddns complete: DDNS timer removed\n"

## Deploy the kernel egress firewall: per-uid nftables ruleset + euler-firewall.service (DD-8)
deploy-firewall:
	./scripts/setup/firewall.sh deploy
	@printf "✓ deploy-firewall complete: euler egress ruleset loaded (boot-enabled)\n"

## Remove the kernel egress firewall (flushes the euler table; nothing else touched)
remove-firewall:
	./scripts/setup/firewall.sh remove
	@printf "✓ remove-firewall complete: euler egress ruleset removed\n"

## Deploy the loopback SMTP relay: euler-smtp user + service, scoped Gmail creds (DD-8)
deploy-smtp:
	./scripts/setup/smtp.sh deploy
	@printf "✓ deploy-smtp complete: loopback mail relay deployed (sudo systemctl for lifecycle)\n"

## Remove the loopback SMTP relay (prompts before deleting smtp.env and the euler-smtp user)
remove-smtp:
	./scripts/setup/smtp.sh remove
	@printf "✓ remove-smtp complete: loopback mail relay removed\n"

## Upgrade the loopback SMTP relay (alias of deploy: relay + config + unit, restart)
upgrade-smtp:
	./scripts/setup/smtp.sh upgrade
	@printf "✓ upgrade-smtp complete: loopback mail relay upgraded\n"

## Deploy the auth-service runtime: euler-auth/euler-adm + /opt/euler venv + auth.env (DD-5/DD-6)
deploy-auth:
	./scripts/setup/auth.sh deploy
	@printf "✓ deploy-auth complete: auth runtime deployed\n"

## Remove the auth service (prompts before deleting the venv, state, and identities)
remove-auth:
	./scripts/setup/auth.sh remove
	@printf "✓ remove-auth complete: auth service removed\n"

## Upgrade the auth service (alias of deploy: pip re-install the repo into /opt/euler,
## refresh config + unit)
upgrade-auth:
	./scripts/setup/auth.sh upgrade
	@printf "✓ upgrade-auth complete: auth service upgraded\n"

## Redeploy the shared /opt/euler venv (auth.sh owns it) + restart the auth service
redeploy-auth:
	./scripts/setup/auth.sh redeploy
	@printf "✓ redeploy-auth complete: venv + authorizations refreshed, auth restarted\n"

## Deploy the message spool: the euler-msg user + /etc/euler/msg.env + euler-msg.service
## (user<->staff threads; see docs/web-server-guide.md § Messaging). Needs the shared
## /opt/euler venv, which auth.sh owns — run deploy-auth first
deploy-msg:
	./scripts/setup/msg.sh deploy
	@printf "✓ deploy-msg complete: message spool deployed\n"

## Remove the message spool (prompts before deleting the threads and the identity)
remove-msg:
	./scripts/setup/msg.sh remove
	@printf "✓ remove-msg complete: message spool removed\n"

## Upgrade the message spool (alias of deploy: refresh config + unit, restart)
upgrade-msg:
	./scripts/setup/msg.sh upgrade
	@printf "✓ upgrade-msg complete: message spool upgraded\n"

## Restart the message spool against the current /opt/euler venv (redeploy-auth
## rebuilds that venv; this only bounces the service onto it)
redeploy-msg:
	./scripts/setup/msg.sh redeploy
	@printf "✓ redeploy-msg complete: message spool restarted\n"

## Deploy the public site: the euler-site user + /etc/euler/site.env + euler-site.service,
## serving the packaged content (euler-content, installed into the shared venv by
## deploy-auth) to signed-out visitors (docs/package-guide.md § 4)
deploy-site:
	./scripts/setup/site.sh deploy
	@printf "✓ deploy-site complete: public site deployed\n"

## Remove the public site (signed-out visitors get the login page again)
remove-site:
	./scripts/setup/site.sh remove
	@printf "✓ remove-site complete: public site removed\n"

## Upgrade the public site (alias of deploy: refresh config + unit, restart)
upgrade-site:
	./scripts/setup/site.sh upgrade
	@printf "✓ upgrade-site complete: public site upgraded\n"

## Restart the public site against the current /opt/euler venv and content package
## (redeploy-auth rebuilds both); deploys it when it is not installed yet
redeploy-site:
	./scripts/setup/site.sh redeploy
	@printf "✓ redeploy-site complete: public site restarted\n"

## Deploy the per-user provisioning layer (MT-7): the euler-user group,
## /etc/euler/user.env, and the euler-user@.service/.socket template. Per-collaborator
## uids/homes/clones are created later by `users add <email>`
## (euler_webshell.users → user.sh provision), which clones ~/euler straight
## from the public GitHub repo (ciphertext at rest, MT-13).
deploy-user:
	./scripts/setup/user.sh deploy
	@printf "✓ deploy-user complete: per-user provisioning layer deployed\n"

## Remove the per-user layer (refuses while any provisioned collaborator remains)
remove-user:
	./scripts/setup/user.sh remove
	@printf "✓ remove-user complete: per-user provisioning layer removed\n"

## Re-assert the shared per-user layer (alias of deploy; re-lays the unit template)
upgrade-user:
	./scripts/setup/user.sh upgrade
	@printf "✓ upgrade-user complete: per-user provisioning layer upgraded\n"

## Fast path: refresh /etc/euler/user.env and bounce the running per-user instances
## (their sockets re-activate them against the freshly rebuilt venv; drops live shells)
redeploy-user:
	./scripts/setup/user.sh redeploy
	@printf "✓ redeploy-user complete: per-user config refreshed, instances bounced\n"

## Deploy the full web stack, in dependency order: edge (Caddy+ACME+web-content),
## egress (Squid), DDNS, kernel egress firewall, SMTP relay, auth service (and the
## shared /opt/euler venv), message spool, public site, and the per-user provisioning
## layer. Each kit stays independently operable; the later kits reload the firewall as
## their service users appear. (sudo required)
deploy-web: deploy-frontend deploy-egress deploy-ddns deploy-firewall deploy-smtp deploy-auth deploy-msg deploy-site deploy-user
	@printf "✓ deploy-web complete: full web stack deployed\n"

## Remove the full web stack (reverse order; the kits prompt before deleting state)
remove-web: logout-web remove-user remove-site remove-msg remove-auth remove-smtp remove-firewall remove-ddns remove-egress remove-frontend
	@printf "✓ remove-web complete: full web stack removed\n"

## Upgrade the full web stack in place (regenerate configs, redeploy, restart;
## ddns and firewall have no upgrade action — their deploy is idempotent and doubles
## as one, and the firewall reload is the final consistency pass over the euler uids)
upgrade-web: logout-web upgrade-frontend upgrade-egress deploy-ddns upgrade-smtp upgrade-auth upgrade-msg upgrade-site upgrade-user
	./scripts/setup/firewall.sh reload
	@printf "✓ upgrade-web complete: full web stack upgraded\n"

# What each stopping target's notice says; LOGOUT_REASON on the command line overrides it.
LOGOUT_REASON_redeploy-web := the server is redeploying
LOGOUT_REASON_upgrade-web := the server is upgrading
LOGOUT_REASON_remove-web := the web stack is being removed
LOGOUT_REASON ?= $(LOGOUT_REASON_$(firstword $(MAKECMDGOALS)))

## Warn every running instance, then sign everyone out: a 5s countdown, held by running work
## for up to 5 minutes. redeploy-web, upgrade-web and remove-web run it first. Set the notice
## with LOGOUT_REASON; LOGOUT_ARGS=--now skips the wait.
logout-web:
	./scripts/setup/user.sh logout-all $(if $(LOGOUT_REASON),"$(LOGOUT_REASON)") $(LOGOUT_ARGS)

## Fast redeploy of code, templates, and static assets WITHOUT touching identities,
## ACLs, units, certs, or the firewall: refresh the edge's static content + Caddyfile →
## rebuild the shared venv (auth) → restart the message spool and the public site on it →
## bounce the per-user instances so their sockets re-activate them against it. The edge goes first: the restarted services name each
## asset by a hash of its bytes (?v=, cached immutable), so those bytes must already be
## there. logout-web goes before all of it, so users are warned and signed out first: it
## blocks until their running work ends or the limit passes. Skip the wait with
## LOGOUT_ARGS=--now. The
## everyday "I changed Python/templates/CSS/JS, push it" turnaround. Gated on
## check-version so the deployed build's tag is on origin (run serially — the default —
## so the guard runs before any deploy step).
redeploy-web: check-version logout-web redeploy-frontend redeploy-auth redeploy-msg redeploy-site redeploy-user
	@printf "✓ redeploy-web complete: code, templates, and static assets redeployed\n"

## Report the whole web stack in one read-only sweep, in deploy order: the edge, the
## egress proxy, DDNS, the kernel firewall, the mail relay, the auth service, the
## message spool, and the per-user tier. The last section is the collaborator roster — one entry per unix name
## (the slug: the uid, home, socket and unit instance are all called this) with the
## e-mail as an alias, and per user whether their instance is running and whether their
## web shell is live and currently connected to a terminal.
##
## Read-only, but needs sudo: the state it reads is root-owned (nftables, /etc/euler),
## the clones sit under 0700 homes, and the shell report is a query on each running
## instance's own socket. Each kit is run best-effort so one missing component (or a
## failing egress probe) never truncates the sweep.
status-web:
	@printf "══ edge (Caddy + ACME) ═══════════════════════════════════════════════\n"
	@./scripts/setup/frontend.sh status || true
	@printf "\n══ egress proxy (Squid) ══════════════════════════════════════════════\n"
	@./scripts/setup/egress.sh status || true
	@printf "\n══ dynamic DNS ═══════════════════════════════════════════════════════\n"
	@./scripts/setup/ddns.sh status || true
	@printf "\n══ kernel egress firewall (nftables) ═════════════════════════════════\n"
	@./scripts/setup/firewall.sh status || true
	@printf "\n══ mail relay (loopback SMTP) ════════════════════════════════════════\n"
	@./scripts/setup/smtp.sh status || true
	@printf "\n══ auth service ══════════════════════════════════════════════════════\n"
	@./scripts/setup/auth.sh status || true
	@printf "\n══ message spool (user↔staff threads) ════════════════════════════════\n"
	@./scripts/setup/msg.sh status || true
	@printf "\n══ public site (signed-out content) ══════════════════════════════════\n"
	@./scripts/setup/site.sh status || true
	@printf "\n══ per-user tier (collaborators + their web shells) ══════════════════\n"
	@./scripts/setup/user.sh status || true