mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 11:36:36 +08:00
247 lines
8.5 KiB
YAML
247 lines
8.5 KiB
YAML
name: Build & Publish
|
||
|
||
# 版本策略(无回写):
|
||
# - push 到 main:发布 latest 镜像并部署服务器,CI 不再修改 VERSION / 不再向 main 提交 bump,
|
||
# 本地与远程永远不会因 CI 抢提交而产生推送冲突。
|
||
# - push tag v*:正式发版,按 tag 名发布版本镜像、GitHub Release 与多平台二进制。
|
||
# - 手动触发:等同正式发版,版本号取 VERSION 文件当前内容。
|
||
# 需要发正式版时:修改 VERSION(可选)→ git tag v1.2.3 → git push origin v1.2.3
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
tags: ['v*']
|
||
|
||
workflow_dispatch:
|
||
|
||
permissions:
|
||
contents: write # 发布 Release 与上传二进制需要
|
||
packages: write
|
||
|
||
jobs:
|
||
build-image:
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
version: ${{ steps.version.outputs.version }}
|
||
release_tag: ${{ steps.version.outputs.release_tag }}
|
||
is_release: ${{ steps.version.outputs.is_release }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
# 1. 解析版本号:tag 触发取 tag 名(去掉 v 前缀),其余场景读 VERSION 文件
|
||
- name: Resolve version
|
||
id: version
|
||
run: |
|
||
if [[ "${{ github.ref_type }}" = "tag" ]]; then
|
||
VERSION="${GITHUB_REF_NAME#v}"
|
||
else
|
||
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0")
|
||
fi
|
||
# 手动触发视为正式发版;日常 push main 仅滚动 latest
|
||
if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [[ "${{ github.ref_type }}" = "tag" ]]; then
|
||
IS_RELEASE=true
|
||
else
|
||
IS_RELEASE=false
|
||
fi
|
||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||
echo "release_tag=mebox-v${VERSION}" >> "$GITHUB_OUTPUT"
|
||
echo "is_release=${IS_RELEASE}" >> "$GITHUB_OUTPUT"
|
||
|
||
# 2. 设置 Docker QEMU 和 Buildx
|
||
- uses: docker/setup-qemu-action@v3
|
||
- uses: docker/setup-buildx-action@v3
|
||
|
||
# 3. 登录 GHCR
|
||
- name: Log in to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ghcr.io
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
# 4. 提取镜像元数据
|
||
- name: Extract image metadata
|
||
id: meta
|
||
uses: docker/metadata-action@v5
|
||
with:
|
||
images: ghcr.io/${{ github.repository_owner }}/mebox
|
||
tags: |
|
||
type=raw,value=latest
|
||
type=raw,value=${{ steps.version.outputs.version }}
|
||
|
||
# 5. 构建并推送
|
||
- name: Build & push
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: .
|
||
platforms: linux/amd64,linux/arm64
|
||
push: true
|
||
provenance: false
|
||
sbom: false
|
||
tags: ${{ steps.meta.outputs.tags }}
|
||
labels: ${{ steps.meta.outputs.labels }}
|
||
build-args: |
|
||
VERSION=${{ steps.version.outputs.release_tag }}
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
# 单文件可执行构建:把前端打包进二进制(go:embed),交叉编译 Windows /
|
||
# Linux / macOS 的 amd64 / arm64 产物,作为 GitHub Release 附件发布。
|
||
# 仅正式发版(tag v* 或手动触发)时执行。
|
||
build-frontend:
|
||
if: needs.build-image.outputs.is_release == 'true'
|
||
needs: [build-image]
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
cache: 'npm'
|
||
cache-dependency-path: web/package-lock.json
|
||
- name: Install
|
||
working-directory: web
|
||
run: npm ci
|
||
- name: Build SPA
|
||
working-directory: web
|
||
run: npm run build
|
||
- name: Upload web/dist
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: web-dist
|
||
path: web/dist
|
||
retention-days: 1
|
||
|
||
# 先创建(幂等)空的 GitHub Release,供后续 build-binaries 并行上传附件,
|
||
# 也避免矩阵各 job 并发 upload 时 release 尚不存在而互相竞争。
|
||
publish-create-release:
|
||
if: needs.build-image.outputs.is_release == 'true'
|
||
needs: [build-image]
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Create release
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
RELEASE_TAG: ${{ needs.build-image.outputs.release_tag }}
|
||
run: |
|
||
set -eux
|
||
# tag push 时 tag 已存在;手动触发时基于当前 main 创建 tag(幂等)
|
||
if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||
git tag "$RELEASE_TAG"
|
||
git push origin "$RELEASE_TAG"
|
||
fi
|
||
gh release create "$RELEASE_TAG" \
|
||
--title "MeBox ${{ needs.build-image.outputs.version }}" \
|
||
--notes "自动化发布 ${{ needs.build-image.outputs.version }}" \
|
||
--verify-tag --latest || true
|
||
|
||
build-binaries:
|
||
if: needs.build-image.outputs.is_release == 'true'
|
||
needs: [build-image, build-frontend, publish-create-release]
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- goos: linux
|
||
goarch: amd64
|
||
ext: ""
|
||
- goos: linux
|
||
goarch: arm64
|
||
ext: ""
|
||
- goos: windows
|
||
goarch: amd64
|
||
ext: .exe
|
||
- goos: windows
|
||
goarch: arm64
|
||
ext: .exe
|
||
- goos: darwin
|
||
goarch: amd64
|
||
ext: ""
|
||
- goos: darwin
|
||
goarch: arm64
|
||
ext: ""
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: actions/setup-go@v5
|
||
with:
|
||
go-version: '1.25'
|
||
cache: true
|
||
- name: Download web/dist
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: web-dist
|
||
path: web/dist
|
||
- name: Build binary
|
||
run: |
|
||
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} \
|
||
go build -trimpath -ldflags="-s -w -X main.version=${{ needs.build-image.outputs.release_tag }}" \
|
||
-o "dist/mebox-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}" ./cmd/server
|
||
- name: Package
|
||
run: |
|
||
mkdir -p package/mebox
|
||
cp "dist/mebox-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}" package/mebox/mebox${{ matrix.ext }}
|
||
cp README.md package/mebox/ 2>/dev/null || true
|
||
if [ "${{ matrix.goos }}" = "windows" ]; then
|
||
(cd package && zip -r "../mebox_${{ matrix.goos }}_${{ matrix.goarch }}.zip" mebox)
|
||
else
|
||
tar -czf "mebox_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz" -C package mebox
|
||
fi
|
||
- name: Upload to GitHub Release
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
RELEASE_TAG: ${{ needs.build-image.outputs.release_tag }}
|
||
run: |
|
||
set -eux
|
||
PKG="mebox_${{ matrix.goos }}_${{ matrix.goarch }}.zip"
|
||
TAR="mebox_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz"
|
||
# 并发上传到同一 release 各自文件,--clobber 幂等覆盖
|
||
if [ -f "$PKG" ]; then
|
||
for i in 1 2 3; do gh release upload "$RELEASE_TAG" "$PKG" --clobber && break || sleep 5; done
|
||
fi
|
||
if [ -f "$TAR" ]; then
|
||
for i in 1 2 3; do gh release upload "$RELEASE_TAG" "$TAR" --clobber && break || sleep 5; done
|
||
fi
|
||
|
||
deploy:
|
||
name: Deploy to Server
|
||
needs: [build-image]
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Deploy via SSH
|
||
uses: appleboy/ssh-action@v1.0.3
|
||
with:
|
||
host: ${{ secrets.SERVER_HOST }}
|
||
username: ${{ secrets.SERVER_USER }}
|
||
password: ${{ secrets.SERVER_PASSWORD }}
|
||
port: ${{ secrets.SERVER_PORT }}
|
||
script: |
|
||
set -e
|
||
echo "==== 开始部署 MeBox ===="
|
||
cd /root/dockerData/mebox
|
||
|
||
# 判断 compose 命令版本兼容性(docker compose 或 docker-compose)
|
||
if docker compose version >/dev/null 2>&1; then
|
||
COMPOSE_CMD="docker compose"
|
||
elif command -v docker-compose >/dev/null 2>&1; then
|
||
COMPOSE_CMD="docker-compose"
|
||
else
|
||
echo "错误: 未找到 docker compose 或 docker-compose"
|
||
exit 1
|
||
fi
|
||
|
||
echo "正在拉取最新镜像..."
|
||
$COMPOSE_CMD pull
|
||
|
||
echo "正在重启服务..."
|
||
$COMPOSE_CMD up -d
|
||
|
||
echo "清理旧的无用镜像..."
|
||
docker image prune -f
|
||
|
||
echo "==== 部署完成并已启动 ===="
|