157 lines
6.6 KiB
YAML
157 lines
6.6 KiB
YAML
name: build
|
||
|
||
# 提交 -> 本机 Runner 编译 -> 微信通知 + 网页预览
|
||
# 实测结论(勿随意改动):
|
||
# 1. 不用任何第三方 action(作业容器不联网拉 action,全用镜像自带工具)
|
||
# 2. 必须自己 clone —— Runner 不会自动检出代码,工作区初始为空
|
||
# 3. act 以 `bash -e -o pipefail` 执行每步 -> 禁止 `cmd | head` 这种会 SIGPIPE 提前退出的管道
|
||
# 4. 工具链来自主机只读挂载 /opt/citools(go/uv),缓存写 /opt/citools-cache
|
||
# 5. bridge 从容器内走 docker 网关 172.17.0.1:8899(实测可达)
|
||
# 6. 校验/上报逻辑放在 .gitea/scripts/ 下的脚本里,YAML 只负责调用(避免 YAML 里嵌深缩进代码)
|
||
on:
|
||
push:
|
||
branches:
|
||
- "**"
|
||
workflow_dispatch:
|
||
|
||
env:
|
||
BRIDGE_URL: ${{ vars.CI_BRIDGE_URL }}
|
||
BRIDGE_TOKEN: ${{ secrets.CI_BRIDGE_TOKEN }}
|
||
GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
concurrency:
|
||
group: build-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
build:
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 40
|
||
steps:
|
||
- name: 检出代码
|
||
run: |
|
||
set -uo pipefail
|
||
SRV="${GITHUB_SERVER_URL:-http://192.168.50.223:3000}"
|
||
HP="${SRV#*://}"
|
||
BR="${GITHUB_REF_NAME:-${GITHUB_REF#refs/heads/}}"
|
||
cd "${GITHUB_WORKSPACE:-$(pwd)}"
|
||
rm -rf ./* .git 2>/dev/null || true
|
||
git init -q .
|
||
git remote add origin "http://x-access-token:${GIT_TOKEN}@${HP}/${GITHUB_REPOSITORY}.git"
|
||
git fetch -q --depth 50 origin "$BR"
|
||
git checkout -q -B "$BR" FETCH_HEAD
|
||
git rev-parse HEAD > /tmp/ci-sha
|
||
date +%s > /tmp/ci-start
|
||
: > /tmp/ci-preview-url
|
||
echo skipped > /tmp/ci-build-status
|
||
git rev-list --count FETCH_HEAD > /tmp/ci-commits-count 2>/dev/null || echo 1 > /tmp/ci-commits-count
|
||
echo "已检出 ${GITHUB_REPOSITORY} 分支 ${BR}"
|
||
git log -1 --format=' %h %an %s'
|
||
|
||
- name: 提交信息规范校验
|
||
run: python3 .gitea/scripts/ci_gate.py
|
||
|
||
- name: 编译与自检
|
||
run: |
|
||
set +e; set +o pipefail
|
||
LOG=/tmp/ci-build.log; : > "$LOG"
|
||
run() { echo "-- $* --"; "$@" 2>&1 | tee -a "$LOG"; return "${PIPESTATUS[0]}"; }
|
||
runin(){ local d="$1"; shift; echo "-- ($d) $* --"; ( cd "$d" && "$@" ) 2>&1 | tee -a "$LOG"; return "${PIPESTATUS[0]}"; }
|
||
|
||
export PATH=/opt/citools/go/bin:/opt/citools/bin:$PATH
|
||
export GOCACHE=/opt/citools-cache/gocache GOMODCACHE=/opt/citools-cache/gomod UV_CACHE_DIR=/opt/citools-cache/uv
|
||
export GOPROXY=https://goproxy.cn,direct GOTOOLCHAIN=local GOFLAGS=-mod=mod GOPATH=/opt/citools-cache/gopath
|
||
mkdir -p "$GOCACHE" "$GOMODCACHE" "$UV_CACHE_DIR" "$GOPATH" 2>/dev/null
|
||
|
||
echo "工具链: $(go version 2>&1) | $(uv --version 2>&1) | $(node -v 2>&1) | $(python3 -V 2>&1)"
|
||
fail=0; ran=""
|
||
|
||
if [ -f go.mod ]; then
|
||
ran="$ran go"
|
||
run go build ./... || fail=1
|
||
run go vet ./... || fail=1
|
||
run go test ./... || fail=1
|
||
fi
|
||
|
||
if [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f setup.py ]; then
|
||
ran="$ran python"
|
||
if grep -qE '^check:' Makefile 2>/dev/null; then
|
||
run make UV=uv check || fail=1
|
||
else
|
||
run uv sync || fail=1
|
||
run uv run pytest -q || fail=1
|
||
fi
|
||
fi
|
||
|
||
WEBPKG=""
|
||
for p in package.json web/package.json frontend/package.json ui/package.json; do
|
||
if [ -f "$p" ]; then WEBPKG="$p"; break; fi
|
||
done
|
||
if [ -n "$WEBPKG" ]; then
|
||
ran="$ran node"
|
||
D="$(dirname "$WEBPKG")"
|
||
if [ -f "$D/package-lock.json" ]; then
|
||
runin "$D" npm ci || fail=1
|
||
else
|
||
runin "$D" npm install || fail=1
|
||
fi
|
||
if grep -q '"build"[[:space:]]*:' "$WEBPKG" 2>/dev/null; then
|
||
runin "$D" npm run build || fail=1
|
||
fi
|
||
fi
|
||
|
||
if [ -z "$ran" ] && [ -f Makefile ]; then
|
||
ran="$ran make"
|
||
run make || fail=1
|
||
fi
|
||
if [ -z "$ran" ]; then
|
||
echo "未识别到构建入口(go.mod / pyproject.toml / package.json / Makefile 都没有),跳过编译"
|
||
fi
|
||
|
||
if [ "$fail" -eq 0 ]; then echo success > /tmp/ci-build-status; else echo failure > /tmp/ci-build-status; fi
|
||
tail -c 4000 "$LOG" > /tmp/ci-build-tail
|
||
echo "构建结果: $(cat /tmp/ci-build-status)(识别:$ran)"
|
||
[ "$fail" -eq 0 ] || exit 1
|
||
|
||
- name: 发布网页预览
|
||
if: always()
|
||
run: |
|
||
set +e; set +o pipefail
|
||
: > /tmp/ci-preview-url
|
||
if [ "$(cat /tmp/ci-build-status 2>/dev/null)" != "success" ]; then
|
||
echo "构建未成功,跳过预览发布"; exit 0
|
||
fi
|
||
WEB=""
|
||
for d in web/dist dist build out public web/build frontend/dist; do
|
||
if [ -f "$d/index.html" ]; then WEB="$d"; break; fi
|
||
done
|
||
if [ -z "$WEB" ]; then echo "未发现静态网页产物目录,跳过预览发布"; exit 0; fi
|
||
tar -czf /tmp/ci-web.tgz -C "$WEB" . || { echo "打包失败"; exit 0; }
|
||
B="${BRIDGE_URL:-}"
|
||
for cand in "$B" "http://172.17.0.1:8899" "http://192.168.50.134:8899" "http://100.92.49.78:8899"; do
|
||
if [ -n "$cand" ]; then
|
||
curl -s -m 5 -o /dev/null "$cand/healthz" && { B="$cand"; break; }
|
||
fi
|
||
done
|
||
SHA="$(cat /tmp/ci-sha)"
|
||
echo "发布预览到 $B(产物目录 $WEB)"
|
||
curl -sS -m 90 -X POST "$B/api/ci/publish?repo=${GITHUB_REPOSITORY}&sha=${SHA}" -H "X-CI-Token: ${BRIDGE_TOKEN}" -H 'Content-Type: application/gzip' --data-binary @/tmp/ci-web.tgz -o /tmp/ci-pub.json
|
||
cat /tmp/ci-pub.json; echo
|
||
URL="$(python3 -c "import json;print(json.load(open('/tmp/ci-pub.json')).get('url',''))" 2>/dev/null)"
|
||
if [ -n "$URL" ]; then echo "$URL" > /tmp/ci-preview-url; echo "网页预览: $URL"; fi
|
||
|
||
- name: 通知微信
|
||
if: always()
|
||
run: |
|
||
set +e; set +o pipefail
|
||
B="${BRIDGE_URL:-}"
|
||
for cand in "$B" "http://172.17.0.1:8899" "http://192.168.50.134:8899" "http://100.92.49.78:8899"; do
|
||
if [ -n "$cand" ]; then
|
||
curl -s -m 5 -o /dev/null "$cand/healthz" && { B="$cand"; break; }
|
||
fi
|
||
done
|
||
echo "使用 ci-bridge: $B"
|
||
S=$(date +%s); T=$(cat /tmp/ci-start 2>/dev/null); [ -n "$T" ] || T=$S
|
||
echo $((S - T)) > /tmp/ci-duration
|
||
CI_BRIDGE_RESOLVED="$B" python3 .gitea/scripts/ci_report.py
|