diff --git a/.gitignore b/.gitignore index 4d4ffc0..196f1e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ logs .env .test-case-*.sh +__pycache__ \ No newline at end of file diff --git a/Bun/.env.example b/Bun/.env.example index 4a707e4..b5cf544 100644 --- a/Bun/.env.example +++ b/Bun/.env.example @@ -1,3 +1,3 @@ -BASE_IMAGE=ubuntu-xray-transparent-test +BASE_IMAGE=docker.pchuan.top/forge/ubuntu:latest IMAGE=bun-test BUN_REGISTRY=https://registry.npmmirror.com/ diff --git a/Bun/Dockerfile b/Bun/Dockerfile index a58670d..f5ef717 100644 --- a/Bun/Dockerfile +++ b/Bun/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ubuntu-xray-transparent-test +ARG BASE_IMAGE=docker.pchuan.top/forge/ubuntu:latest FROM ${BASE_IMAGE} ARG BUN_REGISTRY=https://registry.npmmirror.com/ diff --git a/Bun/compose.yml b/Bun/compose.yml index 607b68e..8e292f8 100644 --- a/Bun/compose.yml +++ b/Bun/compose.yml @@ -3,7 +3,7 @@ services: build: context: . args: - BASE_IMAGE: ${BASE_IMAGE:-ubuntu-xray-transparent-test} + BASE_IMAGE: ${BASE_IMAGE:-docker.pchuan.top/forge/ubuntu:latest} BUN_REGISTRY: ${BUN_REGISTRY:-https://registry.npmmirror.com/} image: ${IMAGE:-bun-test} environment: diff --git a/Ubuntu/Dockerfile b/Ubuntu/Dockerfile index 5318e3a..6179aea 100644 --- a/Ubuntu/Dockerfile +++ b/Ubuntu/Dockerfile @@ -24,7 +24,7 @@ RUN set -eux; \ {} +; \ fi; \ apt-get update; \ - apt-get install -y --no-install-recommends ca-certificates curl iptables jq unzip xz-utils; \ + apt-get install -y --no-install-recommends ca-certificates curl git iptables jq unzip xz-utils; \ rm -rf /var/lib/apt/lists/* # Install s6-overlay as PID 1 / service supervisor. diff --git a/scripts/build-all.py b/scripts/build-all.py new file mode 100644 index 0000000..bbc3b46 --- /dev/null +++ b/scripts/build-all.py @@ -0,0 +1,71 @@ +""" +构建并上传全部 forge 编译环境镜像 + +默认按依赖顺序构建 ubuntu 和 bun + +1. 先构建并上传 docker.pchuan.top/forge/ubuntu:latest +2. 再构建并上传 docker.pchuan.top/forge/bun:latest +3. bun 默认依赖刚上传的 ubuntu 镜像 + +- --no-push 只构建不上传 +- --force-build 使用 docker build --no-cache +- --platform linux/amd64 指定构建平台 +""" + +import argparse +import subprocess +import sys +from pathlib import Path + + +# 基础配置 +ROOT = Path(__file__).resolve().parents[1] +BUILD_SCRIPT = ROOT / "scripts" / "build.py" +TARGETS = ["ubuntu", "bun"] + +parser = argparse.ArgumentParser(description="构建并上传全部 forge 编译环境镜像") +parser.add_argument("--registry", default="docker.pchuan.top", help="镜像仓库地址") +parser.add_argument("--namespace", default="forge", 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("--bun-registry", default="", help="覆盖 bun 的 npm registry") +args = parser.parse_args() + +ubuntu_image = f"{args.registry.rstrip('/')}/{args.namespace.strip('/')}/ubuntu:{args.tag}" + +for target in TARGETS: + command = [ + sys.executable, + str(BUILD_SCRIPT), + target, + "--registry", + args.registry, + "--namespace", + args.namespace, + "--tag", + args.tag, + ] + + if args.no_push: + command += ["--no-push"] + + if args.force_build: + command += ["--force-build"] + + if args.platform: + command += ["--platform", args.platform] + + # bun 必须显式依赖本次构建的 ubuntu 镜像地址 + if target == "bun": + command += ["--base-image", ubuntu_image] + if args.bun_registry: + command += ["--bun-registry", args.bun_registry] + + print(f"[RUN] Build target {target}") + print("[INFO] " + " ".join(command)) + subprocess.run(command, cwd=ROOT, check=True) + print(f"[PASS] Build target {target}") + +print("[PASS] build all completed") diff --git a/scripts/build.py b/scripts/build.py new file mode 100644 index 0000000..7578521 --- /dev/null +++ b/scripts/build.py @@ -0,0 +1,129 @@ +""" +构建并上传 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")