MOBUX_PORT ?= 5151
MOBUX_DEV_PORT ?= 5152
MOBUX_SMOKE_PORT ?= 8281
MOBUX_USER ?= $(USER)
MOBUX_PIN ?= 30879
CARGO := $(HOME)/.cargo/bin/cargo
MOBUX_PACKAGE_ID ?= io.github.mvhenten.mobux
MOBUX_APP_NAME ?= Mobux
TWA_INSTALL_DIR ?= web/static/install
TWA_WELLKNOWN_DIR ?= web/static/.well-known
MOBUX_DEV_DOMAIN ?= sandbox:5152
PID := $(shell lsof -ti :$(MOBUX_PORT) 2>/dev/null)
SMOKE_PID := $(shell lsof -ti :$(MOBUX_SMOKE_PORT) 2>/dev/null)
.PHONY: build run dev dev-watch _dev-bounce clean start stop restart status logs test web setup setup-twa twa twa-dev \
transcribe setup-transcribe \
smoke-start smoke-stop smoke-logs smoke-status \
test-smoke test-critical-path test-update-runner test-spa test-reader test-reader-grouping test-stt-ux test-stt-per-kind test-e2e \
podman-build podman-run podman-stop podman-test stt-install
PODMAN_IMAGE ?= localhost/mobux:dev
PODMAN_PORT ?= 8381
PODMAN_NAME ?= mobux-podman
WHISPER_DIR ?= $(HOME)/.local/whisper.cpp
WHISPER_MODEL_NAME ?= base.en
setup:
./bin/setup
setup-twa:
./bin/setup-twa
transcribe:
@if [ -z "$(FILE)" ]; then echo "usage: make transcribe FILE=<audio-or-video-file>" >&2; exit 2; fi
@WHISPER_DIR="$(WHISPER_DIR)" ./bin/transcribe "$(FILE)"
setup-transcribe:
@command -v ffmpeg >/dev/null 2>&1 || { echo "ffmpeg is required (apt install ffmpeg)"; exit 1; }
@if [ ! -d "$(WHISPER_DIR)" ]; then \
git clone --depth 1 https://github.com/ggerganov/whisper.cpp "$(WHISPER_DIR)"; \
fi
cmake -B "$(WHISPER_DIR)/build" -S "$(WHISPER_DIR)" -DCMAKE_BUILD_TYPE=Release
cmake --build "$(WHISPER_DIR)/build" -j --config Release
bash "$(WHISPER_DIR)/models/download-ggml-model.sh" "$(WHISPER_MODEL_NAME)" "$(WHISPER_DIR)/models"
@echo "whisper.cpp ready at $(WHISPER_DIR) (model: $(WHISPER_MODEL_NAME))"
web:
node web/build.js
stt-install:
@bin/stt-install
clean:
$(CARGO) clean -p mobux
build: web
$(CARGO) build
run: build
env MOBUX_AUTH_USER=$(MOBUX_USER) MOBUX_PIN=$(MOBUX_PIN) PORT=$(MOBUX_PORT) \
$(CARGO) run
dev: build
env MOBUX_AUTH_USER=$(MOBUX_USER) MOBUX_PIN=$(MOBUX_PIN) MOBUX_DEV=1 PORT=$(MOBUX_DEV_PORT) \
$(CARGO) run
dev-watch: build
cargo watch -w src -w Cargo.toml -x build -s '$(MAKE) _dev-bounce'
_dev-bounce:
-@kill $$(lsof -ti :$(MOBUX_DEV_PORT)) 2>/dev/null || true
@sleep 1
@nohup env MOBUX_AUTH_USER=$(MOBUX_USER) MOBUX_PIN=$(MOBUX_PIN) MOBUX_DEV=1 PORT=$(MOBUX_DEV_PORT) \
./target/debug/mobux > /tmp/mobux-dev.log 2>&1 &
@sleep 2 && lsof -i :$(MOBUX_DEV_PORT) >/dev/null 2>&1 \
&& echo "dev :$(MOBUX_DEV_PORT) rebuilt + restarted" || echo "FAILED to restart :$(MOBUX_DEV_PORT)"
spa-dev:
@cd web/spa && env MOBUX_BACKEND=https://127.0.0.1:$(MOBUX_DEV_PORT) \
MOBUX_DEV_AUTH='$(MOBUX_USER):$(MOBUX_PIN)' \
npm run dev -- --host 0.0.0.0
DEV_LOG_DIR ?= $(HOME)/development/.tmp/mobux-dev
dev-up: build
@mkdir -p $(DEV_LOG_DIR)
@if lsof -i :$(MOBUX_DEV_PORT) >/dev/null 2>&1; then echo "backend :$(MOBUX_DEV_PORT) already up"; else \
setsid nohup env MOBUX_AUTH_USER=$(MOBUX_USER) MOBUX_PIN=$(MOBUX_PIN) MOBUX_DEV=1 \
PORT=$(MOBUX_DEV_PORT) ./target/debug/mobux \
> $(DEV_LOG_DIR)/backend.log 2>&1 < /dev/null & \
fi
@if lsof -i :5173 >/dev/null 2>&1; then echo "spa :5173 already up"; else \
cd web/spa && setsid nohup env MOBUX_BACKEND=https://127.0.0.1:$(MOBUX_DEV_PORT) \
MOBUX_DEV_AUTH='$(MOBUX_USER):$(MOBUX_PIN)' \
npm run dev -- --host 0.0.0.0 \
> $(DEV_LOG_DIR)/spa.log 2>&1 < /dev/null & \
fi
@sleep 6
@$(MAKE) --no-print-directory dev-status
dev-down:
-@kill $$(lsof -ti :$(MOBUX_DEV_PORT)) 2>/dev/null || true
-@kill $$(lsof -ti :5173) 2>/dev/null || true
@echo "dev servers stopped"
dev-status:
@lsof -i :$(MOBUX_DEV_PORT) >/dev/null 2>&1 && echo "backend :$(MOBUX_DEV_PORT) up" || echo "backend :$(MOBUX_DEV_PORT) DOWN"
@lsof -i :5173 >/dev/null 2>&1 && echo "spa :5173 up -> https://$(shell hostname):5173/static/spa/" || echo "spa :5173 DOWN"
dev-logs:
@tail -f $(DEV_LOG_DIR)/backend.log $(DEV_LOG_DIR)/spa.log
start: build
@if [ -n "$(PID)" ]; then echo "already running (pid $(PID))"; exit 1; fi
nohup env MOBUX_AUTH_USER=$(MOBUX_USER) MOBUX_PIN=$(MOBUX_PIN) PORT=$(MOBUX_PORT) \
./target/debug/mobux > /tmp/mobux.log 2>&1 &
@sleep 2 && lsof -i :$(MOBUX_PORT) >/dev/null 2>&1 && echo "mobux running on port $(MOBUX_PORT)" || echo "FAILED to start"
stop:
@if [ -z "$(PID)" ]; then echo "not running"; exit 0; fi
kill $(PID) && echo "stopped (pid $(PID))"
restart: stop
@sleep 2
@$(MAKE) start
status:
@if [ -n "$(PID)" ]; then echo "running (pid $(PID)) on port $(MOBUX_PORT)"; else echo "not running"; fi
logs:
@tail -f /tmp/mobux.log
smoke-start: build
@if [ -n "$(SMOKE_PID)" ]; then echo "smoke already running (pid $(SMOKE_PID)) on $(MOBUX_SMOKE_PORT)"; exit 1; fi
@if [ "$(MOBUX_SMOKE_PORT)" = "$(MOBUX_PORT)" ]; then echo "MOBUX_SMOKE_PORT must differ from MOBUX_PORT"; exit 1; fi
@mkdir -p /tmp/mobux-smoke/home
@nohup env MOBUX_DATA_DIR=/tmp/mobux-smoke MOBUX_TLS=0 \
HOME=/tmp/mobux-smoke/home HISTFILE=/dev/null \
MOBUX_TMUX_SOCKET=mobux-test \
MOBUX_UPDATE_TEST_INDEX='{"name":"mobux","vers":"999.0.0","yanked":false}' \
MOBUX_UPDATE_CHECK_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT)/api/update/test-index \
MOBUX_UPDATE_DISABLE_RUN=1 \
PORT=$(MOBUX_SMOKE_PORT) MOBUX_AUTH_USER=smoke MOBUX_PIN=00000 \
./target/debug/mobux > /tmp/mobux-smoke/mobux.log 2>&1 < /dev/null &
@sleep 2 && lsof -i :$(MOBUX_SMOKE_PORT) >/dev/null 2>&1 \
&& echo "smoke mobux running on port $(MOBUX_SMOKE_PORT) (data /tmp/mobux-smoke)" \
|| { echo "smoke FAILED to start"; tail /tmp/mobux-smoke/mobux.log; exit 1; }
smoke-stop:
@if [ -n "$(SMOKE_PID)" ]; then kill $(SMOKE_PID) && echo "smoke stopped (pid $(SMOKE_PID))"; else echo "smoke not running"; fi
@env -u TMUX -u TMUX_PANE tmux -L mobux-test kill-server 2>/dev/null || true
smoke-logs:
@tail -f /tmp/mobux-smoke/mobux.log
smoke-status:
@if [ -n "$(SMOKE_PID)" ]; then echo "smoke running (pid $(SMOKE_PID)) on port $(MOBUX_SMOKE_PORT)"; else echo "smoke not running"; fi
test:
MOBUX_USER=$(MOBUX_USER) MOBUX_PASS=$(MOBUX_PIN) npx playwright test
.PHONY: test-smoke
test-smoke:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/smoke.spec.cjs
.PHONY: test-critical-path
test-critical-path:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/critical-path.spec.cjs
.PHONY: test-update-runner
test-update-runner:
@command -v shellcheck >/dev/null 2>&1 && shellcheck src/update_runner.sh test/update-runner.test.sh || echo "shellcheck not installed; skipping lint"
@bash test/update-runner.test.sh
.PHONY: test-stt-ux
test-stt-ux:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_STT_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_STT_USER=smoke MOBUX_STT_PASS=00000 \
npx playwright test test/stt-ux.spec.cjs
.PHONY: test-stt-per-kind
test-stt-per-kind:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_STT_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_STT_USER=smoke MOBUX_STT_PASS=00000 \
npx playwright test test/stt-per-kind.spec.cjs
.PHONY: test-spa
test-spa:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/spa.spec.cjs test/reader-font.spec.cjs test/reader-command-grouping.spec.cjs test/session-history.spec.cjs test/read-mode-render.spec.cjs test/read-mode.spec.cjs
.PHONY: test-reader
test-reader:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/reader-font.spec.cjs
.PHONY: test-reader-grouping
test-reader-grouping:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/reader-command-grouping.spec.cjs
.PHONY: test-read-mode-render
test-read-mode-render:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/read-mode-render.spec.cjs
.PHONY: test-read-mode
test-read-mode:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/read-mode.spec.cjs
.PHONY: test-session-history
test-session-history:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/session-history.spec.cjs
.PHONY: test-conformance
test-conformance:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test test/conformance.spec.cjs
.PHONY: test-fleet
test-fleet: build
npx playwright test test/fleet
.PHONY: test-e2e
test-e2e:
@$(MAKE) smoke-start
@trap '$(MAKE) smoke-stop' EXIT; \
MOBUX_URL=http://127.0.0.1:$(MOBUX_SMOKE_PORT) \
MOBUX_USER=smoke MOBUX_PASS=00000 \
npx playwright test
podman-build:
podman build -t $(PODMAN_IMAGE) -f Containerfile .
podman-run: podman-build
-@podman rm -f $(PODMAN_NAME) >/dev/null 2>&1
@podman run -d --name $(PODMAN_NAME) -p $(PODMAN_PORT):8080 \
-e MOBUX_AUTH_USER=test -e MOBUX_PIN=00000 \
$(PODMAN_IMAGE) >/dev/null
@echo "mobux running in container on http://localhost:$(PODMAN_PORT) (test/00000)"
podman-stop:
-@podman rm -f $(PODMAN_NAME) >/dev/null 2>&1 && echo "stopped $(PODMAN_NAME)" || echo "not running"
podman-test: podman-build
-@podman rm -f $(PODMAN_NAME) >/dev/null 2>&1
@podman run -d --name $(PODMAN_NAME) -p $(PODMAN_PORT):8080 \
-e MOBUX_AUTH_USER=test -e MOBUX_PIN=00000 \
$(PODMAN_IMAGE) >/dev/null
@trap 'podman rm -f $(PODMAN_NAME) >/dev/null 2>&1' EXIT; \
for i in $$(seq 1 30); do \
if curl -fsS -u test:00000 -o /dev/null http://localhost:$(PODMAN_PORT)/ 2>/dev/null; then break; fi; \
sleep 0.5; \
done; \
MOBUX_URL=http://localhost:$(PODMAN_PORT) \
MOBUX_USER=test MOBUX_PASS=00000 \
MOBUX_TEST_TMUX="podman exec $(PODMAN_NAME) tmux" \
npx playwright test
twa:
@env MOBUX_PACKAGE_ID=$(MOBUX_PACKAGE_ID) \
MOBUX_APP_NAME=$(MOBUX_APP_NAME) \
TWA_WORK_DIR=twa \
TWA_INSTALL_DIR=$(TWA_INSTALL_DIR) \
TWA_WELLKNOWN_DIR=$(TWA_WELLKNOWN_DIR) \
./bin/twa-build
twa-dev:
@$(MAKE) twa \
MOBUX_DOMAIN=$(MOBUX_DEV_DOMAIN) \
MOBUX_PACKAGE_ID=io.github.mvhenten.mobux.dev \
MOBUX_APP_NAME="Mobux Dev" \
TWA_INSTALL_DIR=twa/dist-dev/install \
TWA_WELLKNOWN_DIR=twa/dist-dev/.well-known