Files
thebet365/docs/docker/build-and-export-images.sh
Mars be5b4a4921 优化 Docker 镜像打包脚本并修复管理端赛事操作列布局
- 打包脚本默认 tag 改为 latest,支持 --service 单服务构建/导出
- 新增 api/player/admin 三个独立 bat,更新文档与 manifest
- 联赛赛事面板操作列加宽、横向滚动与按钮换行,避免操作按钮被裁切

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 16:35:49 +08:00

193 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 构建 api / player / admin 生产镜像并导出为 tar
# 用法:
# ./docs/docker/build-and-export-images.sh
# ./docs/docker/build-and-export-images.sh --service admin
# ./docs/docker/build-and-export-images.sh --tag v1.2.3
# ./build-and-export-images.sh --use-cache
# ./build-and-export-images.sh --export-only
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$ROOT"
COMPOSE_FILE="docker-compose.prod.yml"
ENV_FILE=".env.docker"
OUTPUT=""
SERVICE="all"
NO_CACHE=1
EXPORT_ONLY=0
TAG="${IMAGE_TAG:-}"
default_tag() { echo "latest"; }
default_output() {
local svc="$1" image_tag="$2"
if [[ "$svc" == "all" ]]; then
echo "thebet365-images-${image_tag}.tar"
else
echo "thebet365-${svc}-${image_tag}.tar"
fi
}
validate_tag() {
printf '%s' "$1" | grep -Eq '^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$' ||
{ echo "错误: 镜像 tag 不合法: $1" >&2; exit 1; }
}
validate_service() {
case "$1" in
all|api|player|admin) ;;
*) echo "错误: --service 无效: $1(可选 api / player / admin / all" >&2; exit 1 ;;
esac
}
usage() {
cat <<'EOF'
用法: docs/docker/build-and-export-images.sh [选项]
选项:
--service SVC api | player | admin | all默认 all
--tag TAG 镜像 tag默认 latest
--use-cache 构建时使用 Docker 缓存(默认 --no-cache
--export-only 跳过构建,仅导出已有指定 tag 镜像
--output PATH 导出文件路径
-h, --help 显示帮助
示例:
./docs/docker/build-and-export-images.sh --service admin
./docs/docker/build-and-export-images.sh --service admin --export-only
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--tag)
TAG="${2:?缺少 --tag 参数值}"
shift 2
;;
--service)
SERVICE="${2:?缺少 --service 参数值}"
shift 2
;;
--use-cache) NO_CACHE=0; shift ;;
--export-only) EXPORT_ONLY=1; shift ;;
--output)
OUTPUT="${2:?缺少 --output 参数值}"
shift 2
;;
-h|--help) usage; exit 0 ;;
*) echo "未知参数: $1" >&2; usage; exit 1 ;;
esac
done
if [[ ! -f "$COMPOSE_FILE" ]]; then
echo "错误: 未找到 ${COMPOSE_FILE}(目录: ${ROOT}" >&2
exit 1
fi
if [[ ! -f "$ENV_FILE" ]]; then
if [[ -f ".env.docker.example" ]]; then
echo "警告: 未找到 ${ENV_FILE},将使用 .env.docker.example生产部署请复制为 .env.docker 并修改密钥)"
ENV_FILE=".env.docker.example"
else
echo "错误: 未找到 ${ENV_FILE} 或 .env.docker.example" >&2
exit 1
fi
fi
validate_service "$SERVICE"
TAG="${TAG:-$(default_tag)}"
validate_tag "$TAG"
OUTPUT="${OUTPUT:-$(default_output "$SERVICE" "$TAG")}"
if [[ "$SERVICE" == "all" ]]; then
BUILD_SERVICES=(api player admin)
else
BUILD_SERVICES=("$SERVICE")
fi
if [[ "$EXPORT_ONLY" -eq 0 ]]; then
echo "==> 构建镜像: ${BUILD_SERVICES[*]} (tag: $TAG)"
BUILD_ARGS=(compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build)
if [[ "$NO_CACHE" -eq 1 ]]; then
BUILD_ARGS+=(--no-cache)
fi
BUILD_ARGS+=("${BUILD_SERVICES[@]}")
IMAGE_TAG="$TAG" docker "${BUILD_ARGS[@]}"
fi
OUTPUT_PATH="$OUTPUT"
if [[ "$OUTPUT" != /* ]] && [[ "$OUTPUT" != [A-Za-z]:* ]]; then
OUTPUT_PATH="$ROOT/$OUTPUT"
fi
SAVE_ARGS=()
for svc in "${BUILD_SERVICES[@]}"; do
SAVE_ARGS+=("thebet365-${svc}:$TAG")
done
echo "==> 导出镜像 ($SERVICE) -> $OUTPUT_PATH"
docker save "${SAVE_ARGS[@]}" -o "$OUTPUT_PATH"
MANIFEST_PATH="${OUTPUT_PATH%.tar}.manifest.txt"
BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
GIT_COMMIT="unknown"
GIT_DIRTY="unknown"
if command -v git >/dev/null 2>&1 && git rev-parse HEAD >/dev/null 2>&1; then
GIT_COMMIT="$(git rev-parse HEAD)"
if git diff --quiet && git diff --cached --quiet; then
GIT_DIRTY="false"
else
GIT_DIRTY="true"
fi
fi
CHECKSUM="unavailable"
if command -v sha256sum >/dev/null 2>&1; then
CHECKSUM="$(sha256sum "$OUTPUT_PATH" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
CHECKSUM="$(shasum -a 256 "$OUTPUT_PATH" | awk '{print $1}')"
fi
{
echo "tag=$TAG"
echo "service=$SERVICE"
echo "built_at=$BUILT_AT"
echo "git_commit=$GIT_COMMIT"
echo "git_dirty=$GIT_DIRTY"
echo "tar=$(basename "$OUTPUT_PATH")"
echo "tar_sha256=$CHECKSUM"
for svc in "${BUILD_SERVICES[@]}"; do
if [[ "$SERVICE" == "all" ]]; then
echo "${svc}_image=thebet365-${svc}:$TAG"
echo "${svc}_image_id=$(docker image inspect "thebet365-${svc}:$TAG" --format '{{.Id}}')"
else
echo "image=thebet365-${svc}:$TAG"
echo "image_id=$(docker image inspect "thebet365-${svc}:$TAG" --format '{{.Id}}')"
fi
done
} > "$MANIFEST_PATH"
if command -v du >/dev/null 2>&1; then
SIZE="$(du -h "$OUTPUT_PATH" | awk '{print $1}')"
echo "完成: $OUTPUT_PATH ($SIZE)"
else
echo "完成: $OUTPUT_PATH"
fi
echo "Manifest: $MANIFEST_PATH"
RECREATE="$SERVICE"
if [[ "$SERVICE" == "all" ]]; then
RECREATE="api player admin"
fi
cat <<EOF
服务器导入并重建:
docker load -i $(basename "$OUTPUT_PATH")
docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate $RECREATE
EOF