Skip to content

Commit e892d85

Browse files
committed
cli cold start
1 parent a65b729 commit e892d85

4 files changed

Lines changed: 74 additions & 6 deletions

File tree

docker-compose.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ volumes:
1212
caddy_config:
1313
go_mod_cache:
1414
driver: local
15+
go_build_cache:
16+
driver: local
1517
prometheus_data_prod:
1618
driver: local
1719
prometheus_data_local:
@@ -344,11 +346,16 @@ services:
344346
volumes:
345347
- .:/app
346348
- go_mod_cache:/go/pkg/mod
349+
- go_build_cache:/tmp/go-build
347350
- "${ENV_SPA_DIR}:${ENV_SPA_DIR}"
348351
- "${ENV_SPA_IMAGES_DIR}:${ENV_SPA_IMAGES_DIR}"
349352
working_dir: /app
350353
environment:
351354
CGO_ENABLED: 1
355+
PATH: /usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
356+
GOPATH: /go
357+
GOMODCACHE: /go/pkg/mod
358+
GOCACHE: /tmp/go-build
352359
GOTOOLCHAIN: ${GO_LOCAL_TOOLCHAIN:-go1.26.1}
353360
ENV_DB_HOST: api-db
354361
ENV_SPA_DIR: ${ENV_SPA_DIR}

docs/SETUP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ The application uses a PostgreSQL database. You can manage it using the followin
5555

5656
To run the application locally:
5757

58-
- **CLI Mode**: `make run-cli`
58+
- **CLI Mode**: `make run-cli` reuses `oullin_db` when it is already healthy, starts `api-db` only when needed, and reuses a Docker-built CLI binary on warm runs.
5959
- **Metal (Dev) Mode**: `make run-metal`
6060

6161
### Monitoring

infra/makefile/app.mk

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ DB_SECRETS_DIR := $(ROOT_PATH)/database/infra/secrets
1616
GO_LOCAL_TOOLCHAIN ?= auto
1717
GOIMPORTS_VERSION ?= v0.43.0
1818
GOBIN := $(shell go env GOPATH)/bin
19+
CLI_DOCKER_BINARY_HOST := $(ROOT_PATH)/bin/metal-cli
20+
CLI_DOCKER_BINARY_CONTAINER := /app/bin/metal-cli
21+
CLI_DOCKER_BUILD_INPUTS := $(shell git ls-files '*.go' go.mod go.sum 2>/dev/null)
1922

2023
DB_SECRET_USERNAME ?= $(DB_SECRETS_DIR)/pg_username
2124
DB_SECRET_PASSWORD ?= $(DB_SECRETS_DIR)/pg_password
@@ -25,7 +28,7 @@ DB_SECRET_DBNAME ?= $(DB_SECRETS_DIR)/pg_dbname
2528
# PHONY Targets
2629
# -------------------------------------------------------------------------------------------------------------------- #
2730

28-
.PHONY: fresh destroy audit watch format run-cli test-all run-cli-docker run-metal install-air install-goimports
31+
.PHONY: fresh destroy audit watch format run-cli test-all run-cli-docker run-metal install-air install-goimports build-cli-docker
2932

3033
run-cli run-cli-docker: export DB_SECRET_USERNAME := $(value DB_SECRET_USERNAME)
3134
run-cli run-cli-docker: export DB_SECRET_PASSWORD := $(value DB_SECRET_PASSWORD)
@@ -96,6 +99,28 @@ install-goimports:
9699
# CLI Commands
97100
# -------------------------------------------------------------------------------------------------------------------- #
98101

