# Deploy Runbook Two things live here: deploying the **MuseHub server** and publishing a new **Muse CLI build**. --- ## 0 — Build frontend assets (required before deploy) The Docker image copies compiled assets directly from the working tree. `app.css` and `app.js` are in `.museignore` (not committed), so they must be built locally before running `push.sh`. ```bash cd ~/ecosystem/musehub npm run build # compiles src/scss → app.css and src/ts → app.js ``` Or rebuild only what changed: ```bash npm run build:css # SCSS only (src/scss/app.scss → musehub/templates/musehub/static/app.css) npm run build:js # TS only (src/ts/app.ts → musehub/templates/musehub/static/app.js) ``` **Source files to edit** (never edit the compiled output directly): | Asset | Source | |-------|--------| | `static/app.css` | `src/scss/` — entry point `app.scss`, partials `_*.scss` | | `static/app.js` | `src/ts/` — entry point `app.ts` | | `static/embed.css` | `src/scss/embed.scss` | | `static/embed-player.js` | `src/ts/embed-player.ts` | During local development, use the watch commands to rebuild automatically on save: ```bash npm run watch:css # rebuilds app.css on every SCSS change npm run watch:js # rebuilds app.js on every TS change ``` --- ## 1 — Deploy MuseHub (server) ```bash bash deploy/push.sh staging # build, push to ECR, blue-green deploy bash deploy/push.sh prod # same for prod bash deploy/push.sh staging prod # staging then prod in sequence IMAGE_TAG= bash deploy/push.sh staging # rollback to a prior image ``` `push.sh` does a full blue-green deploy: builds a Docker image for `linux/amd64`, pushes it to ECR, starts the inactive slot, health-checks `/healthz`, switches nginx, and stops the old slot. The image tag is `-`. For full infrastructure details (instance IDs, slot switching, SSM recovery) see [infrastructure.md](infrastructure.md). --- ## 2 — Publish a new Muse CLI build ```bash bash deploy/publish_muse_release.sh ``` Run from the `musehub` repo root. It reads the version from `~/ecosystem/muse/pyproject.toml` automatically. **What it does:** 1. Builds `muse-.tar.gz` from `~/ecosystem/muse` using `python3 -m build` 2. Uploads the tarball to `s3://musehub-releases/` 3. SSMs into staging and copies it to `/data/releases/` on the instance volume 4. Prunes S3 and the instance to keep only the 3 most recent tarballs 5. Smoke-tests `https://staging.musehub.ai/releases/muse-.tar.gz` — exits non-zero on failure After a successful run, the new version is live and anyone running the installer gets it: ```bash curl -fsSL https://staging.musehub.ai/install.sh | sh ``` ### Prerequisites - Python 3.14 + `build` package: `pip install build` - AWS CLI configured with credentials that can write to `s3://musehub-releases` and send SSM commands to `i-07547cd20bee2dea5` ### Override version label ```bash MUSE_VERSION=0.2.1 bash deploy/publish_muse_release.sh ``` Useful if you need to re-publish a tarball under a different version label without editing `pyproject.toml`. ### Standard release flow ```bash # 1. Bump version in both pyproject.toml files (muse + musehub), commit, merge to main, push # 2. Deploy the new MuseHub server (so install.sh serves the right version string) bash deploy/push.sh staging # 3. Publish the Muse CLI tarball bash deploy/publish_muse_release.sh # 4. Verify curl -fsSL https://staging.musehub.ai/install.sh | sh muse --version ``` Steps 2 and 3 are independent but 2 should come first — `install.sh` is generated dynamically from `MUSE_VERSION` in `musehub/protocol/version.py`, so the server must be running the new version before the tarball is published.