Compare commits

...

1 Commits

Author SHA1 Message Date
Lambda
e79783fea4 fix(selfhost): fail early with actionable message when Docker Compose v2 is missing
The selfhost make targets hardcoded 'docker compose' (Compose v2). On hosts with only the legacy v1 'docker-compose' (e.g. apt install docker-compose) the plugin is absent, so 'docker compose -f ... pull' fell through to the top-level docker CLI and failed with the cryptic 'unknown shorthand flag: f in -f', which users mistook for a Docker version problem (MUL-3458).

Add a REQUIRE_COMPOSE preflight that checks 'docker compose version' and prints an actionable English install hint, and route every selfhost invocation through $(COMPOSE). v1 fallback is intentionally not supported: docker-compose.selfhost.yml uses compose-spec syntax (top-level name:, no version:) that v1 cannot parse.

Co-authored-by: multica-agent <github@multica.ai>
2026-06-20 12:36:11 +08:00

View File

@@ -37,6 +37,26 @@ define REQUIRE_ENV
fi
endef
# Self-hosting requires Docker Compose v2 (the `docker compose` CLI plugin).
# The self-host compose files use compose-spec syntax (top-level `name:`, no
# `version:`) that the legacy v1 `docker-compose` standalone cannot parse, so we
# fail early with an actionable message instead of a cryptic CLI parse error
# (e.g. "unknown shorthand flag: 'f' in -f") when the plugin is missing.
define REQUIRE_COMPOSE
@if ! $(COMPOSE) version >/dev/null 2>&1; then \
echo "Docker Compose v2 ('docker compose') was not found."; \
echo "Self-hosting requires the Compose v2 CLI plugin; the legacy 'docker-compose' (v1) is not supported."; \
echo ""; \
echo "Install it (see https://docs.docker.com/compose/install/linux/):"; \
echo " sudo mkdir -p /usr/local/lib/docker/cli-plugins"; \
echo " sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$$(uname -m) -o /usr/local/lib/docker/cli-plugins/docker-compose"; \
echo " sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose"; \
echo ""; \
echo "Then verify with: docker compose version"; \
exit 1; \
fi
endef
# Default target changed from selfhost to help: bare `make` now prints this help
# instead of launching a full Docker Compose build, which is safer for onboarding.
.DEFAULT_GOAL := help
@@ -54,6 +74,7 @@ makehelp: help ## Alias for `make help`
##@ Self-hosting
selfhost: ## Create .env if needed, then pull and start the official self-hosted images
$(REQUIRE_COMPOSE)
@if [ ! -f .env ]; then \
echo "==> Creating .env from .env.example..."; \
cp .env.example .env; \
@@ -71,7 +92,7 @@ selfhost: ## Create .env if needed, then pull and start the official self-hosted
echo "==> Generated random JWT_SECRET and POSTGRES_PASSWORD"; \
fi
@echo "==> Pulling official Multica images..."
@if ! docker compose -f docker-compose.selfhost.yml pull; then \
@if ! $(COMPOSE) -f docker-compose.selfhost.yml pull; then \
echo ""; \
echo "Official images for tag '$${MULTICA_IMAGE_TAG:-latest}' are not published yet."; \
echo "If this is before the first GHCR release, build from the current checkout:"; \
@@ -79,7 +100,7 @@ selfhost: ## Create .env if needed, then pull and start the official self-hosted
exit 1; \
fi
@echo "==> Starting Multica via Docker Compose..."
docker compose -f docker-compose.selfhost.yml up -d
$(COMPOSE) -f docker-compose.selfhost.yml up -d
@echo "==> Waiting for backend to be ready..."
@for i in $$(seq 1 30); do \
if curl -sf http://localhost:$${PORT:-8080}/health > /dev/null 2>&1; then \
@@ -105,10 +126,11 @@ selfhost: ## Create .env if needed, then pull and start the official self-hosted
else \
echo ""; \
echo "Services are still starting. Check logs:"; \
echo " docker compose -f docker-compose.selfhost.yml logs"; \
echo " $(COMPOSE) -f docker-compose.selfhost.yml logs"; \
fi
selfhost-build: ## Build backend/web from the current checkout and start the self-hosted stack
$(REQUIRE_COMPOSE)
@if [ ! -f .env ]; then \
echo "==> Creating .env from .env.example..."; \
cp .env.example .env; \
@@ -126,7 +148,7 @@ selfhost-build: ## Build backend/web from the current checkout and start the sel
echo "==> Generated random JWT_SECRET and POSTGRES_PASSWORD"; \
fi
@echo "==> Building Multica from the current checkout..."
docker compose -f docker-compose.selfhost.yml -f docker-compose.selfhost.build.yml up -d --build
$(COMPOSE) -f docker-compose.selfhost.yml -f docker-compose.selfhost.build.yml up -d --build
@echo "==> Waiting for backend to be ready..."
@for i in $$(seq 1 30); do \
if curl -sf http://localhost:$${PORT:-8080}/health > /dev/null 2>&1; then \
@@ -152,12 +174,13 @@ selfhost-build: ## Build backend/web from the current checkout and start the sel
else \
echo ""; \
echo "Services are still starting. Check logs:"; \
echo " docker compose -f docker-compose.selfhost.yml logs"; \
echo " $(COMPOSE) -f docker-compose.selfhost.yml logs"; \
fi
selfhost-stop: ## Stop the self-hosted Docker Compose stack
$(REQUIRE_COMPOSE)
@echo "==> Stopping Multica services..."
docker compose -f docker-compose.selfhost.yml down
$(COMPOSE) -f docker-compose.selfhost.yml down
@echo "✓ All services stopped."
# ---------- One-click commands ----------