feat(staging): add isolated orchestrator-staging service (port 8501, separate DB)
- Add orchestrator-staging compose service under profile 'staging' so normal 'docker compose up -d' does NOT start it. - Port 8501 via command override; network_mode: host (no ports mapping needed). - DB isolation via separate volume ./data/staging:/app/data — physically separate from prod ./data/orchestrator.db on the host. - ORCH_DB_PATH=/app/data/orchestrator.db explicit in env (same container path, isolated by volume mount). - Add .env.staging.example with all required keys and placeholders. - Update .gitignore: add .env.staging and data/staging/ exclusions. - Add docs/STAGING.md: how to start staging, architecture table, roadmap. Refs: ORCH-31 (Stage 1 of 5)
This commit is contained in:
52
.env.staging.example
Normal file
52
.env.staging.example
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# STAGING env for orchestrator-staging (port 8501).
|
||||||
|
# Plane/Gitea tokens and sandbox project — configured in ORCH-32.
|
||||||
|
# On Stage 1 (ORCH-31) you can copy from prod .env, changing only isolation-related keys.
|
||||||
|
#
|
||||||
|
# DO NOT COMMIT the real .env.staging — this file is the template only.
|
||||||
|
# Create .env.staging on the server and fill in real values before starting staging.
|
||||||
|
|
||||||
|
# ── Plane ─────────────────────────────────────────────────────────────────────
|
||||||
|
ORCH_PLANE_API_URL=http://localhost:8091
|
||||||
|
ORCH_PLANE_API_TOKEN=<plane-api-token>
|
||||||
|
ORCH_PLANE_WORKSPACE_SLUG=<workspace-slug>
|
||||||
|
ORCH_PLANE_WEBHOOK_SECRET=<webhook-secret>
|
||||||
|
|
||||||
|
# Per-agent Plane bot tokens (authorship in Plane comments).
|
||||||
|
# Leave empty to use ORCH_PLANE_API_TOKEN fallback.
|
||||||
|
ORCH_PLANE_BOT_ANALYST=
|
||||||
|
ORCH_PLANE_BOT_ARCHITECT=
|
||||||
|
ORCH_PLANE_BOT_DEVELOPER=
|
||||||
|
ORCH_PLANE_BOT_REVIEWER=
|
||||||
|
ORCH_PLANE_BOT_TESTER=
|
||||||
|
ORCH_PLANE_BOT_DEPLOYER=
|
||||||
|
ORCH_PLANE_BOT_STREAM=
|
||||||
|
|
||||||
|
# ── Gitea ─────────────────────────────────────────────────────────────────────
|
||||||
|
ORCH_GITEA_URL=http://localhost:3000
|
||||||
|
ORCH_GITEA_PUBLIC_URL=https://git.mva154.duckdns.org
|
||||||
|
ORCH_GITEA_TOKEN=<gitea-token>
|
||||||
|
ORCH_GITEA_WEBHOOK_SECRET=<gitea-webhook-secret>
|
||||||
|
|
||||||
|
# ── Telegram ──────────────────────────────────────────────────────────────────
|
||||||
|
ORCH_TELEGRAM_BOT_TOKEN=<telegram-bot-token>
|
||||||
|
ORCH_TELEGRAM_CHAT_ID=<telegram-chat-id>
|
||||||
|
|
||||||
|
# ── Claude / repos ────────────────────────────────────────────────────────────
|
||||||
|
ORCH_CLAUDE_BIN=/usr/bin/claude
|
||||||
|
ORCH_REPOS_DIR=/repos
|
||||||
|
ORCH_HOST_REPOS_DIR=/home/slin/repos
|
||||||
|
|
||||||
|
# ── Database (ISOLATION KEY for staging) ─────────────────────────────────────
|
||||||
|
# The staging volume mounts ./data/staging:/app/data, so the DB physically lives
|
||||||
|
# at ./data/staging/orchestrator.db on the host — fully isolated from prod.
|
||||||
|
# Do NOT change this path; isolation is achieved via the volume mount, not this path.
|
||||||
|
ORCH_DB_PATH=/app/data/orchestrator.db
|
||||||
|
|
||||||
|
# ── Concurrency / worker ──────────────────────────────────────────────────────
|
||||||
|
ORCH_MAX_CONCURRENCY=1
|
||||||
|
ORCH_QUEUE_POLL_INTERVAL=2.0
|
||||||
|
|
||||||
|
# ── Deploy hook ───────────────────────────────────────────────────────────────
|
||||||
|
DEPLOY_SSH_USER=slin
|
||||||
|
DEPLOY_SSH_HOST=127.0.0.1
|
||||||
|
DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,3 +5,7 @@ __pycache__/
|
|||||||
data/
|
data/
|
||||||
*.db
|
*.db
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
# ORCH-31: staging env (secrets, not committed — see .env.staging.example)
|
||||||
|
.env.staging
|
||||||
|
# ORCH-31: staging DB data directory
|
||||||
|
data/staging/
|
||||||
|
|||||||
@@ -25,3 +25,39 @@ services:
|
|||||||
- DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh
|
- DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh
|
||||||
group_add:
|
group_add:
|
||||||
- "999"
|
- "999"
|
||||||
|
|
||||||
|
# ORCH-31: staging instance (port 8501, isolated DB).
|
||||||
|
# Starts ONLY with: docker compose --profile staging up -d orchestrator-staging
|
||||||
|
# Normal "docker compose up -d" does NOT start this service.
|
||||||
|
orchestrator-staging:
|
||||||
|
profiles:
|
||||||
|
- staging
|
||||||
|
build: .
|
||||||
|
container_name: orchestrator-staging
|
||||||
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
network_mode: host
|
||||||
|
command: ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8501"]
|
||||||
|
volumes:
|
||||||
|
- ./data/staging:/app/data
|
||||||
|
- /home/slin/repos:/repos
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /usr/lib/node_modules/@anthropic-ai/claude-code:/opt/claude-code:ro
|
||||||
|
- /usr/bin/node:/usr/bin/node:ro
|
||||||
|
- /home/slin/.claude:/home/slin/.claude
|
||||||
|
- /home/slin/.claude.json:/home/slin/.claude.json:ro
|
||||||
|
- /home/slin/.orchestrator-ssh:/root/.ssh:ro
|
||||||
|
env_file: .env.staging
|
||||||
|
environment:
|
||||||
|
- ORCH_REPOS_DIR=/repos
|
||||||
|
- ORCH_HOST_REPOS_DIR=/home/slin/repos
|
||||||
|
- DEPLOY_SSH_USER=slin
|
||||||
|
- DEPLOY_SSH_HOST=127.0.0.1
|
||||||
|
- DEPLOY_HOOK_SCRIPT=/home/slin/bin/enduro-deploy-hook.sh
|
||||||
|
# Staging DB is isolated via ./data/staging volume mount.
|
||||||
|
# Inside the container the path remains /app/data/orchestrator.db (same default),
|
||||||
|
# but on the host it physically lives at ./data/staging/orchestrator.db —
|
||||||
|
# completely separate from prod ./data/orchestrator.db.
|
||||||
|
- ORCH_DB_PATH=/app/data/orchestrator.db
|
||||||
|
group_add:
|
||||||
|
- "999"
|
||||||
|
|||||||
85
docs/STAGING.md
Normal file
85
docs/STAGING.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Staging Environment (ORCH-31)
|
||||||
|
|
||||||
|
Orchestrator supports a permanent **staging instance** running on port **8501** with a
|
||||||
|
fully-isolated SQLite database. The staging instance shares the same codebase and
|
||||||
|
Dockerfile as production but is started under the `staging` Docker Compose profile so it
|
||||||
|
**never starts accidentally** during a normal `docker compose up -d`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
| | Production | Staging |
|
||||||
|
|---|---|---|
|
||||||
|
| Port | 8500 | 8501 |
|
||||||
|
| Container name | `orchestrator` | `orchestrator-staging` |
|
||||||
|
| DB (host path) | `./data/orchestrator.db` | `./data/staging/orchestrator.db` |
|
||||||
|
| DB (container path) | `/app/data/orchestrator.db` | `/app/data/orchestrator.db` |
|
||||||
|
| env file | `.env` | `.env.staging` |
|
||||||
|
| Compose profile | *(default)* | `staging` |
|
||||||
|
|
||||||
|
DB isolation is achieved via a separate volume mount (`./data/staging:/app/data`), not by
|
||||||
|
changing `ORCH_DB_PATH` — the container path stays identical while the host path is a
|
||||||
|
different directory.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **`.env.staging`** — create from the template (see below). This file is **not committed**
|
||||||
|
to the repo (it contains secrets). Copy and fill in values before first start.
|
||||||
|
2. **`./data/staging/`** directory — created automatically on first container start.
|
||||||
|
|
||||||
|
### Create `.env.staging`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/slin/repos/orchestrator
|
||||||
|
cp .env.staging.example .env.staging
|
||||||
|
# Edit .env.staging — fill in real tokens / secrets.
|
||||||
|
# At Stage 1 (ORCH-31) you can reuse prod values; sandbox Plane project
|
||||||
|
# and isolated Gitea webhook will be wired in ORCH-32.
|
||||||
|
nano .env.staging
|
||||||
|
```
|
||||||
|
|
||||||
|
## Starting Staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/slin/repos/orchestrator
|
||||||
|
docker compose --profile staging up -d orchestrator-staging
|
||||||
|
```
|
||||||
|
|
||||||
|
Check it is running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps | grep orchestrator-staging
|
||||||
|
curl -s http://localhost:8501/health | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stopping Staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile staging stop orchestrator-staging
|
||||||
|
# or remove the container entirely:
|
||||||
|
docker compose --profile staging down orchestrator-staging
|
||||||
|
```
|
||||||
|
|
||||||
|
## Normal `up -d` does NOT start staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# This starts ONLY the prod orchestrator (port 8500). Staging is NOT affected.
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
The `profiles: [staging]` directive in `docker-compose.yml` ensures staging is
|
||||||
|
completely invisible to commands that do not pass `--profile staging`.
|
||||||
|
|
||||||
|
## Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs -f orchestrator-staging
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
| Task | Description |
|
||||||
|
|---|---|
|
||||||
|
| **ORCH-31** *(this PR)* | Infra: compose service, .env template, gitignore, docs |
|
||||||
|
| **ORCH-32** | Sandbox: isolated Plane project + Gitea repo for staging |
|
||||||
|
| **ORCH-33** | Test suite running against staging endpoint |
|
||||||
|
| **ORCH-34** | Deploy hook: promote `orchestrator:candidate` image to staging |
|
||||||
Reference in New Issue
Block a user