docs: update deployment docs and standardize logging (#194)

* docs: update deployment docs and costs for production-fe workflow

- update README costs to ~$25-30/month (2x fly apps + neon)
- fix backend API links to use api.plyr.fm custom domain
- update environments.md for production-fe branch deployment
- document new release workflow (just release merges main → production-fe)
- remove outdated overview.md (referenced old relay branding)
- remove STATUS.md link (not tracked in git)
- fix local development to use justfile commands

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: add hot reloading to transcoder with cargo-watch

use cargo watch for transcoder development to auto-reload on file changes,
matching the hot reload experience of backend (uvicorn --reload) and
frontend (bun run dev)

requires: cargo install cargo-watch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: organize frontend commands into nested justfile

- create frontend/justfile with dev, build, preview, check, install
- remove deploy-frontend from root justfile (deployment via cloudflare pages)
- update docs to use 'just frontend dev' instead of 'just run-frontend'
- matches transcoder pattern with nested justfile modules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: standardize logging with logfire handler

- use LogfireLoggingHandler for consistent log formatting
- remove duplicate logfire.configure() call in database.py
- add per-file import sort ignore for main.py (warnings filter must be first)
- downgrade handle resolution log to debug level
- clean up README deployment instructions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: simplify frontend justfile to only used targets

remove build, preview, install targets - these are never used locally.
cloudflare pages handles builds, and deps are managed directly with bun.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

authored by zzstoatzz.io Claude and committed by GitHub 25b1ef31 ce91960a

Changed files
+129 -199
docs
frontend
src
backend
_internal
utilities
transcoder
+17 -23
README.md
··· 52 52 ```bash 53 53 # install dependencies 54 54 uv sync 55 - cd frontend && bun install 56 - 57 - # run backend 58 - just serve 55 + just frontend install 59 56 60 - # run frontend (new terminal) 61 - just dev 62 - ``` 63 - 64 - or manually: 57 + # run backend (hot reloads) 58 + just run-backend 65 59 66 - ```bash 67 - # backend 68 - uv sync 69 - uv run uvicorn backend.main:app --reload --port 8001 60 + # run frontend (hot reloads) 61 + just frontend dev 70 62 71 - # frontend (new terminal) 72 - cd frontend && bun install && bun run dev 63 + # run transcoder (hot reloads) 64 + just transcoder run 73 65 ``` 74 66 75 67 visit http://localhost:5173 ··· 139 131 <details> 140 132 <summary>costs</summary> 141 133 142 - ~$5-6/month for MVP: 143 - - cloudflare pages: free 144 - - cloudflare r2: ~$0.16 145 - - fly.io: $5 146 - - neon: free 134 + ~$25-30/month: 135 + - fly.io backend (production): ~$10/month (shared-cpu-1x, 256MB RAM) 136 + - fly.io backend (staging): ~$10/month (shared-cpu-1x, 256MB RAM) 137 + - fly.io transcoder: ~$0-5/month (auto-scales to zero when idle) 138 + - neon postgres: $5/month (starter plan) 139 + - cloudflare pages: free (frontend hosting) 140 + - cloudflare r2: ~$0.16/month (6 buckets across dev/staging/prod) 147 141 148 142 </details> 149 143 150 144 ## links 151 145 152 146 - **production**: https://plyr.fm 153 - - **backend API**: https://relay-api.fly.dev 147 + - **backend API**: https://api.plyr.fm 154 148 - **repository**: https://github.com/zzstoatzz/plyr.fm 155 149 156 150 ## documentation 157 151 158 - - [deployment guide](docs/deployment/overview.md) 159 - - [latest status](STATUS.md) 152 + - [deployment guide](docs/deployment/environments.md) 160 153 - [configuration](docs/configuration.md) 154 + - [full documentation](docs/README.md)
+54 -99
docs/deployment/environments.md
··· 4 4 5 5 ## environments 6 6 7 - | environment | trigger | backend app | backend URL | database | frontend | storage | 8 - |-------------|---------|-------------|-------------|----------|----------|---------| 9 - | **development** | local | local server | localhost:8001 | plyr-dev (neon) | localhost:5173 | audio-dev, images-dev (r2) | 10 - | **staging** | push to main | plyr-api-staging | plyr-api-staging.fly.dev | plyr-staging (neon) | cloudflare pages preview | audio-staging, images-staging (r2) | 11 - | **production** | github release | plyr-api | plyr-api.fly.dev | plyr-prod (neon) | plyr.fm (cloudflare pages) | audio-prod, images-prod (r2) | 7 + | environment | trigger | backend URL | database | frontend | storage | 8 + |-------------|---------|-------------|----------|----------|---------| 9 + | **development** | local | localhost:8001 | plyr-dev (neon) | localhost:5173 | audio-dev, images-dev (r2) | 10 + | **staging** | push to main | relay-api-staging.fly.dev | plyr-staging (neon) | cloudflare pages preview (main) | audio-staging, images-staging (r2) | 11 + | **production** | github release | api.plyr.fm | plyr-prod (neon) | plyr.fm (production-fe branch) | audio-prod, images-prod (r2) | 12 12 13 13 ## workflow 14 14 15 15 ### local development 16 16 17 17 ```bash 18 - # start backend 19 - uv run uvicorn backend.main:app --reload --port 8001 18 + # start backend (hot reloads) 19 + just run-backend 20 20 21 - # start frontend 22 - cd frontend && bun run dev 21 + # start frontend (hot reloads) 22 + just frontend dev 23 23 24 - # start transcoder (optional) 25 - cd transcoder && just run 24 + # start transcoder (hot reloads) 25 + just transcoder run 26 26 ``` 27 27 28 28 connects to `plyr-dev` neon database and uses `fm.plyr` atproto namespace. ··· 32 32 **trigger**: push to `main` branch 33 33 34 34 **backend**: 35 - 1. github actions runs `.github/workflows/deploy-backend.yml` 36 - 2. deploys to `plyr-api-staging` fly app using `fly.staging.toml` 37 - 3. runs `alembic upgrade head` via `release_command` 38 - 4. backend available at `https://plyr-api-staging.fly.dev` 35 + 1. github actions runs `.github/workflows/deploy-staging.yml` 36 + 2. runs `alembic upgrade head` via `release_command` 37 + 3. backend available at `https://relay-api-staging.fly.dev` 39 38 40 39 **frontend**: 41 - - cloudflare pages automatically deploys from `main` branch 42 - - uses preview environment with `PUBLIC_API_URL=https://plyr-api-staging.fly.dev` 43 - - available at cloudflare-generated preview URL 40 + - cloudflare pages automatically creates preview builds from `main` branch 41 + - uses preview environment with `PUBLIC_API_URL=https://relay-api-staging.fly.dev` 44 42 45 43 **testing**: 46 - - backend: `https://plyr-api-staging.fly.dev/docs` 44 + - backend: `https://relay-api-staging.fly.dev/docs` 47 45 - database: `plyr-staging` (neon) 48 46 - storage: `audio-staging`, `images-staging` (r2) 49 - - atproto namespace: `fm.plyr` 50 47 51 48 ### production deployment (manual) 52 49 53 - **trigger**: create github release (e.g., `v1.0.0`) 50 + **trigger**: run `just release` (creates github tag, merges main → production-fe) 54 51 55 52 **backend**: 56 - 1. github actions runs `.github/workflows/deploy-production.yml` 57 - 2. deploys to `plyr-api` fly app using `fly.toml` 58 - 3. runs `alembic upgrade head` via `release_command` 59 - 4. backend available at `https://plyr-api.fly.dev` 53 + 1. github actions runs `.github/workflows/deploy-prod.yml` 54 + 2. runs `alembic upgrade head` via `release_command` 55 + 3. backend available at `https://api.plyr.fm` 60 56 61 57 **frontend**: 62 - - cloudflare pages production environment serves `main` branch 63 - - uses production environment with `PUBLIC_API_URL=https://plyr-api.fly.dev` 64 - - available at `https://plyr.fm` (custom domain) 58 + 1. release script merges `main` → `production-fe` branch 59 + 2. cloudflare pages production environment tracks `production-fe` branch 60 + 3. uses production environment with `PUBLIC_API_URL=https://api.plyr.fm` 61 + 4. available at `https://plyr.fm` 65 62 66 63 **creating a release**: 67 64 ```bash 68 65 # after validating changes in staging: 69 66 just release 70 - 71 - # or manually via gh cli: 72 - gh release create v1.0.0 --title "v1.0.0" --notes "release notes here" 73 67 ``` 74 68 75 - or via github UI: releases → draft new release → create tag → publish 69 + this will: 70 + 1. create timestamped github tag (triggers backend deploy) 71 + 2. merge main → production-fe (triggers frontend deploy) 76 72 77 73 **testing**: 78 74 - frontend: `https://plyr.fm` 79 - - backend: `https://plyr-api.fly.dev/docs` 75 + - backend: `https://api.plyr.fm/docs` 80 76 - database: `plyr-prod` (neon) 81 77 - storage: `audio-prod`, `images-prod` (r2) 82 - - atproto namespace: `fm.plyr` 83 78 84 79 ## configuration files 85 80 86 81 ### backend 87 82 88 - **fly.staging.toml**: 89 - - app: `plyr-api-staging` 90 - - release_command: `uv run alembic upgrade head` (runs migrations) 91 - - environment variables configured in fly.io 92 - 93 - **fly.toml**: 94 - - app: `plyr-api` 95 - - release_command: `uv run alembic upgrade head` (runs migrations) 96 - - environment variables configured in fly.io 97 - 98 - **transcoder/fly.toml**: 99 - - app: `plyr-transcoder` 100 - - auto-scaling enabled (stops when idle) 101 - - environment variables configured in fly.io 83 + **fly.staging.toml** / **fly.toml**: 84 + - release_command: `uv run alembic upgrade head` (runs migrations before deploy) 85 + - environment variables configured via `flyctl secrets set` 102 86 103 87 ### frontend 104 88 ··· 106 90 - framework: sveltekit 107 91 - build command: `cd frontend && bun run build` 108 92 - build output: `frontend/build` 93 + - production branch: `production-fe` 94 + - preview branch: `main` 109 95 - environment variables: 110 - - preview: `PUBLIC_API_URL=https://plyr-api-staging.fly.dev` 111 - - production: `PUBLIC_API_URL=https://plyr-api.fly.dev` 96 + - preview: `PUBLIC_API_URL=https://relay-api-staging.fly.dev` 97 + - production: `PUBLIC_API_URL=https://api.plyr.fm` 112 98 113 99 ### secrets management 114 100 115 - **staging secrets** (set via `flyctl secrets set`): 116 - - `DATABASE_URL` → neon staging connection string 117 - - `ATPROTO_CLIENT_ID` → `https://plyr-api-staging.fly.dev/client-metadata.json` 118 - - `ATPROTO_REDIRECT_URI` → `https://plyr-api-staging.fly.dev/auth/callback` 119 - - `ATPROTO_APP_NAMESPACE` → `fm.plyr` 120 - - `OAUTH_ENCRYPTION_KEY` → unique 44-char base64 fernet key 101 + all secrets configured via `flyctl secrets set`. key environment variables: 102 + - `DATABASE_URL` → neon connection string (env-specific) 103 + - `ATPROTO_CLIENT_ID`, `ATPROTO_REDIRECT_URI` → oauth config (env-specific URLs) 104 + - `OAUTH_ENCRYPTION_KEY` → unique per environment 121 105 - `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` → r2 credentials 122 - - `LOGFIRE_WRITE_TOKEN`, `LOGFIRE_ENABLED`, `LOGFIRE_ENVIRONMENT` 123 - - `FRONTEND_URL` → cloudflare pages preview URL 124 - - `TRANSCODER_URL` → `https://plyr-transcoder.fly.dev` 125 - - `TRANSCODER_AUTH_TOKEN` → shared secret for transcoder auth 126 - 127 - **production secrets** (already configured): 128 - - same structure but with production URLs and database 129 - - `ATPROTO_APP_NAMESPACE` → `fm.plyr` 130 - - `FRONTEND_CORS_ORIGIN_REGEX` → `^https://(www\.)?plyr\.fm$` 131 - - additional: `NOTIFY_BOT_HANDLE`, `NOTIFY_BOT_PASSWORD`, `NOTIFY_ENABLED` 132 - 133 - **transcoder secrets**: 134 - - `TRANSCODER_AUTH_TOKEN` → shared with backend for authentication 135 - 136 - **local dev (.env)**: 137 - - `ATPROTO_APP_NAMESPACE` → `fm.plyr` 106 + - `LOGFIRE_WRITE_TOKEN`, `LOGFIRE_ENVIRONMENT` → observability config 138 107 139 108 ## database migrations 140 109 ··· 157 126 158 127 **staging**: 159 128 - logfire: environment filter `LOGFIRE_ENVIRONMENT=staging` 160 - - backend logs: `flyctl logs -a plyr-api-staging` 161 - - transcoder logs: `flyctl logs -a plyr-transcoder` 129 + - backend logs: `flyctl logs -a relay-api-staging` 162 130 163 131 **production**: 164 132 - logfire: environment filter `LOGFIRE_ENVIRONMENT=production` 165 - - backend logs: `flyctl logs -a plyr-api` 166 - - transcoder logs: `flyctl logs -a plyr-transcoder` 133 + - backend logs: `flyctl logs -a relay-api` 167 134 168 135 ## costs 169 136 170 - **current**: ~$15-20/month 171 - - fly.io backend (production): $5-10/month (shared-cpu-1x, 256MB RAM) 172 - - fly.io backend (staging): $5-10/month (shared-cpu-1x, 256MB RAM) 173 - - fly.io transcoder: $0-5/month (auto-scales to zero when idle, 1GB RAM) 174 - - neon dev: free tier (0.5GB storage) 175 - - neon staging: free tier (0.5GB storage) 176 - - neon production: free tier (0.5GB storage, 3GB data transfer) 137 + **current**: ~$25-30/month 138 + - fly.io backend (production): ~$10/month (shared-cpu-1x, 256MB RAM) 139 + - fly.io backend (staging): ~$10/month (shared-cpu-1x, 256MB RAM) 140 + - fly.io transcoder: ~$0-5/month (auto-scales to zero when idle) 141 + - neon postgres: $5/month (starter plan) 177 142 - cloudflare pages: free (frontend hosting) 178 - - cloudflare R2: ~$0.16/month (6 buckets: audio-dev, audio-staging, audio-prod, images-dev, images-staging, images-prod) 179 - 180 - ## benefits 143 + - cloudflare R2: ~$0.16/month (6 buckets across dev/staging/prod) 181 144 182 - - **safe testing**: catch bugs in staging before production 183 - - **migration validation**: test database changes in production-like environment 184 - - **rollback capability**: releases enable version-based rollbacks via github 185 - - **clear release process**: explicit versioning via github releases 186 - - **single branch**: no branch management - just `main` and feature branches 145 + ## workflow summary 187 146 188 - ## deployment history 189 - 190 - 1. ✅ production backend deployed (plyr-api.fly.dev) 191 - 2. ✅ production frontend deployed (plyr.fm) 192 - 3. ✅ transcoder service deployed (plyr-transcoder.fly.dev) 193 - 4. ✅ staging environment configured (plyr-api-staging.fly.dev) 194 - 5. ✅ automated deployments via github actions 195 - 6. ✅ database migrations automated via fly.io release_command 147 + - **merge PR to main**: deploys staging backend, creates frontend preview 148 + - **run `just release`**: deploys production backend + production frontend together 149 + - **database migrations**: run automatically before deploy completes 150 + - **rollback**: revert github release or restore database from neon backup
-37
docs/deployment/overview.md
··· 1 - # deployment overview 2 - 3 - relay uses a three-tier deployment strategy. see [`environments.md`](./environments.md) for complete details. 4 - 5 - ## quick reference 6 - 7 - | environment | frontend | backend | trigger | 8 - |-------------|----------|---------|---------| 9 - | **staging** | staging.relay-4i6.pages.dev | relay-api-staging.fly.dev | push to `staging` branch (frontend) <br> push to `main` (backend) | 10 - | **production** | relay-4i6.pages.dev | relay-api.fly.dev | push to `production` branch (frontend) <br> github release (backend) | 11 - 12 - ## deployment workflow 13 - 14 - ### staging (automatic testing) 15 - 1. push backend changes to `main` → deploys to relay-api-staging 16 - 2. push frontend changes to `staging` branch → deploys to staging.relay-4i6.pages.dev 17 - 3. test end-to-end before promoting to production 18 - 19 - ### production (manual release) 20 - 1. validate changes in staging 21 - 2. create github release → deploys backend to relay-api 22 - 3. merge to `production` branch → deploys frontend to relay-4i6.pages.dev 23 - 24 - ## key files 25 - 26 - - backend configs: `fly.toml` (production), `fly.staging.toml` (staging) 27 - - backend deploy: `.github/workflows/deploy-backend.yml`, `.github/workflows/deploy-production.yml` 28 - - frontend: cloudflare pages (configured via UI) 29 - - migrations: see [`database-migrations.md`](./database-migrations.md) 30 - 31 - ## monitoring 32 - 33 - - logfire: filter by `deployment_environment` (staging/production) 34 - - fly.io: `flyctl logs -a relay-api` or `flyctl logs -a relay-api-staging` 35 - - neon: separate databases per environment (relay-dev, relay-staging, relay) 36 - 37 - for complete details, see [`environments.md`](./environments.md).
+10
frontend/justfile
··· 1 + set shell := ["bash", "-eu", "-o", "pipefail", "-c"] 2 + default := "dev" 3 + 4 + # run frontend dev server (hot reloads) 5 + dev: 6 + cd {{justfile_directory()}}/frontend && bun run dev --host 0.0.0.0 7 + 8 + # check svelte types 9 + check: 10 + cd {{justfile_directory()}}/frontend && bun run check
+2 -9
justfile
··· 1 1 # plyr.fm dev workflows 2 + mod frontend 2 3 mod transcoder 3 4 4 5 ··· 7 8 @just --list 8 9 9 10 10 - # run backend server 11 + # run backend server (hot reloads) 11 12 run-backend: 12 13 uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port ${PORT:-8001} 13 - 14 - # run frontend dev server 15 - run-frontend: 16 - cd frontend && bun run dev --host 0.0.0.0 17 14 18 15 # run tests with docker-compose 19 16 test *ARGS='tests/': ··· 37 34 migrate-status: 38 35 uv run alembic current 39 36 40 - 41 - # deploy frontend to cloudflare pages 42 - deploy-frontend: 43 - cd frontend && bun run build && bun x wrangler pages deploy .svelte-kit/cloudflare 44 37 45 38 # show commits since last release 46 39 changelog:
+19 -17
pyproject.toml
··· 30 30 readme = "README.md" 31 31 license = "Apache-2.0" 32 32 33 - keywords = [ 34 - "music", 35 - "streaming", 36 - "atproto", 37 - "bluesky", 38 - "decentralized", 39 - ] 33 + keywords = ["music", "streaming", "atproto", "bluesky", "decentralized"] 40 34 41 35 [dependency-groups] 42 36 dev = [ ··· 87 81 python_files = ["test_*.py", "*_test.py"] 88 82 python_classes = ["Test*"] 89 83 python_functions = ["test_*"] 90 - filterwarnings = [ 91 - "ignore::pydantic.warnings.UnsupportedFieldAttributeWarning", 92 - ] 84 + filterwarnings = ["ignore::pydantic.warnings.UnsupportedFieldAttributeWarning"] 93 85 94 86 [tool.ruff.lint] 95 87 fixable = ["ALL"] 96 88 ignore = [ 97 89 "COM812", 98 90 "PLR0913", # Too many arguments 99 - "SIM102", # Dont require combining if statements 91 + "SIM102", # Dont require combining if statements 100 92 ] 101 93 extend-select = [ 102 - "B", # flake8-bugbear 103 - "C4", # flake8-comprehensions 104 - "I", # isort 94 + "B", # flake8-bugbear 95 + "C4", # flake8-comprehensions 96 + "I", # isort 105 97 "PIE", # flake8-pie 106 98 "RUF", # Ruff-specific 107 99 "SIM", # flake8-simplify 108 - "UP", # pyupgrade 100 + "UP", # pyupgrade 109 101 ] 110 102 111 103 [tool.ruff.lint.per-file-ignores] 112 104 "__init__.py" = ["F401", "I001"] 113 105 "tests/**/*.py" = ["S101"] # Allow assert in tests 114 - "src/backend/api/**/*.py" = ["B008"] # Allow Depends() and File() in FastAPI route defaults 106 + "src/backend/api/**/*.py" = [ 107 + "B008", 108 + ] # Allow Depends() and File() in FastAPI route defaults 109 + "src/backend/main.py" = ["E402"] # Need warnings filter before imports 115 110 116 111 [tool.coverage.run] 117 112 source = ["src"] ··· 129 124 130 125 [tool.ty.src] 131 126 include = ["src", "tests"] 132 - exclude = ["**/node_modules", "**/__pycache__", ".venv", ".git", "dist", "frontend"] 127 + exclude = [ 128 + "**/node_modules", 129 + "**/__pycache__", 130 + ".venv", 131 + ".git", 132 + "dist", 133 + "frontend", 134 + ] 133 135 134 136 [tool.ty.environment] 135 137 python-version = "3.11"
+1 -1
src/backend/_internal/notifications.py
··· 61 61 {"actor": settings.notify.recipient_handle} 62 62 ) 63 63 self.recipient_did = profile.did 64 - logger.info( 64 + logger.debug( 65 65 f"resolved {settings.notify.recipient_handle} to {self.recipient_did}" 66 66 ) 67 67
+25 -8
src/backend/main.py
··· 3 3 import asyncio 4 4 import contextlib 5 5 import logging 6 + import warnings 6 7 from collections.abc import AsyncIterator 7 8 from contextlib import asynccontextmanager 8 9 9 10 from fastapi import FastAPI 10 11 from fastapi.middleware.cors import CORSMiddleware 12 + 13 + # filter pydantic warning from atproto library 14 + warnings.filterwarnings( 15 + "ignore", 16 + message="The 'default' attribute with value None was provided to the `Field\\(\\)` function", 17 + category=UserWarning, 18 + module="pydantic._internal._generate_schema", 19 + ) 11 20 12 21 from backend._internal import notification_service, queue_service 13 22 from backend.api import ( ··· 22 31 from backend.api.migration import router as migration_router 23 32 from backend.config import settings 24 33 from backend.models import init_db 25 - 26 - # configure logging level based on debug mode 27 - logging.basicConfig( 28 - level=logging.DEBUG if settings.app.debug else logging.INFO, 29 - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 30 - ) 31 - 32 - logger = logging.getLogger(__name__) 33 34 34 35 # configure logfire if enabled 35 36 if settings.observability.enabled: ··· 42 43 token=settings.observability.write_token, 43 44 environment=settings.observability.environment, 44 45 ) 46 + 47 + # configure logging with logfire handler 48 + logging.basicConfig( 49 + level=logging.DEBUG if settings.app.debug else logging.INFO, 50 + handlers=[logfire.LogfireLoggingHandler()], 51 + ) 45 52 else: 46 53 logfire = None 54 + # fallback to basic logging when logfire is disabled 55 + logging.basicConfig( 56 + level=logging.DEBUG if settings.app.debug else logging.INFO, 57 + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 58 + ) 59 + 60 + # # reduce noise from verbose loggers 61 + # logging.getLogger("httpx").setLevel(logging.WARNING) 62 + 63 + logger = logging.getLogger(__name__) 47 64 48 65 49 66 async def run_periodic_tasks():
-4
src/backend/utilities/database.py
··· 54 54 if settings.observability.enabled: 55 55 import logfire 56 56 57 - logfire.configure( 58 - token=settings.observability.write_token, 59 - environment=settings.observability.environment, 60 - ) 61 57 logfire.instrument_sqlalchemy(engine.sync_engine) 62 58 63 59 ENGINES[cache_key] = engine
+1 -1
transcoder/Justfile
··· 8 8 cd {{justfile_directory()}}/transcoder && \ 9 9 TRANSCODER_HOST="${TRANSCODER_HOST:-127.0.0.1}" \ 10 10 TRANSCODER_PORT="${TRANSCODER_PORT:-8082}" \ 11 - cargo run 11 + cargo watch -x run 12 12 13 13 build: 14 14 cd {{justfile_directory()}}/transcoder && cargo build --release