102+
build-cli-docker: $(CLI_DOCKER_BINARY_HOST)
103+
@printf " $(CYAN)Docker CLI binary ready at %s.$(NC)\n" "$(CLI_DOCKER_BINARY_HOST)"
104+
105+
$(CLI_DOCKER_BINARY_HOST): $(CLI_DOCKER_BUILD_INPUTS) | ensure-base-images
106+
@mkdir -p "$(dir $@)"
107+
@status=0; \
108+
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then \
109+
printf "Building Docker CLI binary at $(CLI_DOCKER_BINARY_CONTAINER).\n"; \
110+
docker compose run --rm --no-deps api-runner sh -lc '/usr/local/go/bin/go build -o "$(CLI_DOCKER_BINARY_CONTAINER)" ./metal/cli/main.go' || status=$$?; \
111+
elif command -v docker-compose >/dev/null 2>&1; then \
112+
printf "Building Docker CLI binary at $(CLI_DOCKER_BINARY_CONTAINER).\n"; \
113+
docker-compose run --rm --no-deps api-runner sh -lc '/usr/local/go/bin/go build -o "$(CLI_DOCKER_BINARY_CONTAINER)" ./metal/cli/main.go' || status=$$?; \
114+
else \
115+
printf "\n$(RED)❌ Neither 'docker compose' nor 'docker-compose' is available.$(NC)\n"; \
116+
printf " Install Docker Compose or run the CLI locally without containers.\n\n"; \
117+
exit 1; \
118+
fi; \
119+
if [ $$status -ne 0 ]; then \
120+
printf "\n$(RED)❌ Failed to build the Docker CLI binary (status $$status).$(NC)\n"; \
121+
exit $$status; \
122+
fi
123+
99124
run-cli:
100125
@missing_values=""; \
101126
missing_files=""; \
@@ -164,17 +189,53 @@ run-cli:
164189
esac`; \
165190
printf " DB_SECRET_DBNAME=%s\n\n" "$$DB_SECRET_DBNAME_DISPLAY"
166191
@status=0; \
192+
compose_cmd=""; \
167193
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then \
194+
compose_cmd="docker compose"; \
168195
printf "Using docker compose to run the CLI.\n"; \
169-
docker compose run --rm api-runner go run ./metal/cli/main.go || status=$$?; \
170196
elif command -v docker-compose >/dev/null 2>&1; then \
197+
compose_cmd="docker-compose"; \
171198
printf "Using docker-compose to run the CLI.\n"; \
172-
docker-compose run --rm api-runner go run ./metal/cli/main.go || status=$$?; \
173199
else \
174200
printf "\n$(RED)❌ Neither 'docker compose' nor 'docker-compose' is available.$(NC)\n"; \
175201
printf " Install Docker Compose or run the CLI locally without containers.\n\n"; \
176202
exit 1; \
177203
fi; \
204+
db_running() { docker inspect --format '{{.State.Running}}' $(DB_DOCKER_CONTAINER_NAME) 2>/dev/null || true; }; \
205+
db_health() { docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}unknown{{end}}' $(DB_DOCKER_CONTAINER_NAME) 2>/dev/null || true; }; \
206+
$(MAKE) --no-print-directory ensure-base-images || status=$$?; \
207+
if [ $$status -eq 0 ] && [ "$$(db_running)" = "true" ] && [ "$$(db_health)" = "healthy" ]; then \
208+
printf "Database container $(DB_DOCKER_CONTAINER_NAME) is already healthy.\n"; \
209+
elif [ $$status -eq 0 ]; then \
210+
printf "Database container $(DB_DOCKER_CONTAINER_NAME) is not ready. Starting $(DB_DOCKER_SERVICE_NAME)...\n"; \
211+
$(MAKE) --no-print-directory ensure-db-volume || status=$$?; \
212+
if [ $$status -eq 0 ]; then \
213+
$$compose_cmd up -d $(DB_DOCKER_SERVICE_NAME) || status=$$?; \
214+
fi; \
215+
if [ $$status -eq 0 ]; then \
216+
printf "Waiting for database to become healthy...\n"; \
217+
attempt=0; max_attempts=30; \
218+
while [ $$attempt -lt $$max_attempts ]; do \
219+
if [ "$$(db_running)" = "true" ] && [ "$$(db_health)" = "healthy" ]; then \
220+
printf "Database is healthy.\n"; \
221+
break; \
222+
fi; \
223+
attempt=$$((attempt + 1)); \
224+
if [ $$attempt -eq $$max_attempts ]; then \
225+
printf "\n$(RED)❌ Database failed to become healthy after 60 seconds.$(NC)\n"; \
226+
status=1; \
227+
break; \
228+
fi; \
229+
sleep 2; \
230+
done; \
231+
fi; \
232+
fi; \
233+
if [ $$status -eq 0 ]; then \
234+
$(MAKE) --no-print-directory build-cli-docker || status=$$?; \
235+
fi; \
236+
if [ $$status -eq 0 ]; then \
237+
$$compose_cmd run --rm --no-deps api-runner $(CLI_DOCKER_BINARY_CONTAINER) || status=$$?; \
238+
fi; \
178239
if [ $$status -ne 0 ]; then \
179240
printf "\n$(RED)❌ CLI exited with status $$status.$(NC)\n"; \
180241
exit $$status; \

infra/makefile/db.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ db\:secure:
6262

6363
db\:seed:
6464
docker compose --env-file ./.env run --rm $(DB_MIGRATE_DOCKER_ENV_FLAGS) $(DB_API_RUNNER_SERVICE) \
65-
go run ./database/seeder/main.go
65+
/usr/local/go/bin/go run ./database/seeder/main.go
6666

6767
db\:import:
6868
@if [ ! -f "./storage/sql/dump.sql" ]; then \
6969
echo "db:import requires ./storage/sql/dump.sql"; \
7070
exit 1; \
7171
fi
7272
docker compose --env-file ./.env run --rm $(DB_MIGRATE_DOCKER_ENV_FLAGS) $(DB_API_RUNNER_SERVICE) \
73-
go run ./database/seeder/importer/cmd
73+
/usr/local/go/bin/go run ./database/seeder/importer/cmd
7474
# -------------------------------------------------------------------------------------------------------------------- #
7575
# --- Migrations
7676
# -------------------------------------------------------------------------------------------------------------------- #

0 commit comments

Comments
 (0)