Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ volumes:
caddy_config:
go_mod_cache:
driver: local
go_build_cache:
driver: local
prometheus_data_prod:
driver: local
prometheus_data_local:
Expand Down Expand Up @@ -344,11 +346,16 @@ services:
volumes:
- .:/app
- go_mod_cache:/go/pkg/mod
- go_build_cache:/tmp/go-build
- "${ENV_SPA_DIR}:${ENV_SPA_DIR}"
- "${ENV_SPA_IMAGES_DIR}:${ENV_SPA_IMAGES_DIR}"
working_dir: /app
environment:
CGO_ENABLED: 1
PATH: /usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding the PATH variable can be brittle. If the Go installation path in the base image changes, this will break. Since other parts of the Makefile are being updated to use absolute paths to the go executable (e.g., /usr/local/go/bin/go), this explicit PATH definition seems redundant. It would be more robust to remove the PATH override and rely on the base image's PATH.

              CGO_ENABLED: 1

GOPATH: /go
GOMODCACHE: /go/pkg/mod
GOCACHE: /tmp/go-build
GOTOOLCHAIN: ${GO_LOCAL_TOOLCHAIN:-go1.26.1}
ENV_DB_HOST: api-db
ENV_SPA_DIR: ${ENV_SPA_DIR}
Expand Down
2 changes: 1 addition & 1 deletion docs/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The application uses a PostgreSQL database. You can manage it using the followin

To run the application locally:

- **CLI Mode**: `make run-cli`
- **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.
- **Metal (Dev) Mode**: `make run-metal`

### Monitoring
Expand Down
67 changes: 64 additions & 3 deletions infra/makefile/app.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ DB_SECRETS_DIR := $(ROOT_PATH)/database/infra/secrets
GO_LOCAL_TOOLCHAIN ?= auto
GOIMPORTS_VERSION ?= v0.43.0
GOBIN := $(shell go env GOPATH)/bin
CLI_DOCKER_BINARY_HOST := $(ROOT_PATH)/bin/metal-cli
CLI_DOCKER_BINARY_CONTAINER := /app/bin/metal-cli
CLI_DOCKER_BUILD_INPUTS := $(shell git ls-files '*.go' go.mod go.sum 2>/dev/null)

DB_SECRET_USERNAME ?= $(DB_SECRETS_DIR)/pg_username
DB_SECRET_PASSWORD ?= $(DB_SECRETS_DIR)/pg_password
Expand All @@ -25,7 +28,7 @@ DB_SECRET_DBNAME ?= $(DB_SECRETS_DIR)/pg_dbname
# PHONY Targets
# -------------------------------------------------------------------------------------------------------------------- #

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

run-cli run-cli-docker: export DB_SECRET_USERNAME := $(value DB_SECRET_USERNAME)
run-cli run-cli-docker: export DB_SECRET_PASSWORD := $(value DB_SECRET_PASSWORD)
Expand Down Expand Up @@ -96,6 +99,28 @@ install-goimports:
# CLI Commands
# -------------------------------------------------------------------------------------------------------------------- #

build-cli-docker: $(CLI_DOCKER_BINARY_HOST)
@printf " $(CYAN)Docker CLI binary ready at %s.$(NC)\n" "$(CLI_DOCKER_BINARY_HOST)"

$(CLI_DOCKER_BINARY_HOST): $(CLI_DOCKER_BUILD_INPUTS) | ensure-base-images
@mkdir -p "$(dir $@)"
@status=0; \
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then \
printf "Building Docker CLI binary at $(CLI_DOCKER_BINARY_CONTAINER).\n"; \
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=$$?; \
elif command -v docker-compose >/dev/null 2>&1; then \
printf "Building Docker CLI binary at $(CLI_DOCKER_BINARY_CONTAINER).\n"; \
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=$$?; \
else \
printf "\n$(RED)❌ Neither 'docker compose' nor 'docker-compose' is available.$(NC)\n"; \
printf " Install Docker Compose or run the CLI locally without containers.\n\n"; \
exit 1; \
fi; \
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This if/elif block for detecting docker compose vs docker-compose contains duplicated code. You can make this more maintainable by first determining the correct command and storing it in a variable, then using that variable to execute the build command. This avoids repeating the printf and the docker compose run... lines, similar to the pattern you've used in the run-cli target.

    compose_cmd=""; \
    if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then \
        compose_cmd="docker compose"; \
    elif command -v docker-compose >/dev/null 2>&1; then \
        compose_cmd="docker-compose"; \
    else \
        printf "\n$(RED)❌ Neither 'docker compose' nor 'docker-compose' is available.$(NC)\n"; \
        printf "   Install Docker Compose or run the CLI locally without containers.\n\n"; \
        exit 1; \
    fi; \
    printf "Building Docker CLI binary at $(CLI_DOCKER_BINARY_CONTAINER).\n"; \
    $$compose_cmd run --rm --no-deps api-runner sh -lc '/usr/local/go/bin/go build -o "$(CLI_DOCKER_BINARY_CONTAINER)" ./metal/cli/main.go' || status=$$?;

