Files
2026-05-18 00:50:46 +08:00

130 lines
3.8 KiB
Python

"""
构建并上传 forge 编译环境镜像
默认仓库是 docker.pchuan.top/forge
1. 读取目标容器配置
2. 检查目标 Dockerfile
3. 根据目标目录源码时间生成 SOURCE_VERSION
4. 执行 docker build 并打上目标 tag
5. 默认执行 docker push 上传镜像
- target 支持 ubuntu 和 bun
- --no-push 只构建不上传
- --force-build 使用 docker build --no-cache
- --platform linux/amd64 指定构建平台
"""
import argparse
import subprocess
from pathlib import Path
# 基础配置
ROOT = Path(__file__).resolve().parents[1]
REGISTRY = "docker.pchuan.top"
NAMESPACE = "forge"
# 目标容器配置
TARGETS = {
"ubuntu": {
"context": "ubuntu",
"image_name": "ubuntu",
"build_args": {},
},
"bun": {
"context": "bun",
"image_name": "bun",
"build_args": {
"BASE_IMAGE": "docker.pchuan.top/forge/ubuntu:latest",
"BUN_REGISTRY": "https://registry.npmmirror.com/",
},
},
}
parser = argparse.ArgumentParser(description="构建并上传 forge 编译环境镜像")
parser.add_argument("target", choices=TARGETS.keys(), help="目标容器")
parser.add_argument("--registry", default=REGISTRY, help="镜像仓库地址")
parser.add_argument("--namespace", default=NAMESPACE, help="镜像命名空间")
parser.add_argument("--tag", default="latest", help="镜像 tag")
parser.add_argument("--no-push", action="store_true", help="只构建不上传")
parser.add_argument("--force-build", action="store_true", help="强制无缓存构建")
parser.add_argument("--platform", default="", help="目标平台,例如 linux/amd64")
parser.add_argument("--base-image", default="", help="覆盖 bun 的基础镜像")
parser.add_argument("--bun-registry", default="", help="覆盖 bun 的 npm registry")
args = parser.parse_args()
target = TARGETS[args.target]
context = ROOT / target["context"]
dockerfile = context / "Dockerfile"
image = (
f"{args.registry.rstrip('/')}/"
f"{args.namespace.strip('/')}/"
f"{target['image_name']}:{args.tag}"
)
if not dockerfile.exists():
print(f"[NO] {args.target} Dockerfile exists")
print(f"reason: not found: {dockerfile}")
raise SystemExit(1)
# 真实 .env 和 logs 不参与 SOURCE_VERSION
source_files = [
path
for path in context.rglob("*")
if path.is_file()
and path.name != ".env"
and "logs" not in path.relative_to(context).parts
]
source_version = str(max((path.stat().st_mtime_ns for path in source_files), default=0))
build_args = dict(target["build_args"])
build_args["SOURCE_VERSION"] = source_version
if args.target == "bun" and args.base_image:
build_args["BASE_IMAGE"] = args.base_image
if args.target == "bun" and args.bun_registry:
build_args["BUN_REGISTRY"] = args.bun_registry
# 直接使用 Dockerfile 构建,不读取目标目录里的 .env
build_command = [
"docker",
"build",
"--file",
str(dockerfile),
"--tag",
image,
]
for name, value in build_args.items():
build_command += ["--build-arg", f"{name}={value}"]
if args.platform:
build_command += ["--platform", args.platform]
if args.force_build:
build_command += ["--no-cache"]
build_command += [str(context)]
print(f"[INFO] target={args.target}")
print(f"[INFO] image={image}")
print("[RUN] Build image")
print("[INFO] " + " ".join(build_command))
subprocess.run(build_command, cwd=ROOT, check=True)
print("[PASS] Build image")
# 默认上传镜像,使用 --no-push 可以只构建不上传
if not args.no_push:
push_command = ["docker", "push", image]
print("[RUN] Push image")
print("[INFO] " + " ".join(push_command))
subprocess.run(push_command, cwd=ROOT, check=True)
print("[PASS] Push image")
else:
print("[INFO] --no-push, skip docker push")
print("[PASS] build script completed")