mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-25 05:56:18 +00:00
fix(docker): add 'make prod-logs' entry point and hint when dev logs are empty (#5538)
* fix(docker): add 'make prod-logs' entry point and hint when dev logs are empty `make up` starts the production stack (deploy.sh: project `deer-flow`, docker/docker-compose.yaml) while `make docker-logs` tails the dev stack (project `deer-flow-dev`, docker-compose-dev.yaml), so after `make up` it printed nothing (#5529), and no make entry point showed production logs at all. - scripts/docker.sh logs gains `--prod`: targets the stack deploy.sh started, passes --env-file ../.env when present, and exports the same interpolation defaults deploy.sh exports before every compose call — without them the production volume specs fail to parse on checkouts without .env. - dev-only `logs` with no running containers now prints a hint pointing at `make prod-logs` instead of staying silent. - Makefile gains `prod-logs`, listed under Docker Production Commands. New tests cover production targeting and the empty-state hint; they are red on unfixed main and green here. Verified live: with a deer-flow redis running, `logs --prod --redis` streams its logs. Fixes #5529 * fix(docker): append --env-file after compose detection rebuilds COMPOSE_CMD compose_preflight() probes the Compose binary and rebuilds COMPOSE_CMD, so an --env-file appended before it was silently dropped. Append it after preflight instead, and drive the regression test through the real detection path (stub Docker Compose version v5.3.1, not require_compose_version) so the append cannot regress silently. Reviewed-in: #5538
This commit is contained in:
parent
82cf57a9c3
commit
859b105b40
7
Makefile
7
Makefile
@ -1,6 +1,6 @@
|
|||||||
# DeerFlow - Unified Development Environment
|
# DeerFlow - Unified Development Environment
|
||||||
|
|
||||||
.PHONY: help config config-upgrade check check-agent-guidance install extension-install extension-upgrade extension-list extension-enable extension-disable extension-remove setup doctor support-bundle detect-thread-boundaries detect-blocking-io dev dev-daemon start start-daemon nginx stop up down clean docker-init docker-start docker-stop docker-logs docker-logs-frontend docker-logs-gateway docker-logs-redis setup-sandbox
|
.PHONY: help config config-upgrade check check-agent-guidance install extension-install extension-upgrade extension-list extension-enable extension-disable extension-remove setup doctor support-bundle detect-thread-boundaries detect-blocking-io dev dev-daemon start start-daemon nginx stop up down prod-logs clean docker-init docker-start docker-stop docker-logs docker-logs-frontend docker-logs-gateway docker-logs-redis setup-sandbox
|
||||||
|
|
||||||
BASH ?= bash
|
BASH ?= bash
|
||||||
BACKEND_UV_RUN = cd backend && uv run
|
BACKEND_UV_RUN = cd backend && uv run
|
||||||
@ -50,6 +50,7 @@ help:
|
|||||||
@echo ""
|
@echo ""
|
||||||
@echo "Docker Production Commands:"
|
@echo "Docker Production Commands:"
|
||||||
@echo " make up - Build and start production Docker services (localhost:2026)"
|
@echo " make up - Build and start production Docker services (localhost:2026)"
|
||||||
|
@echo " make prod-logs - Follow production Docker logs (stack started by 'make up')"
|
||||||
@echo " make down - Stop and remove production Docker containers"
|
@echo " make down - Stop and remove production Docker containers"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Docker Development Commands:"
|
@echo "Docker Development Commands:"
|
||||||
@ -217,3 +218,7 @@ up:
|
|||||||
# Stop and remove production containers
|
# Stop and remove production containers
|
||||||
down:
|
down:
|
||||||
@$(RUN_SHELL_SCRIPT) ./scripts/deploy.sh down
|
@$(RUN_SHELL_SCRIPT) ./scripts/deploy.sh down
|
||||||
|
|
||||||
|
# Follow production container logs (stack started by `make up`)
|
||||||
|
prod-logs:
|
||||||
|
@$(RUN_SHELL_SCRIPT) ./scripts/docker.sh logs --prod
|
||||||
|
|||||||
@ -313,6 +313,83 @@ require_compose_version
|
|||||||
assert "too old" in result.stdout
|
assert "too old" in result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("args", ["logs --prod", "logs --prod --gateway"])
|
||||||
|
def test_logs_prod_targets_production_stack(args):
|
||||||
|
"""`logs --prod` must tail the stack deploy.sh started, not the dev project.
|
||||||
|
|
||||||
|
`make up` runs scripts/deploy.sh (project `deer-flow`, docker-compose.yaml)
|
||||||
|
while the dev default is project `deer-flow-dev`, so `make docker-logs`
|
||||||
|
after `make up` printed nothing (#5529). The production entry point must
|
||||||
|
target the same project and interpolate the same .env.
|
||||||
|
|
||||||
|
Compose detection is NOT stubbed here: it rebuilds COMPOSE_CMD, and the
|
||||||
|
appended `--env-file` must survive that rebuild (#5538 review).
|
||||||
|
"""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
tmp_root = Path(tmpdir)
|
||||||
|
(tmp_root / "docker-compose.yaml").write_text("services: {}\n", encoding="utf-8")
|
||||||
|
(tmp_root / ".env").write_text("KEEP=me\n", encoding="utf-8")
|
||||||
|
marker = tmp_root / "prod_invoke.txt"
|
||||||
|
|
||||||
|
command = f"""
|
||||||
|
source '{SCRIPT_PATH}'
|
||||||
|
PROJECT_ROOT='{tmp_root}'
|
||||||
|
DOCKER_DIR='{tmp_root}'
|
||||||
|
docker() {{
|
||||||
|
if [ "$1" = compose ] && [ "$2" = version ]; then
|
||||||
|
echo '2.41.0'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$1" = compose ]; then
|
||||||
|
printf '%s\\n' "$*" "DEER_FLOW_HOME=${{DEER_FLOW_HOME:-unset}}" > '{marker}'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
command docker "$@"
|
||||||
|
}}
|
||||||
|
unset DEER_FLOW_ROOT DEER_FLOW_HOME DEER_FLOW_CONFIG_PATH
|
||||||
|
unset DEER_FLOW_EXTENSIONS_CONFIG_PATH DEER_FLOW_REPO_ROOT
|
||||||
|
unset BETTER_AUTH_SECRET DEER_FLOW_INTERNAL_AUTH_TOKEN
|
||||||
|
{args}
|
||||||
|
"""
|
||||||
|
subprocess.check_call([BASH_EXECUTABLE, "-lc", command])
|
||||||
|
|
||||||
|
recorded = marker.read_text(encoding="utf-8")
|
||||||
|
assert "-p deer-flow " in recorded, recorded
|
||||||
|
assert "-f docker-compose.yaml" in recorded, recorded
|
||||||
|
assert "--env-file ../.env" in recorded, recorded
|
||||||
|
assert "logs" in recorded, recorded
|
||||||
|
# deploy.sh exports interpolation defaults before every compose call;
|
||||||
|
# `logs --prod` must too, or the volume specs fail to parse.
|
||||||
|
assert "DEER_FLOW_HOME=" in recorded, recorded
|
||||||
|
assert "DEER_FLOW_HOME=unset" not in recorded, recorded
|
||||||
|
|
||||||
|
|
||||||
|
def test_logs_without_dev_containers_hints_at_prod_logs():
|
||||||
|
"""Silent empty output is the #5529 report: point at the production entry point."""
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
tmp_root = Path(tmpdir)
|
||||||
|
_seed_compose_file(tmp_root)
|
||||||
|
|
||||||
|
command = f"""
|
||||||
|
source '{SCRIPT_PATH}'
|
||||||
|
PROJECT_ROOT='{tmp_root}'
|
||||||
|
DOCKER_DIR='{tmp_root}'
|
||||||
|
require_compose_version() {{ :; }}
|
||||||
|
COMPOSE_CMD=true
|
||||||
|
unset DEER_FLOW_ROOT
|
||||||
|
logs
|
||||||
|
"""
|
||||||
|
result = subprocess.run(
|
||||||
|
[BASH_EXECUTABLE, "-lc", command],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.returncode == 0, result.stdout + result.stderr
|
||||||
|
assert "make prod-logs" in result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_aio_dood_socket_preflight_allows_windows_when_docker_reachable():
|
def test_aio_dood_socket_preflight_allows_windows_when_docker_reachable():
|
||||||
"""Windows Git Bash without /var/run/docker.sock proceeds when Docker daemon is reachable."""
|
"""Windows Git Bash without /var/run/docker.sock proceeds when Docker daemon is reachable."""
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
|||||||
@ -16,12 +16,15 @@ DOCKER_DIR="$PROJECT_ROOT/docker"
|
|||||||
# Use a filename relative to DOCKER_DIR (we always `cd` there) so Windows
|
# Use a filename relative to DOCKER_DIR (we always `cd` there) so Windows
|
||||||
# Docker Desktop does not receive a Git Bash `/c/...` path it cannot open.
|
# Docker Desktop does not receive a Git Bash `/c/...` path it cannot open.
|
||||||
COMPOSE_FILE="docker-compose-dev.yaml"
|
COMPOSE_FILE="docker-compose-dev.yaml"
|
||||||
|
# Dev stack project name. `logs --prod` swaps both values for the production
|
||||||
|
# stack started by `make up` (scripts/deploy.sh: project `deer-flow`).
|
||||||
|
COMPOSE_PROJECT="deer-flow-dev"
|
||||||
# Selected by require_compose_version: prefer the V2 plugin, else hyphenated binary.
|
# Selected by require_compose_version: prefer the V2 plugin, else hyphenated binary.
|
||||||
# Kept as an array so "docker compose" stays two words under set -u / quoting.
|
# Kept as an array so "docker compose" stays two words under set -u / quoting.
|
||||||
COMPOSE_BIN=(docker compose)
|
COMPOSE_BIN=(docker compose)
|
||||||
|
|
||||||
_refresh_compose_cmd() {
|
_refresh_compose_cmd() {
|
||||||
COMPOSE_CMD="${COMPOSE_BIN[*]} -p deer-flow-dev -f ${COMPOSE_FILE}"
|
COMPOSE_CMD="${COMPOSE_BIN[*]} -p ${COMPOSE_PROJECT} -f ${COMPOSE_FILE}"
|
||||||
}
|
}
|
||||||
_refresh_compose_cmd
|
_refresh_compose_cmd
|
||||||
|
|
||||||
@ -416,43 +419,73 @@ start() {
|
|||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# View Docker development logs
|
# View Docker logs. The dev stack (make docker-start) is the default;
|
||||||
|
# `--prod` tails the production stack started by `make up` (scripts/deploy.sh).
|
||||||
logs() {
|
logs() {
|
||||||
local service=""
|
local service=""
|
||||||
|
local is_prod=0
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--prod)
|
||||||
|
is_prod=1
|
||||||
|
;;
|
||||||
|
--frontend|--gateway|--nginx|--redis|--provisioner)
|
||||||
|
if [ -n "$service" ]; then
|
||||||
|
echo -e "${YELLOW}Only one service option is allowed (got --$service and $1).${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
service="${1#--}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${YELLOW}Unknown option: $1${NC}"
|
||||||
|
echo "Usage: $0 logs [--prod] [--frontend|--gateway|--nginx|--redis|--provisioner]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$is_prod" = 1 ]; then
|
||||||
|
# Target the same project deploy.sh started. Relative paths: this
|
||||||
|
# runs with cwd=$DOCKER_DIR.
|
||||||
|
COMPOSE_FILE="docker-compose.yaml"
|
||||||
|
COMPOSE_PROJECT="deer-flow"
|
||||||
|
# deploy.sh exports these before every compose invocation so the
|
||||||
|
# volume specs in docker-compose.yaml interpolate; without them even
|
||||||
|
# `logs` fails to parse the file on checkouts without a .env.
|
||||||
|
export DEER_FLOW_HOME="${DEER_FLOW_HOME:-$PROJECT_ROOT/backend/.deer-flow}"
|
||||||
|
export DEER_FLOW_CONFIG_PATH="${DEER_FLOW_CONFIG_PATH:-$DEER_FLOW_HOME/config.yaml}"
|
||||||
|
export DEER_FLOW_EXTENSIONS_CONFIG_PATH="${DEER_FLOW_EXTENSIONS_CONFIG_PATH:-$DEER_FLOW_HOME/extensions_config.json}"
|
||||||
|
export DEER_FLOW_REPO_ROOT="${DEER_FLOW_REPO_ROOT:-$PROJECT_ROOT}"
|
||||||
|
export BETTER_AUTH_SECRET="${BETTER_AUTH_SECRET:-placeholder}"
|
||||||
|
export DEER_FLOW_INTERNAL_AUTH_TOKEN="${DEER_FLOW_INTERNAL_AUTH_TOKEN:-placeholder}"
|
||||||
|
elif [ -z "$service" ]; then
|
||||||
|
# The dev and production stacks use different compose projects, so
|
||||||
|
# `make docker-logs` after `make up` would exit with empty output;
|
||||||
|
# point at the production entry point instead of staying silent.
|
||||||
|
if [ -z "$(cd "$DOCKER_DIR" && $COMPOSE_CMD ps -q 2>/dev/null)" ]; then
|
||||||
|
echo -e "${YELLOW}No dev containers are running.${NC}"
|
||||||
|
echo "Started the production stack with 'make up'? View its logs with 'make prod-logs'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
compose_preflight
|
compose_preflight
|
||||||
|
|
||||||
case "$1" in
|
# Append --env-file only after compose_preflight(): its Compose detection
|
||||||
--frontend)
|
# may rebuild COMPOSE_CMD, which would drop anything appended before it.
|
||||||
service="frontend"
|
if [ "$is_prod" = 1 ] && [ -f "$PROJECT_ROOT/.env" ]; then
|
||||||
echo -e "${BLUE}Viewing frontend logs...${NC}"
|
COMPOSE_CMD="$COMPOSE_CMD --env-file ../.env"
|
||||||
;;
|
fi
|
||||||
--gateway)
|
|
||||||
service="gateway"
|
if [ -n "$service" ]; then
|
||||||
echo -e "${BLUE}Viewing gateway logs...${NC}"
|
echo -e "${BLUE}Viewing $service logs...${NC}"
|
||||||
;;
|
elif [ "$is_prod" = 1 ]; then
|
||||||
--nginx)
|
echo -e "${BLUE}Viewing production stack logs...${NC}"
|
||||||
service="nginx"
|
else
|
||||||
echo -e "${BLUE}Viewing nginx logs...${NC}"
|
echo -e "${BLUE}Viewing all logs...${NC}"
|
||||||
;;
|
fi
|
||||||
--redis)
|
|
||||||
service="redis"
|
|
||||||
echo -e "${BLUE}Viewing redis logs...${NC}"
|
|
||||||
;;
|
|
||||||
--provisioner)
|
|
||||||
service="provisioner"
|
|
||||||
echo -e "${BLUE}Viewing provisioner logs...${NC}"
|
|
||||||
;;
|
|
||||||
"")
|
|
||||||
echo -e "${BLUE}Viewing all logs...${NC}"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo -e "${YELLOW}Unknown option: $1${NC}"
|
|
||||||
echo "Usage: $0 logs [--frontend|--gateway|--nginx|--redis|--provisioner]"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
cd "$DOCKER_DIR" && $COMPOSE_CMD logs -f $service
|
cd "$DOCKER_DIR" && $COMPOSE_CMD logs -f $service
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,7 +526,8 @@ help() {
|
|||||||
echo " init - Pull the sandbox image (speeds up first Pod startup)"
|
echo " init - Pull the sandbox image (speeds up first Pod startup)"
|
||||||
echo " start - Start Docker services (auto-detects sandbox mode from config.yaml)"
|
echo " start - Start Docker services (auto-detects sandbox mode from config.yaml)"
|
||||||
echo " restart - Restart all running Docker services"
|
echo " restart - Restart all running Docker services"
|
||||||
echo " logs [option] - View Docker development logs"
|
echo " logs [option] - View Docker logs (dev stack by default, production with --prod)"
|
||||||
|
echo " --prod View production stack logs (containers from 'make up')"
|
||||||
echo " --frontend View frontend logs only"
|
echo " --frontend View frontend logs only"
|
||||||
echo " --gateway View gateway logs only"
|
echo " --gateway View gateway logs only"
|
||||||
echo " --nginx View nginx logs only"
|
echo " --nginx View nginx logs only"
|
||||||
@ -518,7 +552,8 @@ main() {
|
|||||||
restart
|
restart
|
||||||
;;
|
;;
|
||||||
logs)
|
logs)
|
||||||
logs "$2"
|
shift
|
||||||
|
logs "$@"
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
stop
|
stop
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user