if [ $$status -ne 0 ]; then \
printf "\n$(RED)❌ Failed to build the Docker CLI binary (status $$status).$(NC)\n"; \
exit $$status; \
fi

run-cli:
@missing_values=""; \
missing_files=""; \
Expand Down Expand Up @@ -164,17 +189,53 @@ run-cli:
esac`; \
printf " DB_SECRET_DBNAME=%s\n\n" "$$DB_SECRET_DBNAME_DISPLAY"
@status=0; \
compose_cmd=""; \
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then \
compose_cmd="docker compose"; \
printf "Using docker compose to run the CLI.\n"; \
docker compose run --rm api-runner go run ./metal/cli/main.go || status=$$?; \
elif command -v docker-compose >/dev/null 2>&1; then \
compose_cmd="docker-compose"; \
printf "Using docker-compose to run the CLI.\n"; \
docker-compose run --rm api-runner go run ./metal/cli/main.go || status=$$?; \
else \
printf "\n$(RED)❌ Neither 'docker compose' nor 'docker-compose' is available.$(NC)\n"; \
printf " Install Docker Compose or run the CLI locally without containers.\n\n"; \
exit 1; \
fi; \
db_running() { docker inspect --format '{{.State.Running}}' $(DB_DOCKER_CONTAINER_NAME) 2>/dev/null || true; }; \
db_health() { docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}unknown{{end}}' $(DB_DOCKER_CONTAINER_NAME) 2>/dev/null || true; }; \
$(MAKE) --no-print-directory ensure-base-images || status=$$?; \
if [ $$status -eq 0 ] && [ "$$(db_running)" = "true" ] && [ "$$(db_health)" = "healthy" ]; then \
printf "Database container $(DB_DOCKER_CONTAINER_NAME) is already healthy.\n"; \
elif [ $$status -eq 0 ]; then \
printf "Database container $(DB_DOCKER_CONTAINER_NAME) is not ready. Starting $(DB_DOCKER_SERVICE_NAME)...\n"; \
$(MAKE) --no-print-directory ensure-db-volume || status=$$?; \
if [ $$status -eq 0 ]; then \
$$compose_cmd up -d $(DB_DOCKER_SERVICE_NAME) || status=$$?; \
fi; \
if [ $$status -eq 0 ]; then \
printf "Waiting for database to become healthy...\n"; \
attempt=0; max_attempts=30; \
while [ $$attempt -lt $$max_attempts ]; do \
if [ "$$(db_running)" = "true" ] && [ "$$(db_health)" = "healthy" ]; then \
printf "Database is healthy.\n"; \
break; \
fi; \
attempt=$$((attempt + 1)); \
if [ $$attempt -eq $$max_attempts ]; then \
printf "\n$(RED)❌ Database failed to become healthy after 60 seconds.$(NC)\n"; \
status=1; \
break; \
fi; \
sleep 2; \
done; \
fi; \
fi; \
if [ $$status -eq 0 ]; then \
$(MAKE) --no-print-directory build-cli-docker || status=$$?; \
fi; \
if [ $$status -eq 0 ]; then \
$$compose_cmd run --rm --no-deps api-runner $(CLI_DOCKER_BINARY_CONTAINER) || status=$$?; \
fi; \
if [ $$status -ne 0 ]; then \
printf "\n$(RED)❌ CLI exited with status $$status.$(NC)\n"; \
exit $$status; \
Expand Down
4 changes: 2 additions & 2 deletions infra/makefile/db.mk
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ db\:secure:

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

db\:import:
@if [ ! -f "./storage/sql/dump.sql" ]; then \
echo "db:import requires ./storage/sql/dump.sql"; \
exit 1; \
fi
docker compose --env-file ./.env run --rm $(DB_MIGRATE_DOCKER_ENV_FLAGS) $(DB_API_RUNNER_SERVICE) \
go run ./database/seeder/importer/cmd
/usr/local/go/bin/go run ./database/seeder/importer/cmd
# -------------------------------------------------------------------------------------------------------------------- #
# --- Migrations
# -------------------------------------------------------------------------------------------------------------------- #
Expand Down
Loading