Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
ci / backend (pull_request) Has been cancelled
ci / frontend (pull_request) Has been cancelled
ci / delivery (pull_request) Has been cancelled
240 lines
8.9 KiB
Bash
240 lines
8.9 KiB
Bash
#!/usr/bin/env zsh
|
||
|
||
# Docker prerequisites for planet.sh; lifecycle and logging remain in planet.sh.
|
||
DOCKER_MIN_BUILDX_VERSION="0.17.0"
|
||
|
||
compose_available() {
|
||
docker compose version >/dev/null 2>&1
|
||
}
|
||
|
||
compose_v1_available() {
|
||
command -v docker-compose >/dev/null 2>&1
|
||
}
|
||
|
||
buildx_version() {
|
||
docker buildx version 2>/dev/null | awk '{print $2}' | sed 's/^v//'
|
||
}
|
||
|
||
buildx_meets_minimum() {
|
||
local current_version="$1"
|
||
local required_version="$2"
|
||
|
||
[ -n "$current_version" ] || return 1
|
||
[ -n "$required_version" ] || return 1
|
||
|
||
if [ "$(printf '%s\n%s\n' "$required_version" "$current_version" | sort -V | head -n 1)" = "$required_version" ]; then
|
||
return 0
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
docker_daemon_available() {
|
||
command -v docker >/dev/null 2>&1 && command docker info >/dev/null 2>&1
|
||
}
|
||
|
||
docker_uses_local_engine() {
|
||
local endpoint="${DOCKER_HOST:-}"
|
||
if [ -n "${DOCKER_CONTEXT:-}" ] || [ -z "$endpoint" ]; then
|
||
if command -v docker >/dev/null 2>&1; then
|
||
endpoint="$(command docker context inspect --format '{{.Endpoints.docker.Host}}' 2>/dev/null)" || return 1
|
||
elif [ -n "${DOCKER_CONTEXT:-}" ]; then
|
||
return 1
|
||
else
|
||
endpoint="unix:///var/run/docker.sock"
|
||
fi
|
||
fi
|
||
case "$endpoint" in
|
||
unix:///var/run/docker.sock|unix:///run/docker.sock) return 0 ;;
|
||
*) return 1 ;;
|
||
esac
|
||
}
|
||
|
||
docker_desktop_present() {
|
||
local docker_path=""
|
||
[ -d /mnt/wsl/docker-desktop ] && return 0
|
||
docker_path="$(command -v docker 2>/dev/null || true)"
|
||
[ -n "$docker_path" ] || return 1
|
||
docker_path="$(readlink -f "$docker_path" 2>/dev/null || true)"
|
||
[[ "$docker_path" == */docker-desktop/* ]]
|
||
}
|
||
|
||
docker_systemd_unit_exists() {
|
||
command -v systemctl >/dev/null 2>&1 || return 1
|
||
[ "$(systemctl show --property=LoadState --value "$1" 2>/dev/null)" = "loaded" ]
|
||
}
|
||
|
||
docker_socket_needs_group_access() {
|
||
[ -S /var/run/docker.sock ] && { [ ! -r /var/run/docker.sock ] || [ ! -w /var/run/docker.sock ]; }
|
||
}
|
||
|
||
docker_require_sudo() {
|
||
[ "$(id -u)" -eq 0 ] && return 0
|
||
if ! command -v sudo >/dev/null 2>&1; then
|
||
log_error "缺少 sudo,无法安装或配置 Docker;请先由管理员安装 sudo 并授予当前用户权限。"
|
||
return 1
|
||
fi
|
||
# Authenticate in the foreground, before command output is redirected to a log.
|
||
sudo -v
|
||
}
|
||
|
||
docker_as_root() {
|
||
if [ "$(id -u)" -eq 0 ]; then
|
||
"$@"
|
||
else
|
||
sudo -- "$@"
|
||
fi
|
||
}
|
||
|
||
refresh_docker_group() {
|
||
[ "$(id -u)" -ne 0 ] || return 0
|
||
command -v docker >/dev/null 2>&1 || return 0
|
||
docker_uses_local_engine || return 0
|
||
docker_socket_needs_group_access || return 0
|
||
|
||
local username="$(id -un)"
|
||
local groups="$(id -nG "$username")"
|
||
[[ " $groups " == *" docker "* ]] || return 0
|
||
if [ "${PLANET_DOCKER_GROUP_REEXEC:-0}" = "1" ]; then
|
||
log_error "刷新组权限后仍无法访问 Docker socket,请检查 socket 的属组和权限。"
|
||
return 1
|
||
fi
|
||
docker_require_sudo || return 1
|
||
log_note "刷新 Docker 组权限,以当前用户继续执行。"
|
||
# Preserve WSL interop/tool paths even when sudo applies secure_path.
|
||
# argv stays an array: repository paths and arguments are never evaluated as shell text.
|
||
exec sudo -E -u "$username" -g docker -- env PLANET_DOCKER_GROUP_REEXEC=1 "PATH=$PATH" \
|
||
"$(command -v zsh)" "$SCRIPT_DIR/planet.sh" "$@"
|
||
}
|
||
|
||
docker_bootstrap_os_id() {
|
||
[ -r /etc/os-release ] || return 1
|
||
(. /etc/os-release; printf '%s\n' "$ID")
|
||
}
|
||
|
||
install_docker_prerequisites() {
|
||
local log_file="$PLANET_STATE_DIR/docker_install.log"
|
||
local engine_package="docker.io"
|
||
local compose_package="docker-compose-v2"
|
||
local buildx_package="docker-buildx"
|
||
local -a packages=()
|
||
|
||
if [ "$(docker_bootstrap_os_id)" != "ubuntu" ] || ! command -v apt-get >/dev/null 2>&1; then
|
||
log_error "Docker 自动安装目前支持 Ubuntu / Ubuntu WSL;请先安装本系统的 Docker Engine、Compose v2 和 Buildx。"
|
||
return 1
|
||
fi
|
||
if command -v dpkg-query >/dev/null 2>&1 &&
|
||
[ "$(dpkg-query -W -f='${Status}' docker-ce-cli 2>/dev/null)" = "install ok installed" ]; then
|
||
engine_package="docker-ce"
|
||
compose_package="docker-compose-plugin"
|
||
buildx_package="docker-buildx-plugin"
|
||
fi
|
||
if ! command -v docker >/dev/null 2>&1 ||
|
||
{ ! docker_daemon_available && ! command -v dockerd >/dev/null 2>&1; }; then
|
||
packages+=("$engine_package")
|
||
fi
|
||
compose_available || packages+=("$compose_package")
|
||
buildx_meets_minimum "$(buildx_version)" "$DOCKER_MIN_BUILDX_VERSION" || packages+=("$buildx_package")
|
||
[ "${#packages[@]}" -gt 0 ] || return 0
|
||
|
||
docker_require_sudo || return 1
|
||
log_step "安装 Docker 依赖: ${packages[*]}"
|
||
if ! run_command_quiet_unless_verbose "$log_file" docker_as_root apt-get update --error-on=any ||
|
||
! run_command_quiet_unless_verbose "$log_file" docker_as_root apt-get install -y "${packages[@]}"; then
|
||
log_error "Docker 依赖安装失败,请检查软件源、网络和 apt 错误。"
|
||
tail -20 "$log_file" 2>/dev/null || true
|
||
return 1
|
||
fi
|
||
rehash
|
||
}
|
||
|
||
start_local_docker_engine() {
|
||
local log_file="$PLANET_STATE_DIR/docker_service.log"
|
||
if ! docker_systemd_unit_exists docker.service; then
|
||
log_error "没有可用的 docker.service;请确认 Docker Engine 已安装且 systemd 正常运行。"
|
||
log_note "Ubuntu WSL 可在 /etc/wsl.conf 的 [boot] 下启用 systemd=true,再重启 WSL。"
|
||
return 1
|
||
fi
|
||
docker_require_sudo || return 1
|
||
if ! run_command_quiet_unless_verbose "$log_file" docker_as_root systemctl enable --now docker.service; then
|
||
log_error "Docker Engine 启动失败。"
|
||
tail -20 "$log_file" 2>/dev/null || true
|
||
log_note "查看原因: journalctl -u docker.service -n 50 --no-pager"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
ensure_docker_user_access() {
|
||
[ "$(id -u)" -ne 0 ] || return 0
|
||
docker_socket_needs_group_access || return 0
|
||
docker_require_sudo || return 1
|
||
ensure_system_command usermod passwd "usermod" || return 1
|
||
log_note "将当前用户加入 docker 组(该组可管理本机 Docker)。"
|
||
docker_as_root usermod -aG docker "$(id -un)" || return 1
|
||
refresh_docker_group "$@"
|
||
}
|
||
|
||
ensure_docker_runtime() {
|
||
if docker_daemon_available && compose_available &&
|
||
buildx_meets_minimum "$(buildx_version)" "$DOCKER_MIN_BUILDX_VERSION"; then
|
||
return 0
|
||
fi
|
||
if docker_desktop_present; then
|
||
log_error "检测到 Docker Desktop,但当前 WSL 的 Docker / Compose / Buildx 尚不可用。"
|
||
log_note "请启动 Docker Desktop,并为当前发行版启用 WSL Integration。"
|
||
return 1
|
||
fi
|
||
if ! docker_uses_local_engine; then
|
||
log_error "当前 Docker 使用其他 context 或远程/rootless endpoint,请先检查该环境的连接和插件。"
|
||
return 1
|
||
fi
|
||
install_docker_prerequisites || return 1
|
||
if ! command -v docker >/dev/null 2>&1 || ! compose_available ||
|
||
! buildx_meets_minimum "$(buildx_version)" "$DOCKER_MIN_BUILDX_VERSION"; then
|
||
log_error "安装后 Docker CLI、Compose v2 或 Buildx >= ${DOCKER_MIN_BUILDX_VERSION} 仍不可用。"
|
||
return 1
|
||
fi
|
||
if ! docker_daemon_available; then
|
||
start_local_docker_engine || return 1
|
||
ensure_docker_user_access "$@" || return 1
|
||
fi
|
||
if ! docker_daemon_available; then
|
||
log_error "Docker 已安装,但当前用户仍无法连接 daemon。"
|
||
log_docker_daemon_diagnostics
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
log_docker_daemon_diagnostics() {
|
||
if ! command -v docker >/dev/null 2>&1; then
|
||
log_note "未找到 Docker CLI,尚未安装 Docker;Ubuntu / WSL 请先执行 ./planet.sh init。"
|
||
return 0
|
||
fi
|
||
if docker_desktop_present; then
|
||
log_note "请启动 Docker Desktop,并确认当前 WSL 已启用 WSL Integration。"
|
||
return 0
|
||
fi
|
||
if ! docker_uses_local_engine; then
|
||
log_note "当前使用其他 Docker endpoint;请检查 docker context ls 和 docker info。"
|
||
return 0
|
||
fi
|
||
if docker_socket_needs_group_access; then
|
||
log_note "Docker socket 存在,但当前用户没有读写权限;请执行 ./planet.sh init 配置 docker 组。"
|
||
return 0
|
||
fi
|
||
if ! docker_systemd_unit_exists docker.service; then
|
||
log_note "未检测到可用的 docker.service;Ubuntu / WSL 请执行 ./planet.sh init 检查安装。"
|
||
return 0
|
||
fi
|
||
if [ "$(systemctl is-active docker.service 2>/dev/null)" = "active" ]; then
|
||
log_note "Docker 服务已启动,但 CLI 无法连接;请检查 docker context ls 和 docker info。"
|
||
return 0
|
||
fi
|
||
if docker_systemd_unit_exists docker.socket; then
|
||
log_note "可先执行: sudo systemctl enable --now docker.socket && sudo systemctl restart docker.service"
|
||
else
|
||
log_note "可先执行: sudo systemctl enable --now docker.service"
|
||
fi
|
||
log_note "如仍失败,查看: journalctl -u docker.service -n 50 --no-pager"
|
||
}
|