mirror of
https://github.com/Gloridust/WechatOnCloud.git
synced 2026-09-20 11:33:32 +08:00
QEMU 模拟下 arm64 的 apt-get update 反复 OOM(getline: Cannot allocate memory), 让实例镜像 1.4.1/1.4.3 都发不出去;源精简只降概率不治本(1.4.3 又栽在 snapshot InRelease)。 公开仓库可用免费 ubuntu-24.04-arm 原生 runner——彻底不走 QEMU: - 每个 (镜像 × 架构) 在各自原生 runner 上按 digest 推送到 GHCR; - merge job 从两个 arch digest 合成多架构 manifest,一条 imagetools create 打全部 tag (blob 由 GHCR 复制到 Docker Hub,双仓库一并覆盖); - 冒烟门禁保留在 wechat+amd64(原生 x86 可 docker run)。 actionlint 通过。
196 lines
8.3 KiB
YAML
196 lines
8.3 KiB
YAML
name: release
|
||
|
||
# 推送 vX.Y.Z 标签或发布 Release 时,构建并推送多架构镜像(GHCR + 可选 Docker Hub)。
|
||
# workflow_dispatch 可手动触发(只打 latest),用于验证流水线本身。
|
||
#
|
||
# 关键(v1.4.3 之后重写):改用【原生 arm64 runner】分架构构建,不再用 QEMU 模拟。
|
||
# 教训:QEMU 下 arm64 的 apt-get update 会 OOM(getline: Cannot allocate memory),
|
||
# 让实例镜像反复发不出去(1.4.1 / 1.4.3 均折在这)。原生 runner 从根上消除该问题,也更快。
|
||
# 模式:每个 (镜像 × 架构) 在各自原生 runner 上按 digest 推送 → merge job 合成多架构 manifest 打 tag。
|
||
|
||
on:
|
||
push:
|
||
tags:
|
||
- 'v*.*.*'
|
||
release:
|
||
types: [published]
|
||
workflow_dispatch:
|
||
|
||
env:
|
||
REGISTRY: ghcr.io
|
||
|
||
jobs:
|
||
build:
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- { name: wechat, image: wechat-on-cloud, context: ./docker, dockerfile: ./docker/Dockerfile, platform: linux/amd64, arch: amd64, runner: ubuntu-latest }
|
||
- { name: wechat, image: wechat-on-cloud, context: ./docker, dockerfile: ./docker/Dockerfile, platform: linux/arm64, arch: arm64, runner: ubuntu-24.04-arm }
|
||
- { name: panel, image: woc-panel, context: ./panel, dockerfile: ./panel/Dockerfile, platform: linux/amd64, arch: amd64, runner: ubuntu-latest }
|
||
- { name: panel, image: woc-panel, context: ./panel, dockerfile: ./panel/Dockerfile, platform: linux/arm64, arch: arm64, runner: ubuntu-24.04-arm }
|
||
runs-on: ${{ matrix.runner }}
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Log in to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Resolve build version
|
||
id: ver
|
||
run: |
|
||
if [ "${{ github.ref_type }}" = "tag" ]; then
|
||
echo "version=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
||
else
|
||
echo "version=dev-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||
fi
|
||
|
||
# ── 产物门禁:仅在 wechat + amd64(原生 x86,可 docker run)上跑冒烟探针 ──
|
||
# 「验证的东西和发布的东西必须是同一个 artifact」;DPI/CEF 类问题与架构无关,amd64 探针即可覆盖。
|
||
- name: Build amd64 image for smoke test (instance only)
|
||
if: matrix.name == 'wechat' && matrix.arch == 'amd64'
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ matrix.context }}
|
||
file: ${{ matrix.dockerfile }}
|
||
platforms: linux/amd64
|
||
load: true
|
||
tags: woc-probe:ci
|
||
cache-from: type=gha,scope=${{ matrix.name }}-${{ matrix.arch }}
|
||
|
||
- name: Smoke probes (DPI / CEF 两次启动 / 崩溃循环 / 微信模式)
|
||
if: matrix.name == 'wechat' && matrix.arch == 'amd64'
|
||
run: |
|
||
set -eu
|
||
fail() { echo "::error::$1"; docker logs probe 2>&1 | tail -40 || true; exit 1; }
|
||
docker run -d --name probe --security-opt seccomp=unconfined --shm-size=1g \
|
||
-e PUID=1000 -e PGID=1000 -e WOC_APP_TYPE=chromium woc-probe:ci
|
||
for i in $(seq 1 30); do docker exec probe pgrep xsettingsd >/dev/null 2>&1 && break; sleep 3; done
|
||
docker exec probe bash -c 'DISPLAY=:1 dump_xsettings | grep -q "Xft/DPI 98304"' || fail "DPI 探针失败"
|
||
echo "① DPI ✓"
|
||
docker exec probe bash -c '
|
||
for round in 1 2; do
|
||
DISPLAY=:1 timeout 8 chromium --no-sandbox --disable-gpu --user-data-dir=/tmp/probe-profile about:blank >/dev/null 2>&1 &
|
||
p=$!; sleep 7
|
||
kill -0 $p 2>/dev/null || exit 1
|
||
kill -9 $p 2>/dev/null; pkill -9 chromium 2>/dev/null; sleep 1
|
||
done' || fail "CEF 探针崩溃(第 1 或第 2 次启动)"
|
||
echo "② CEF 两次启动 ✓"
|
||
sleep 30
|
||
exits=$(docker logs --since 30s probe 2>&1 | grep -c '已退出' || true)
|
||
[ "$exits" -eq 0 ] || fail "autostart 崩溃循环:30s 内退出 $exits 次"
|
||
echo "③ 零崩溃 ✓"
|
||
docker rm -f probe
|
||
docker run -d --name probe --security-opt seccomp=unconfined --shm-size=1g \
|
||
-e PUID=1000 -e PGID=1000 -e WOC_APP_TYPE=wechat woc-probe:ci
|
||
ok=0
|
||
for i in $(seq 1 30); do docker logs probe 2>&1 | grep -q "尚未安装" && ok=1 && break; sleep 3; done
|
||
[ "$ok" = "1" ] || fail "微信模式未到达等待安装"
|
||
echo "④ 微信模式 ✓"
|
||
docker rm -f probe
|
||
|
||
# ── 按 digest 推送到 GHCR(不打 tag)。同一内容跨仓库 digest 相同,merge 时再复制到 Docker Hub ──
|
||
- name: Build and push by digest
|
||
id: build
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ matrix.context }}
|
||
file: ${{ matrix.dockerfile }}
|
||
platforms: ${{ matrix.platform }}
|
||
build-args: ${{ matrix.name == 'panel' && format('WOC_VERSION={0}', steps.ver.outputs.version) || '' }}
|
||
cache-from: type=gha,scope=${{ matrix.name }}-${{ matrix.arch }}
|
||
cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch }}
|
||
outputs: type=image,name=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true
|
||
|
||
- name: Export digest
|
||
run: |
|
||
mkdir -p /tmp/digests
|
||
digest="${{ steps.build.outputs.digest }}"
|
||
touch "/tmp/digests/${digest#sha256:}"
|
||
|
||
- name: Upload digest
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: digest-${{ matrix.name }}-${{ matrix.arch }}
|
||
path: /tmp/digests/*
|
||
if-no-files-found: error
|
||
retention-days: 1
|
||
|
||
merge:
|
||
needs: build
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- { name: wechat, image: wechat-on-cloud }
|
||
- { name: panel, image: woc-panel }
|
||
steps:
|
||
- name: Download digests
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
path: /tmp/digests
|
||
pattern: digest-${{ matrix.name }}-*
|
||
merge-multiple: true
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Log in to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Log in to Docker Hub
|
||
if: vars.DOCKERHUB_USERNAME != ''
|
||
uses: docker/login-action@v3
|
||
with:
|
||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||
|
||
- name: Docker metadata (tags + labels)
|
||
id: meta
|
||
uses: docker/metadata-action@v5
|
||
with:
|
||
images: |
|
||
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ matrix.image }}
|
||
${{ vars.DOCKERHUB_USERNAME != '' && format('docker.io/{0}/{1}', vars.DOCKERHUB_USERNAME, matrix.image) || '' }}
|
||
tags: |
|
||
type=semver,pattern={{version}}
|
||
type=semver,pattern={{major}}.{{minor}}
|
||
type=semver,pattern={{major}}
|
||
type=raw,value=latest,enable={{is_default_branch}}
|
||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||
|
||
# 从两个 arch 的 digest(都在 GHCR)合成多架构 manifest,一条命令打上所有 tag(含 Docker Hub,
|
||
# imagetools 会把 blob 从 GHCR 复制到 Docker Hub)。
|
||
- name: Create and push multi-arch manifest
|
||
working-directory: /tmp/digests
|
||
run: |
|
||
set -eux
|
||
tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||
srcs=""
|
||
for f in *; do srcs="$srcs ${REGISTRY}/${{ github.repository_owner }}/${{ matrix.image }}@sha256:$f"; done
|
||
docker buildx imagetools create $tags $srcs
|
||
|
||
- name: Verify multi-arch manifest
|
||
run: |
|
||
set -eux
|
||
docker buildx imagetools inspect "${REGISTRY}/${{ github.repository_owner }}/${{ matrix.image }}:${{ steps.meta.outputs.version }}"
|