diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 350afb3..4077b1c 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -36,52 +36,45 @@ env: APT_MIRROR_UBUNTU_SECURITY_HTTP: http://mirrors.tuna.tsinghua.edu.cn/ubuntu-security HTTP_PROXY_EXAMPLE: "" HTTPS_PROXY_EXAMPLE: "" + # ========== Docker 官方镜像国内加速(Runner 拉 node:alpine / buildkit 等最常见卡点) ========== + # 注意:如果你知道哪个镜像源在内网最快,可以在仓库 Variables 里覆盖 DOCKER_REGISTRY_MIRROR_1/2/3 + DOCKER_REGISTRY_MIRROR_1: https://docker.m.daocloud.io + DOCKER_REGISTRY_MIRROR_2: https://docker.1panel.live + DOCKER_REGISTRY_MIRROR_3: https://docker.1ms.run + # Dockerfile 内 FROM 前缀替换(ARG DOCKER_MIRROR=…),避免 buildx build 内部 FROM node:22-alpine 再被 Hub 限速 + DOCKER_MIRROR_PREFIX: docker.m.daocloud.io/library/ + NODE_BUILDER_TAG: "22-alpine" + NODE_RUNTIME_TAG: "22-alpine" jobs: preflight: name: Preflight 语法与构建检查 runs-on: ubuntu-latest timeout-minutes: 15 - container: - image: node:22-bookworm-slim + # 关键修复:去掉 container.image: node:22-bookworm-slim + # 原因:Runner 在 Set up job 阶段会先 docker pull 这个镜像,国内连 Docker Hub 被限速会一直卡转圈。 + # 直接在 Runner 宿主机上执行(通常自带 node 20+);为保证 Node 22 一致性,用 actions/setup-node 主动安装。 steps: - - name: 安装基础运行依赖(git/ca-certificates,已换清华 TUNA 源) + - name: 安装基础运行依赖(git/curl/ca-certificates,Runner 自带) run: | set -euxo pipefail - # ========== Step 0:先用 HTTP 清华源装 ca-certificates(解鸡生蛋:HTTPS 需要证书,证书得先用 HTTP 装) ========== - SOURCES_FILE="/etc/apt/sources.list.d/debian.sources" - if [ -f "${SOURCES_FILE}" ]; then - sed -i "s|http://deb.debian.org/debian|${APT_MIRROR_DEBIAN_HTTP}|g" "${SOURCES_FILE}" - sed -i "s|http://deb.debian.org/debian-security|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|g" "${SOURCES_FILE}" - sed -i "s|https://deb.debian.org/debian|${APT_MIRROR_DEBIAN_HTTP}|g" "${SOURCES_FILE}" - sed -i "s|https://deb.debian.org/debian-security|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|g" "${SOURCES_FILE}" - else - if [ -f /etc/apt/sources.list ]; then - sed -i "s|http://deb.debian.org/debian|${APT_MIRROR_DEBIAN_HTTP}|g" /etc/apt/sources.list - sed -i "s|https://deb.debian.org/debian|${APT_MIRROR_DEBIAN_HTTP}|g" /etc/apt/sources.list - sed -i "s|http://security.debian.org/debian-security|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|g" /etc/apt/sources.list - sed -i "s|https://security.debian.org/debian-security|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|g" /etc/apt/sources.list - fi - fi - apt-get update -qq - DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends ca-certificates - update-ca-certificates 2>/dev/null || true + command -v git || (apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends git ca-certificates) + command -v curl || (apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends curl) + command -v node && node -v || true + command -v npm && npm -v || true - # ========== Step 1:现在有了证书,换成 HTTPS 清华源(更安全)+ 装 git/curl ========== - if [ -f "${SOURCES_FILE}" ]; then - sed -i "s|${APT_MIRROR_DEBIAN_HTTP}|${APT_MIRROR_DEBIAN}|g" "${SOURCES_FILE}" - sed -i "s|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|${APT_MIRROR_DEBIAN_SECURITY}|g" "${SOURCES_FILE}" - else - if [ -f /etc/apt/sources.list ]; then - sed -i "s|${APT_MIRROR_DEBIAN_HTTP}|${APT_MIRROR_DEBIAN}|g" /etc/apt/sources.list - sed -i "s|${APT_MIRROR_DEBIAN_SECURITY_HTTP}|${APT_MIRROR_DEBIAN_SECURITY}|g" /etc/apt/sources.list - fi - fi - apt-get update -qq - DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends git curl - rm -rf /var/lib/apt/lists/* + - name: 使用 Node.js 22(若 Runner 自带版本不足 22 则自动下载) + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: 校验 Node + npm + git 可用 + run: | + set -euxo pipefail # 全局 git 代理:任何 git clone/fetch/pull https://github.com/... 自动走 gh-proxy 前缀 git config --system url."${GH_PROXY_PREFIX}https://github.com/".insteadOf "https://github.com/" || true + # npm registry 走 npmmirror,避免后续 npm ci 也卡 + npm config set registry https://registry.npmmirror.com >/dev/null 2>&1 || true git --version node -v npm -v @@ -166,6 +159,48 @@ jobs: sudo git config --system url."${GH_PROXY_PREFIX}https://github.com/".insteadOf "https://github.com/" || true # npm:强制用 npmmirror(与 preflight 对齐) npm config set registry https://registry.npmmirror.com >/dev/null 2>&1 || true + + # ========== 关键修复:宿主机 Docker daemon 先注入国内 registry mirrors ========== + # 原因:Runner 任何 docker pull / buildx create(moby/buildkit 拉基础镜像)都要先经过 daemon + # 如果直接连 docker.io,国内会被限速到 <200KB/s,导致 Setup / BuildKit 拉镜像一直卡。 + DOCKER_DAEMON_CONF="/etc/docker/daemon.json" + if command -v docker >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then + sudo mkdir -p /etc/docker 2>/dev/null || true + OLD_MIRRORS_JSON="{}" + if [ -f "${DOCKER_DAEMON_CONF}" ]; then + OLD_MIRRORS_JSON="$(sudo cat "${DOCKER_DAEMON_CONF}" 2>/dev/null || echo "{}")" + fi + # 用 python3 合成合法 JSON(避免 bash 手搓 JSON 转义坑) + NEW_DAEMON_JSON="$( + python3 - "$OLD_MIRRORS_JSON" \ + "${DOCKER_REGISTRY_MIRROR_1}" "${DOCKER_REGISTRY_MIRROR_2}" "${DOCKER_REGISTRY_MIRROR_3}" <<'PY' +import json, sys +try: + data = json.loads(sys.argv[1] or "{}") +except Exception: + data = {} +mirrors_envs = [m for m in sys.argv[2:5] if m] +existing = data.get("registry-mirrors") or [] +if isinstance(existing, list): + combined = list(dict.fromkeys([*mirrors_envs, *existing])) +else: + combined = mirrors_envs +if combined: + data["registry-mirrors"] = combined +data.setdefault("log-driver", "json-file") +print(json.dumps(data, ensure_ascii=False, indent=2)) +PY + )" + echo "${NEW_DAEMON_JSON}" | sudo tee "${DOCKER_DAEMON_CONF}" >/dev/null + echo "[Docker] daemon.json 已注入 registry-mirrors:" + sudo cat "${DOCKER_DAEMON_CONF}" 2>/dev/null || true + # 非侵入式重启 docker;runner 没 systemd 时用 service 兜底;失败也跳过(buildkitd.toml 内部仍有 mirrors) + sudo systemctl restart docker 2>/dev/null \ + || sudo service docker restart 2>/dev/null \ + || echo "[Docker] systemctl/service 重启失败,跳过(后续 buildx builder 内部仍有 registry mirrors)" + sleep 3 2>/dev/null || true + fi + echo "环境准备完成" cat /etc/os-release 2>/dev/null | head -4 || true docker --version @@ -307,6 +342,9 @@ jobs: BUILD_ARGS=( --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg NODE_ENV=production + --build-arg DOCKER_MIRROR="${DOCKER_MIRROR_PREFIX}" + --build-arg NODE_BUILDER_TAG="${NODE_BUILDER_TAG}" + --build-arg NODE_RUNTIME_TAG="${NODE_RUNTIME_TAG}" ) # 说明:暂时关闭 registry 分层缓存(buildkitd 推 buildcache 也要认证,同样会触发 Gitea 1.27 OAuth2 空 token) # 改为本地 Docker daemon 缓存,已构建的层会自动复用,下次 build 速度几乎相同 diff --git a/Dockerfile b/Dockerfile index 159407e..8b8a59c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,20 @@ # 支持: linux/amd64, linux/arm64, linux/arm/v7 +# ========== 支持在 CI 注入国内镜像源前缀拉 node 基础镜像 ========== +# 用法:docker buildx build \ +# --build-arg DOCKER_MIRROR=docker.m.daocloud.io/library/ \ +# --build-arg NODE_BUILDER_TAG=22-alpine \ +# --build-arg NODE_RUNTIME_TAG=22-alpine \ +# ... +# 缺省(DOCKER_MIRROR 为空)等价于原来的 node:22-alpine,本地开发不变 +ARG DOCKER_MIRROR="" +ARG NODE_BUILDER_TAG="22-alpine" +ARG NODE_RUNTIME_TAG="22-alpine" + # ========================================== # 第一阶段:依赖安装与构建阶段 # ========================================== -FROM node:22-alpine AS builder +FROM ${DOCKER_MIRROR}node:${NODE_BUILDER_TAG} AS builder WORKDIR /app ENV NODE_OPTIONS="--max-old-space-size=1024" @@ -34,7 +45,7 @@ RUN npm prune --omit=dev # ========================================== # 第二阶段:运行阶段 # ========================================== -FROM node:22-alpine AS runtime +FROM ${DOCKER_MIRROR}node:${NODE_RUNTIME_TAG} AS runtime USER root WORKDIR /app