set dotenv-load := true set shell := ["bash", "-euo", "pipefail", "-c"] local_url := "http://localhost:" + env_var_or_default("PORT", "8010") production_url := env_var_or_default("URL", "https://chart.amow.com") # List available project commands. default: @just --list # Start the local stack in the background. up: docker compose up -d # Build images and run the local stack in the foreground. dev: docker compose up --build # Build or rebuild local images. build: docker compose build # Recreate the API container after environment or dependency changes. restart: docker compose up -d --force-recreate api # Stop and remove the local stack. down: docker compose down # Show local service state. ps: docker compose ps # Validate and print the resolved Compose configuration. compose-config: docker compose config # Follow service logs; e.g. `just logs api 20m`. logs service="api" since="30m": docker compose logs --follow --since "{{since}}" "{{service}}" # Open a shell in the API container. shell: docker compose exec api sh # Copy .env.example only when .env does not exist. env: @if [[ -e .env ]]; then echo ".env already exists"; else cp .env.example .env && echo "created .env"; fi # Install local non-Docker development dependencies into .venv. venv: python3 -m venv .venv .venv/bin/pip install -r requirements.txt -r requirements-dev.txt # Run the app without Docker after `just venv`. serve: .venv/bin/uvicorn main:app --reload # Run all backend tests, or one pytest path/selector. test selector="": @if [[ -n "{{selector}}" ]]; then docker compose exec -T api python -m pytest -q "{{selector}}"; else docker compose exec -T api python -m pytest -q; fi # Run all browser tests, or files matching a filter such as `trendline`. e2e filter="": ./bin/e2e "{{filter}}" # Run complete backend and browser suites. test-all: test e2e # Check patches for whitespace errors. check: git diff --check # Require a clean committed worktree. clean: @if [[ -n "$(git status --porcelain)" ]]; then echo "worktree is not clean; commit intended changes first" >&2; exit 1; fi # Run every required pre-deploy check. predeploy: check test-all # Capture the local chart through the Playwright service. screenshot file="chart.png": docker compose exec -T playwright playwright screenshot --lang en-US --wait-for-timeout 5000 http://api:8000 "/artifacts/{{file}}" # Show local API status. status: curl -fsS "{{local_url}}/api/status" # Replay Yahoo history through the alert calibration sweep. calibrate: docker compose exec -T api python -m scripts.calibrate_alerts # Validate existing Schwab credentials, or print the authorization URL. schwab-check redirect_url="": @if [[ -n "{{redirect_url}}" ]]; then docker compose exec -T api python -m scripts.check_schwab --redirect-url "{{redirect_url}}"; else docker compose exec -T api python -m scripts.check_schwab; fi # Confirm Schwab stream messages for a duration and symbol. stream seconds="60" symbol="/ES": docker compose exec -T api python -m scripts.check_stream "{{seconds}}" "{{symbol}}" # Wait until production serves local HEAD. wait-deploy url=production_url timeout="300": URL="{{url}}" TIMEOUT="{{timeout}}" ./bin/wait-deploy # Check public production health and deployed version. smoke url=production_url: curl -fsS "{{url}}/api/health" curl -fsS "{{url}}/api/version" # Check authenticated production status using TOKEN from the environment. production-status url=production_url: @if [[ -z "${TOKEN:-}" ]]; then echo "TOKEN is required" >&2; exit 1; fi; curl -fsS -H "X-Chart-Token: $TOKEN" "{{url}}/api/status" # Require a clean tree, run all tests, push main, wait, and smoke test. deploy: clean predeploy git push origin main ./bin/wait-deploy just smoke