Add isolated development environment
This commit is contained in:
1 parent
421fa98292
commit
f5af75f305
7 files changed
+240
No files matched your search
@@ -0,0 +1,20 @@
|
||||
# Copying this file is optional. scripts/dev-env-init.sh generates strong secrets.
|
||||
APP_ENV=development
|
||||
APP_TIMEZONE=America/Winnipeg
|
||||
POSTGRES_DB=minerops_dev
|
||||
POSTGRES_USER=minerops_dev
|
||||
POSTGRES_PASSWORD=replace-with-a-random-local-only-secret
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
DATABASE_URL=postgresql://minerops_dev:replace-with-a-random-local-only-secret@postgres:5432/minerops_dev
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
JWT_SECRET_KEY=replace-with-a-random-local-only-secret
|
||||
MINEROPS_AGENT_SHARED_SECRET=replace-with-a-random-local-only-secret
|
||||
MINEROPS_DEV_PORT=18000
|
||||
|
||||
# Safety interlocks: keep these values disabled for development.
|
||||
SCHEDULED_SCAN_ENABLED=false
|
||||
MINEROPS_HUB_MODE=true
|
||||
MINEROPS_DISABLE_LOCAL_SCANNER=true
|
||||
MINEROPS_ENABLE_REAL_REBOOT=false
|
||||
MINEROPS_ENABLE_REAL_POWER_CONTROL=false
|
||||
@@ -2,6 +2,8 @@
|
||||
.env
|
||||
.env.*
|
||||
*.env
|
||||
!.env.example
|
||||
!.env.dev.example
|
||||
|
||||
# Backups / dumps / generated data
|
||||
backups/
|
||||
|
||||
@@ -74,9 +74,39 @@ Git status:
|
||||
|
||||
pct exec 306 -- bash -lc "cd /opt/minerops && git status --short"
|
||||
|
||||
## Safe local development
|
||||
|
||||
Development uses a separate Compose project, database volume, Redis volume, and
|
||||
localhost-only backend port. It does not reuse the production `.env` file.
|
||||
Scheduled scanning and real miner power/reboot controls are disabled.
|
||||
|
||||
First-time setup:
|
||||
|
||||
```bash
|
||||
./scripts/dev-env-init.sh
|
||||
docker compose --env-file .env.dev -f compose.dev.yaml up -d --build
|
||||
```
|
||||
|
||||
Verify the environment:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env.dev -f compose.dev.yaml ps
|
||||
curl --fail --silent http://127.0.0.1:18000/api/health
|
||||
```
|
||||
|
||||
Stop development services without deleting their data:
|
||||
|
||||
```bash
|
||||
docker compose --env-file .env.dev -f compose.dev.yaml down
|
||||
```
|
||||
|
||||
The generated `.env.dev` file is mode `0600` and ignored by Git. To use a
|
||||
different local port, change `MINEROPS_DEV_PORT` in `.env.dev`.
|
||||
|
||||
## Safety notes
|
||||
|
||||
- Do not commit .env.
|
||||
- Do not commit backups, logs, database dumps, imports, or runtime data.
|
||||
- Do not enable scanning or real miner controls in the development environment.
|
||||
- Check /system-health after major changes.
|
||||
- Commit and push after each stable working change.
|
||||
@@ -0,0 +1,6 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.bak
|
||||
*.bak.*
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
@@ -0,0 +1,105 @@
|
||||
name: minerops-dev
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres-dev:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dev
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
command: ["redis-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- redis-dev:/data
|
||||
networks:
|
||||
- dev
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
env_file:
|
||||
- .env.dev
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- dev
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- python
|
||||
- -c
|
||||
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 20s
|
||||
|
||||
gateway:
|
||||
image: nginx:1.29-alpine
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "127.0.0.1:${MINEROPS_DEV_PORT:-18000}:8080"
|
||||
networks:
|
||||
- dev
|
||||
- edge
|
||||
volumes:
|
||||
- ./dev/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
user: "101:101"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --quiet
|
||||
- --spider
|
||||
- http://127.0.0.1:8080/api/health
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
networks:
|
||||
dev:
|
||||
internal: true
|
||||
edge:
|
||||
|
||||
volumes:
|
||||
postgres-dev:
|
||||
redis-dev:
|
||||
@@ -0,0 +1,30 @@
|
||||
worker_processes 1;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 128;
|
||||
}
|
||||
|
||||
http {
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr notice;
|
||||
|
||||
client_body_temp_path /tmp/client_body;
|
||||
proxy_temp_path /tmp/proxy;
|
||||
fastcgi_temp_path /tmp/fastcgi;
|
||||
uwsgi_temp_path /tmp/uwsgi;
|
||||
scgi_temp_path /tmp/scgi;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
env_file="${project_dir}/.env.dev"
|
||||
|
||||
if [[ -e "${env_file}" ]]; then
|
||||
echo "Keeping existing ${env_file}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
umask 077
|
||||
|
||||
generate_secret() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex 32
|
||||
else
|
||||
python3 -c 'import secrets; print(secrets.token_hex(32))'
|
||||
fi
|
||||
}
|
||||
|
||||
postgres_password="$(generate_secret)"
|
||||
jwt_secret="$(generate_secret)"
|
||||
agent_secret="$(generate_secret)"
|
||||
|
||||
{
|
||||
echo "APP_ENV=development"
|
||||
echo "APP_TIMEZONE=America/Winnipeg"
|
||||
echo "POSTGRES_DB=minerops_dev"
|
||||
echo "POSTGRES_USER=minerops_dev"
|
||||
echo "POSTGRES_PASSWORD=${postgres_password}"
|
||||
echo "POSTGRES_HOST=postgres"
|
||||
echo "POSTGRES_PORT=5432"
|
||||
echo "DATABASE_URL=postgresql://minerops_dev:${postgres_password}@postgres:5432/minerops_dev"
|
||||
echo "REDIS_URL=redis://redis:6379/0"
|
||||
echo "JWT_SECRET_KEY=${jwt_secret}"
|
||||
echo "MINEROPS_AGENT_SHARED_SECRET=${agent_secret}"
|
||||
echo "MINEROPS_DEV_PORT=18000"
|
||||
echo "SCHEDULED_SCAN_ENABLED=false"
|
||||
echo "MINEROPS_HUB_MODE=true"
|
||||
echo "MINEROPS_DISABLE_LOCAL_SCANNER=true"
|
||||
echo "MINEROPS_ENABLE_REAL_REBOOT=false"
|
||||
echo "MINEROPS_ENABLE_REAL_POWER_CONTROL=false"
|
||||
} >"${env_file}"
|
||||
|
||||
chmod 0600 "${env_file}"
|
||||
echo "Created ${env_file} with development-only secrets and safety interlocks."
|
||||
Reference in new issue
Block a user