forked from molecule-ai/molecule-core
Merge pull request 'feat(local-dev): air-based hot-reload for workspace-server in docker-compose dev mode' (#118) from feat/air-hot-reload-dev into main
This commit is contained in:
commit
61166f8848
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
||||
# Top-level Makefile — convenience wrappers around docker compose.
|
||||
#
|
||||
# Most molecule-core dev work happens via these shortcuts. CI doesn't
|
||||
# use this Makefile; CI calls docker compose / go test directly so the
|
||||
# Makefile can evolve without breaking the build.
|
||||
|
||||
.PHONY: help dev up down logs build test
|
||||
|
||||
help: ## Show this help.
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-12s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
dev: ## Start the full stack with air hot-reload for the platform service.
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
up: ## Start the full stack in production-shape mode (no air, normal Dockerfile).
|
||||
docker compose up
|
||||
|
||||
down: ## Stop the stack and remove containers (volumes preserved).
|
||||
docker compose down
|
||||
|
||||
logs: ## Tail logs from all services (Ctrl-C to detach).
|
||||
docker compose logs -f
|
||||
|
||||
build: ## Force a fresh build of the platform image (no cache).
|
||||
docker compose build --no-cache platform
|
||||
|
||||
test: ## Run Go unit tests in workspace-server/.
|
||||
cd workspace-server && go test -race ./...
|
||||
43
docker-compose.dev.yml
Normal file
43
docker-compose.dev.yml
Normal file
@ -0,0 +1,43 @@
|
||||
# docker-compose.dev.yml — overlay over docker-compose.yml for local dev
|
||||
# with air-driven live reload of the platform (workspace-server) service.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
# (or `make dev` shorthand from repo root)
|
||||
#
|
||||
# What this overlay changes vs docker-compose.yml alone:
|
||||
# - Platform service uses workspace-server/Dockerfile.dev (air on top of
|
||||
# golang:1.25-alpine) instead of the multi-stage prod Dockerfile.
|
||||
# - Platform service bind-mounts the host's workspace-server/ source
|
||||
# into /app/workspace-server so air sees source edits live.
|
||||
# - Other services (postgres, redis, langfuse, etc.) inherit unchanged
|
||||
# from docker-compose.yml.
|
||||
#
|
||||
# What stays the same:
|
||||
# - All env vars, volumes, depends_on, healthchecks from docker-compose.yml.
|
||||
# - Network topology + ports.
|
||||
# - Postgres/Redis as service containers (no in-process replacements).
|
||||
|
||||
services:
|
||||
platform:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: workspace-server/Dockerfile.dev
|
||||
# Rebind source: edits under host's workspace-server/ propagate live.
|
||||
# The named volume on go-build-cache speeds up first build per container.
|
||||
volumes:
|
||||
- ./workspace-server:/app/workspace-server
|
||||
- go-build-cache:/root/.cache/go-build
|
||||
- go-mod-cache:/go/pkg/mod
|
||||
# Air signals the running binary on rebuild; ensure shell stops cleanly.
|
||||
init: true
|
||||
# Mark the service as dev-mode so the platform can short-circuit any
|
||||
# behavior that's incompatible with hot-reload (e.g. background
|
||||
# cron-style watchers that don't survive process restart). No-op
|
||||
# today; reserved for future flag use.
|
||||
environment:
|
||||
MOLECULE_DEV_HOT_RELOAD: "1"
|
||||
|
||||
volumes:
|
||||
go-build-cache:
|
||||
go-mod-cache:
|
||||
49
workspace-server/.air.toml
Normal file
49
workspace-server/.air.toml
Normal file
@ -0,0 +1,49 @@
|
||||
# air.toml — live-reload config for local docker-compose dev mode.
|
||||
#
|
||||
# Active when the platform service runs from workspace-server/Dockerfile.dev
|
||||
# (selected via docker-compose.dev.yml overlay). In production, the regular
|
||||
# Dockerfile builds a static binary; air is dev-only.
|
||||
#
|
||||
# Reference: https://github.com/air-verse/air
|
||||
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
# Same build invocation as Dockerfile's builder stage minus the
|
||||
# CGO_ENABLED=0 toggle (CGO ok in dev for richer race detector output).
|
||||
cmd = "go build -o ./tmp/server ./cmd/server"
|
||||
bin = "tmp/server"
|
||||
full_bin = ""
|
||||
args_bin = []
|
||||
# Watch every .go and .yaml file under workspace-server/.
|
||||
include_ext = ["go", "yaml", "tmpl"]
|
||||
# Don't watch tests, build artifacts, vendored deps, or migration .sql
|
||||
# (migrations need a clean DB anyway — handled by docker-compose down/up).
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"]
|
||||
exclude_file = []
|
||||
# _test.go and *_mock.go shouldn't trigger a rebuild — saves cycles.
|
||||
exclude_regex = ["_test\\.go$", "_mock\\.go$"]
|
||||
exclude_unchanged = true
|
||||
follow_symlink = false
|
||||
log = "build-errors.log"
|
||||
# Kill running binary 1s before starting new one.
|
||||
kill_delay = "1s"
|
||||
send_interrupt = true
|
||||
stop_on_error = true
|
||||
# Debounce: wait this long after last change before triggering rebuild.
|
||||
delay = 500
|
||||
|
||||
[log]
|
||||
time = false
|
||||
|
||||
[color]
|
||||
main = "magenta"
|
||||
watcher = "cyan"
|
||||
build = "yellow"
|
||||
runner = "green"
|
||||
|
||||
[misc]
|
||||
# Don't keep the tmp/ dir around between runs.
|
||||
clean_on_exit = true
|
||||
38
workspace-server/Dockerfile.dev
Normal file
38
workspace-server/Dockerfile.dev
Normal file
@ -0,0 +1,38 @@
|
||||
# Dockerfile.dev — local-development image with air-driven live reload.
|
||||
#
|
||||
# Selected by docker-compose.dev.yml (overlay over docker-compose.yml).
|
||||
# Production stays on workspace-server/Dockerfile (static binary, no air).
|
||||
#
|
||||
# Workflow:
|
||||
# 1. docker compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
# 2. Edit any .go file under workspace-server/
|
||||
# 3. air detects, rebuilds, kills old binary, starts new one (~3-5s)
|
||||
# 4. No `docker compose up --build` needed
|
||||
#
|
||||
# Templates + plugins are NOT pre-cloned here — air-mode assumes the
|
||||
# developer's filesystem has the workspace-configs-templates/ + plugins/
|
||||
# dirs available, mounted at runtime via docker-compose.dev.yml.
|
||||
|
||||
FROM golang:1.25-alpine
|
||||
|
||||
# air + git (for go mod) + ca-certs (for TLS) + tzdata (for time-zone DB).
|
||||
RUN apk add --no-cache git ca-certificates tzdata wget \
|
||||
&& go install github.com/air-verse/air@latest
|
||||
|
||||
WORKDIR /app/workspace-server
|
||||
|
||||
# Pre-fetch deps so the first `air` rebuild on a fresh container is fast.
|
||||
# These are bind-mount-overridden at runtime, so the COPY here is just
|
||||
# to warm the module cache.
|
||||
COPY workspace-server/go.mod workspace-server/go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Source is bind-mounted at runtime (see docker-compose.dev.yml volumes
|
||||
# block) so the Dockerfile doesn't need to COPY it. air watches the
|
||||
# bind-mounted dir for changes.
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GOFLAGS="-buildvcs=false"
|
||||
|
||||
# Run air with the .air.toml in the bind-mounted source dir.
|
||||
CMD ["air", "-c", ".air.toml"]
|
||||
Loading…
Reference in New Issue
Block a user