Publish Mini Program Dev Version / publish (push) Failing after 11s
1826/1827连续失败:hk runner下载musl版node时拿到错误产物 (glibc版node在musl环境报libstdc++.so.6缺失)。 加固:musl下载增加npmmirror镜像fallback;下载后校验gzip魔数; 解压后验证node可执行,失败则清除缓存避免污染后续run
185 lines
8.4 KiB
YAML
185 lines
8.4 KiB
YAML
name: Publish Mini Program Dev Version
|
||
|
||
on:
|
||
workflow_dispatch:
|
||
push:
|
||
branches:
|
||
- main
|
||
paths:
|
||
- "mini/**"
|
||
- ".gitea/workflows/miniapp-preview.yml"
|
||
|
||
jobs:
|
||
publish:
|
||
# 标准标签:hk / bt-runner 均带 ubuntu-latest,任一空闲 runner 接单
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
# 精简 runner(Alpine)无 node,无法跑 action-checkout;直接用 git 浅克隆
|
||
- name: Checkout
|
||
env:
|
||
REPO_URL: ${{ gitea.server_url }}/${{ gitea.repository }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: |
|
||
git init -q .
|
||
git remote add origin "$REPO_URL"
|
||
git fetch -q --depth 20 origin "$REF_NAME"
|
||
git checkout -q FETCH_HEAD
|
||
git log --oneline -3
|
||
|
||
- name: Prepare Node toolchain
|
||
run: |
|
||
# 优先复用 runner 宿主机缓存的工具链(工作目录持久化)
|
||
TOOLDIR="$RUNNER_WORKSPACE/.lunar-toolchain"
|
||
mkdir -p "$TOOLDIR"
|
||
if [ -x "$TOOLDIR/node/bin/node" ] && "$TOOLDIR/node/bin/node" --version >/dev/null 2>&1; then
|
||
echo "复用缓存 Node 工具链"
|
||
else
|
||
# 清理旧缓存:Alpine busybox mv 遇已存在目录会合并而非替换,
|
||
# 残留的 glibc 版 node 会污染 musl 环境导致 symbol not found
|
||
rm -rf "$TOOLDIR"
|
||
mkdir -p "$TOOLDIR"
|
||
# 探测 libc:Alpine(musl) 用 unofficial musl 构建,其余用官方 glibc 构建(tar.gz,busybox tar 不支持 xz)
|
||
if ldd --version 2>&1 | grep -qi musl || [ ! -e /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ]; then
|
||
NODE_DIST="node-v22.12.0-linux-x64-musl"
|
||
# 多源下载:unofficial-builds 海外源 + npmmirror 镜像(busybox wget 自动跟随 302)
|
||
wget -q --tries=2 --timeout=120 -O "$TOOLDIR/node.tgz" \
|
||
"https://unofficial-builds.nodejs.org/download/release/v22.12.0/$NODE_DIST.tar.gz" \
|
||
|| wget -q --tries=2 --timeout=120 -O "$TOOLDIR/node.tgz" \
|
||
"https://registry.npmmirror.com/-/binary/node-unofficial-builds/v22.12.0/$NODE_DIST.tar.gz"
|
||
else
|
||
NODE_DIST="node-v22.12.0-linux-x64"
|
||
wget -q --tries=2 --timeout=120 -O "$TOOLDIR/node.tgz" \
|
||
"https://nodejs.org/dist/v22.12.0/$NODE_DIST.tar.gz" \
|
||
|| wget -q --tries=2 --timeout=120 -O "$TOOLDIR/node.tgz" \
|
||
"https://mirrors.aliyun.com/nodejs-release/v22.12.0/$NODE_DIST.tar.gz"
|
||
fi
|
||
# 校验下载结果:文件存在且是 gzip 格式(防止半截下载/错误页残留污染缓存)
|
||
test -s "$TOOLDIR/node.tgz"
|
||
head -c 2 "$TOOLDIR/node.tgz" | od -An -tx1 | grep -q "1f 8b"
|
||
tar -C "$TOOLDIR" -xzf "$TOOLDIR/node.tgz"
|
||
mv "$TOOLDIR/$NODE_DIST" "$TOOLDIR/node"
|
||
rm -f "$TOOLDIR/node.tgz"
|
||
fi
|
||
# 验证二进制可执行,失败则清缓存并报错(避免坏缓存被后续 run 复用)
|
||
if ! "$TOOLDIR/node/bin/node" --version; then
|
||
echo "::error::Node 二进制无法执行,请检查下载源与 libc 匹配"
|
||
rm -rf "$TOOLDIR"
|
||
exit 1
|
||
fi
|
||
echo "PATH=$TOOLDIR/node/bin:$PATH" >> "$GITEA_ENV"
|
||
|
||
- name: Verify Node runtime
|
||
run: node --version
|
||
|
||
- name: Install dependencies
|
||
working-directory: mini
|
||
run: npm ci
|
||
|
||
- name: Run tests
|
||
working-directory: mini
|
||
run: npm test
|
||
|
||
- name: Configure production API and app version
|
||
working-directory: mini
|
||
env:
|
||
MINIAPP_BUILD_NUMBER: ${{ gitea.run_number }}
|
||
GITEA_SHA: ${{ gitea.sha }}
|
||
run: |
|
||
test -n "$MINIAPP_BUILD_NUMBER"
|
||
VERSION="$(node -e 'const {resolveVersion}=require("./ci/resolve-version.cjs"); process.stdout.write(resolveVersion(process.cwd()))')"
|
||
test -n "$VERSION"
|
||
mkdir -p ci-artifacts
|
||
printf '%s\n' "$VERSION" > ci-artifacts/resolved-version.txt
|
||
echo "Resolved Mini Program version for build+upload: $VERSION"
|
||
|
||
# 注入版本号到 utils/version.js
|
||
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||
COMMIT_SHORT="${GITEA_SHA:0:7}"
|
||
cat > utils/version.js <<EOF
|
||
/**
|
||
* 版本号配置文件
|
||
* 此文件由 CI 构建时自动生成,请勿手动修改
|
||
*/
|
||
|
||
module.exports = {
|
||
version: "$VERSION",
|
||
buildNumber: "$MINIAPP_BUILD_NUMBER",
|
||
buildTime: "$BUILD_TIME",
|
||
commitSha: "$COMMIT_SHORT",
|
||
};
|
||
EOF
|
||
echo "Version info injected into utils/version.js:"
|
||
cat utils/version.js
|
||
|
||
- name: Materialize the upload key
|
||
env:
|
||
WECHAT_CI_PRIVATE_KEY: ${{ secrets.WECHAT_CI_PRIVATE_KEY }}
|
||
run: |
|
||
test -n "$WECHAT_CI_PRIVATE_KEY"
|
||
umask 077
|
||
printf '%s' "$WECHAT_CI_PRIVATE_KEY" > "$RUNNER_TEMP/wechat-ci.key"
|
||
|
||
- name: Upload WeChat development version
|
||
working-directory: mini
|
||
env:
|
||
MINIAPP_APPID: ${{ vars.MINIAPP_APPID }}
|
||
MINIAPP_ROBOT: ${{ vars.MINIAPP_ROBOT }}
|
||
# Prefer the version resolved before build so UI label matches WeChat upload.
|
||
MINIAPP_BUILD_NUMBER: ${{ gitea.run_number }}
|
||
WECHAT_CI_PRIVATE_KEY_PATH: ${{ runner.temp }}/wechat-ci.key
|
||
MINIAPP_UPLOAD_RESULT: ${{ gitea.workspace }}/mini/ci-artifacts/upload-result.json
|
||
MINIAPP_VERSION_LABEL: ${{ gitea.sha }}
|
||
MINIAPP_UPLOAD_DESC_ITEMS: "3"
|
||
MINIAPP_UPLOAD_DESC_MAX: "100"
|
||
run: |
|
||
test -n "$MINIAPP_APPID"
|
||
test -n "$MINIAPP_BUILD_NUMBER"
|
||
test -s ci-artifacts/resolved-version.txt
|
||
RESOLVED_VERSION="$(tr -d '\n' < ci-artifacts/resolved-version.txt)"
|
||
# Force upload to the same version that was baked into the JS bundle.
|
||
export MINIAPP_VERSION_OVERRIDE="$RESOLVED_VERSION"
|
||
mkdir -p ci-artifacts
|
||
node -e 'const c=require("./project.config.json"); if(c.appid!==process.env.MINIAPP_APPID){console.error("MINIAPP_APPID mismatch with project.config.json", process.env.MINIAPP_APPID, c.appid); process.exit(1)}'
|
||
node -e 'const {resolveVersion}=require("./ci/resolve-version.cjs"); console.log("Resolved Mini Program version:", resolveVersion(process.cwd()))'
|
||
node -e 'const {resolveUploadDesc}=require("./ci/resolve-upload-desc.cjs"); console.log("Resolved upload remark:", resolveUploadDesc({repoRoot:require("path").resolve(".."), versionLabel:process.env.MINIAPP_VERSION_LABEL}))'
|
||
# Normalize PEM newlines that may be flattened when stored in Secrets.
|
||
# 用 node 处理(部分 runner 无 python3)
|
||
node - <<'JS'
|
||
const fs = require("fs");
|
||
const p = process.env.WECHAT_CI_PRIVATE_KEY_PATH;
|
||
let t = fs.readFileSync(p, "utf8");
|
||
if (t.includes("\\n") && t.includes("BEGIN")) t = t.split("\\n").join("\n");
|
||
t = t.split("\r\n").join("\n").trim() + "\n";
|
||
fs.writeFileSync(p, t);
|
||
if (!/PRIVATE KEY/.test(t)) { console.error("WECHAT_CI_PRIVATE_KEY is not a PEM private key"); process.exit(1); }
|
||
console.log("private_key_lines=", t.split("\n").length);
|
||
JS
|
||
node ci/upload-ci.cjs
|
||
test -s ci-artifacts/upload-result.json
|
||
cat ci-artifacts/upload-result.json
|
||
|
||
- name: Send success notification
|
||
if: success()
|
||
env:
|
||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||
run: |
|
||
VERSION="$(tr -d '\n' < mini/ci-artifacts/resolved-version.txt)"
|
||
bash .gitea/scripts/notify.sh success "小程序开发版上传成功" "版本: ${VERSION}" || echo "通知发送失败,不影响主流程"
|
||
|
||
- name: Send failure notification
|
||
if: failure()
|
||
env:
|
||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||
run: |
|
||
bash .gitea/scripts/notify.sh failure \
|
||
"小程序开发版上传失败" \
|
||
"请检查 Actions 日志了解失败原因" || echo "通知发送失败"
|
||
|
||
- name: Remove temporary credentials
|
||
if: always()
|
||
run: rm -f "$RUNNER_TEMP/wechat-ci.key"
|