初始化 ToolBox 开发者工具箱。
CI / docker (push) Successful in 11m42s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
renjue
2026-06-25 11:15:38 +08:00
commit f1502ed08c
66 changed files with 18020 additions and 0 deletions
+263
View File
@@ -0,0 +1,263 @@
# ToolBoxVue 前端 CIGitea Actions
#
# 单 jobnpm test + Docker 多架构构建/推送
# 约定:Dockerfile 在仓库根目录
#
# 前置:act_runnerubuntu-latest + docker.sock)、Secret REGISTRY_TOKEN
# VariablesREGISTRY、IMAGE_NAME、DOCKER_IMAGE_PREFIX、RUN_NPM_TEST 等
name: CI
on:
push:
branches: [main, master]
tags: ['v*']
pull_request:
workflow_dispatch:
env:
REGISTRY: ${{ vars.REGISTRY }}
GITEA_ROOT_URL: ${{ vars.GITEA_ROOT_URL }}
IMAGE_NAME: ${{ vars.IMAGE_NAME }}
DOCKERFILE: ${{ vars.DOCKERFILE }}
DOCKER_PLATFORMS: ${{ vars.DOCKER_PLATFORMS }}
RUN_NPM_TEST: ${{ vars.RUN_NPM_TEST }}
DOCKER_IMAGE_PREFIX: ${{ vars.DOCKER_IMAGE_PREFIX }}
jobs:
docker:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Docker
run: |
set -eux
if ! command -v docker >/dev/null 2>&1; then
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io
fi
if [ ! -S /var/run/docker.sock ]; then
echo "ERROR: /var/run/docker.sock 未挂载到 job 容器。" >&2
echo "请在 act_runner config.yaml 中配置:" >&2
echo " container.options: -v /var/run/docker.sock:/var/run/docker.sock" >&2
echo " container.valid_volumes: [/var/run/docker.sock]" >&2
exit 1
fi
docker info
- name: Run npm tests
if: vars.RUN_NPM_TEST != 'false'
shell: bash
run: |
set -euo pipefail
NODE_VERSION=20.20.2
ARCH=$(uname -m | sed 's/x86_64/x64/; s/aarch64/arm64/')
TAR="node-v${NODE_VERSION}-linux-${ARCH}.tar.gz"
if ! command -v node >/dev/null 2>&1 || ! node -v | grep -qE "v20\."; then
downloaded=0
for url in \
"https://npmmirror.com/mirrors/node/v${NODE_VERSION}/${TAR}" \
"https://mirrors.aliyun.com/nodejs-release/v${NODE_VERSION}/${TAR}" \
"https://nodejs.org/dist/v${NODE_VERSION}/${TAR}"; do
echo "trying ${url}"
if curl -fsSL --connect-timeout 20 --retry 3 --retry-delay 5 --max-time 600 \
"$url" -o /tmp/node.tar.gz; then
downloaded=1
break
fi
done
[ "$downloaded" -eq 1 ] || { echo "failed to download Node ${NODE_VERSION}" >&2; exit 1; }
rm -rf /usr/local/node
mkdir -p /usr/local/node
tar -xzf /tmp/node.tar.gz -C /usr/local/node --strip-components=1
export PATH="/usr/local/node/bin:${PATH}"
fi
node -v
npm -v
# act-runner 工作区 volume 可能残留旧 lockfile,强制与当前 commit 一致
git checkout HEAD -- package-lock.json package.json .npmrc
echo "commit: $(git rev-parse --short HEAD)"
echo "lockfile sample:"
grep -m3 '"resolved"' package-lock.json || true
if grep -qE 'artifactory|xiaohongshu' package-lock.json; then
echo "ERROR: package-lock.json 仍含内网源,请检查 checkout" >&2
exit 1
fi
# 清除 runner 注入的内网 npm 配置,使用独立缓存目录
unset NPM_CONFIG_REGISTRY NPM_REGISTRY npm_config_registry NODE_AUTH_TOKEN 2>/dev/null || true
export CI=true
export NPM_CONFIG_USERCONFIG=/tmp/toolbox-npmrc
export NPM_CONFIG_GLOBALCONFIG=/tmp/toolbox-npmrc-global
export npm_config_cache=/tmp/toolbox-npm-cache
export npm_config_progress=true
export npm_config_loglevel=info
export npm_config_registry=https://registry.npmmirror.com
export npm_config_esbuild_binary_host_mirror=https://npmmirror.com/mirrors/esbuild
printf '%s\n' 'registry=https://registry.npmmirror.com' > /tmp/toolbox-npmrc-global
printf '%s\n' \
'registry=https://registry.npmmirror.com' \
'esbuild_binary_host_mirror=https://npmmirror.com/mirrors/esbuild' \
'always-auth=false' \
> /tmp/toolbox-npmrc
rm -rf node_modules /tmp/toolbox-npm-cache
mkdir -p /tmp/toolbox-npm-cache
npm cache clean --force
echo "npm registry: $(npm config get registry)"
echo "=== npm ci ==="
npm ci --userconfig=/tmp/toolbox-npmrc --registry=https://registry.npmmirror.com
echo "=== npm test ==="
npm run test:run -- --pool=forks --maxWorkers=2 --reporter=verbose
- name: Resolve registry
id: reg
if: gitea.event_name == 'push'
shell: bash
env:
GITEA_SERVER_URL: ${{ gitea.server_url }}
run: |
set -euo pipefail
url_to_host() {
echo "$1" | sed -e 's|^https://||' -e 's|^http://||' -e 's|/.*||'
}
is_internal_host() {
local host="${1%%:*}"
case "$host" in
localhost|127.*|10.*|192.168.*) return 0 ;;
172.*)
local second
second=$(echo "$host" | cut -d. -f2)
[ "$second" -ge 16 ] && [ "$second" -le 31 ]
return
;;
*) return 1 ;;
esac
}
registry="${REGISTRY:-}"
if [ -z "$registry" ] && [ -n "${GITEA_ROOT_URL:-}" ]; then
registry=$(url_to_host "${GITEA_ROOT_URL}")
fi
if [ -z "$registry" ]; then
registry=$(url_to_host "${GITEA_SERVER_URL}")
fi
if is_internal_host "$registry"; then
echo "ERROR: Registry 主机 '${registry}' 是内网地址。" >&2
echo "请设置 Variable REGISTRY=公网 Gitea 域名(如 git.example.com)。" >&2
exit 1
fi
echo "registry host: ${registry}"
auth_header=""
if auth_header=$(curl -fsS -D - "https://${registry}/v2/" -o /dev/null 2>&1 | grep -i '^www-authenticate:'); then
if echo "$auth_header" | grep -qiE '127\.0\.0\.1|172\.(1[6-9]|2[0-9]|3[01])\.|localhost|:13827'; then
echo "ERROR: Registry token realm 指向内网地址:${auth_header}" >&2
exit 1
fi
fi
echo "host=${registry}" >> "${GITHUB_OUTPUT}"
- name: Log in to Container Registry
if: gitea.event_name == 'push'
shell: bash
run: |
set -euo pipefail
echo "${{ secrets.REGISTRY_TOKEN }}" | \
docker login "${{ steps.reg.outputs.host }}" -u "${{ gitea.actor }}" --password-stdin
- name: Build image
shell: bash
env:
GITEA_SHA: ${{ gitea.sha }}
GITEA_REF: ${{ gitea.ref }}
GITEA_REPOSITORY: ${{ gitea.repository }}
GITEA_REPOSITORY_OWNER: ${{ gitea.repository_owner }}
GITEA_EVENT_NAME: ${{ gitea.event_name }}
REGISTRY_HOST: ${{ steps.reg.outputs.host }}
run: |
set -euo pipefail
dockerfile="${DOCKERFILE:-Dockerfile}"
if [ "${DOCKER_IMAGE_PREFIX:-}" = "hub" ] || [ "${DOCKER_IMAGE_PREFIX:-}" = "docker.io" ]; then
image_prefix=""
elif [ -z "${DOCKER_IMAGE_PREFIX:-}" ] || [ "${DOCKER_IMAGE_PREFIX}" = "1panel" ]; then
image_prefix="docker.1panel.live/library/"
elif [ "${DOCKER_IMAGE_PREFIX}" = "daocloud" ]; then
image_prefix="docker.m.daocloud.io/library/"
else
image_prefix="${DOCKER_IMAGE_PREFIX}"
[[ "${image_prefix}" == */ ]] || image_prefix="${image_prefix}/"
fi
echo "using IMAGE_PREFIX=${image_prefix:-<docker.io>}"
build_args=(--build-arg "IMAGE_PREFIX=${image_prefix}")
builder_id="gitea-buildx-${GITEA_REPOSITORY//\//-}"
if [ "${GITEA_EVENT_NAME}" = "push" ]; then
install_binfmt() {
for img in \
"docker.1panel.live/tonistiigi/binfmt:latest" \
"docker.m.daocloud.io/tonistiigi/binfmt:latest" \
"tonistiigi/binfmt:latest"; do
echo "trying binfmt image: ${img}"
if docker run --privileged --rm "${img}" --install all; then
return 0
fi
done
return 1
}
install_binfmt || true
registry="${REGISTRY_HOST}"
image_name="${IMAGE_NAME:-$(echo "${GITEA_REPOSITORY##*/}" | tr '[:upper:]' '[:lower:]')}"
platforms="${DOCKER_PLATFORMS:-linux/amd64,linux/arm64}"
image="${registry}/${GITEA_REPOSITORY_OWNER}/${image_name}"
tags="${image}:sha-${GITEA_SHA:0:7}"
case "${GITEA_REF}" in
refs/heads/main|refs/heads/master)
tags="${tags},${image}:latest"
;;
refs/tags/v*)
tags="${tags},${image}:${GITEA_REF#refs/tags/}"
;;
esac
docker buildx create --name "${builder_id}" --use 2>/dev/null || docker buildx use "${builder_id}"
docker buildx inspect --bootstrap
tag_args=()
IFS=',' read -r -a tag_list <<< "$tags"
for t in "${tag_list[@]}"; do
tag_args+=(-t "$t")
done
docker buildx build \
--platform "${platforms}" \
-f "${dockerfile}" \
"${build_args[@]}" \
"${tag_args[@]}" \
--push \
.
else
# PR:仅单架构构建验证 Dockerfile,不推送
docker buildx create --name "${builder_id}" --use 2>/dev/null || docker buildx use "${builder_id}"
docker buildx inspect --bootstrap
docker buildx build \
--platform linux/amd64 \
-f "${dockerfile}" \
"${build_args[@]}" \
--load \
.
fi