Files
BackupX/.github/workflows/release.yml
Wu Qing 9080a47703 chore(repo): 完善仓库维护与自动化 (#108)
统一 Node.js 24 LTS、CI、文档和发布工作流配置。

完善仓库维护规范、Dependabot 与贡献文档,清理生成产物并统一既有 Go 代码格式。
2026-08-09 21:18:19 +08:00

233 lines
6.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自动化发版流水线
#
# 触发方式:
# 1. 推送 taggit tag v1.2.3 && git push --tags
# 2. 手动触发GitHub Actions 页面 → Run workflow → 输入版本号
#
# 产出物:
# - GitHub Releaselinux/amd64 + linux/arm64 预编译 tar.gz
# - Docker Hubawuqing/backupx:latest + awuqing/backupx:v1.2.3(多架构)
#
# 前置配置:
# 在仓库 Settings → Secrets → Actions 添加:
# - DOCKERHUB_USERNAMEDocker Hub 用户名)
# - DOCKERHUB_TOKENDocker Hub Access Token
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: '版本号(如 v1.2.3'
required: true
type: string
permissions:
contents: read
# 统一版本号tag 推送取 ref_name手动触发取 inputs.version
env:
VERSION: ${{ github.event.inputs.version || github.ref_name }}
concurrency:
group: release-${{ github.ref }}-${{ github.event.inputs.version || 'tag' }}
cancel-in-progress: false
jobs:
# ─── Job 0: 校验版本与测试 ───
verify:
name: Validate Release
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- name: Validate release input
shell: bash
env:
RELEASE_VERSION: ${{ env.VERSION }}
RELEASE_EVENT: ${{ github.event_name }}
RELEASE_REF: ${{ github.ref }}
run: |
if [[ ! "$RELEASE_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$ ]]; then
echo "Version must be a SemVer tag such as v1.2.3 or v1.2.3-rc.1"
exit 1
fi
if [[ "$RELEASE_EVENT" == "workflow_dispatch" && "$RELEASE_REF" != "refs/heads/main" ]]; then
echo "Manual releases must run from the main branch"
exit 1
fi
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
cache-dependency-path: server/go.sum
- name: Verify backend
working-directory: server
run: |
go mod verify
unformatted="$(gofmt -l .)"
if [ -n "$unformatted" ]; then
printf '%s\n' "$unformatted"
exit 1
fi
go vet ./...
go test ./...
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Verify frontend
working-directory: web
run: |
npm ci
npm run test
# ─── Job 1: 构建前端 ───
build-web:
name: Build Frontend
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install & Build
working-directory: web
run: |
npm ci
npm run build
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: web-dist
path: web/dist
retention-days: 1
# ─── Job 2: 预编译二进制 → GitHub Release ───
build-release:
name: Build ${{ matrix.goarch }}
needs: build-web
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
cache-dependency-path: server/go.sum
- name: Download frontend artifact
uses: actions/download-artifact@v4
with:
name: web-dist
path: web/dist
- name: Build binary
working-directory: server
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: '0'
run: |
go build \
-trimpath \
-ldflags "-s -w -X main.version=${VERSION}" \
-o ../backupx \
./cmd/backupx
- name: Package release
run: |
ARCHIVE_NAME="backupx-${VERSION}-${{ matrix.goos }}-${{ matrix.goarch }}"
mkdir -p "${ARCHIVE_NAME}"
cp backupx "${ARCHIVE_NAME}/"
cp -r web/dist "${ARCHIVE_NAME}/web"
cp server/config.example.yaml "${ARCHIVE_NAME}/"
cp deploy/install.sh "${ARCHIVE_NAME}/" 2>/dev/null || true
cp deploy/backupx.service "${ARCHIVE_NAME}/" 2>/dev/null || true
# v2.2+: 随发布包提供 Grafana dashboard 与 nginx.conf 模板
if [ -d deploy/grafana ]; then
cp -r deploy/grafana "${ARCHIVE_NAME}/grafana"
fi
cp deploy/nginx.conf "${ARCHIVE_NAME}/nginx.conf" 2>/dev/null || true
tar czf "${ARCHIVE_NAME}.tar.gz" "${ARCHIVE_NAME}"
cp "${ARCHIVE_NAME}.tar.gz" "backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz"
sha256sum "${ARCHIVE_NAME}.tar.gz" > "${ARCHIVE_NAME}.tar.gz.sha256"
sha256sum "backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" > "backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.sha256"
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.VERSION }}
files: |
backupx-${{ env.VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
backupx-${{ env.VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.sha256
backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.sha256
fail_on_unmatched_files: true
generate_release_notes: true
# ─── Job 3: Docker 多架构 → Docker Hub ───
build-docker:
name: Build & Push Docker
needs: build-web
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
build-args: |
VERSION=${{ env.VERSION }}
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/backupx:latest
${{ secrets.DOCKERHUB_USERNAME }}/backupx:${{ env.VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max