@@ -16,6 +16,9 @@ DB_SECRETS_DIR := $(ROOT_PATH)/database/infra/secrets
1616GO_LOCAL_TOOLCHAIN ?= auto
1717GOIMPORTS_VERSION ?= v0.43.0
1818GOBIN := $(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
2023DB_SECRET_USERNAME ?= $(DB_SECRETS_DIR ) /pg_username
2124DB_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
3033run-cli run-cli-docker : export DB_SECRET_USERNAME := $(value DB_SECRET_USERNAME)
3134run-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+
99124run-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; \
0 commit comments