Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,16 @@ jobs:
with:
node-version: '22.16.0'
cache: 'npm'
cache-dependency-path: |
package-lock.json
client/package-lock.json
server/package-lock.json

- name: Install dependencies
run: |
npm install
cd client && npm install
cd ../server && npm install
npm ci
npm ci --prefix client
npm ci --prefix server
- name: Build Linux ${{ matrix.arch }} package
run: ${{ matrix.package_command }}
env:
Expand All @@ -111,15 +115,15 @@ jobs:
with:
node-version: '22.16.0'
cache: 'npm'
cache-dependency-path: |
package-lock.json
client/package-lock.json
server/package-lock.json
- name: Install dependencies
run: |
npm install
cd client
npm install
cd ..
cd server
npm install
cd ..
npm ci
npm ci --prefix client
npm ci --prefix server
- name: Build package
run: npm run package:windows
env:
Expand Down
106 changes: 106 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: CI

on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
- ready_for_review
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
CI: true
NODE_VERSION: '22.16.0'

jobs:
server:
name: Server test and build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: server/package-lock.json

- name: Install server dependencies
run: npm ci
working-directory: server

- name: Run server tests
run: npm test
working-directory: server

- name: Build server
run: npm run build
working-directory: server

client:
name: Client test and build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: client/package-lock.json

- name: Install client dependencies
run: npm ci
working-directory: client

- name: Run client tests
run: npm test
working-directory: client

- name: Build client
run: npm run build
working-directory: client

project-scripts:
name: Project scripts
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: package-lock.json

- name: Install root dependencies
run: npm ci

- name: Check packaging scripts syntax
run: |
node --check scripts/package.js
node --check scripts/resolve-build-version.js

- name: Resolve build version
run: node scripts/resolve-build-version.js
17 changes: 12 additions & 5 deletions client/src/utils/__tests__/chunkUpload.fault.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ async function waitFor(condition: () => boolean, timeoutMs = 5000): Promise<void
/** 完成所有 XHR 并结束上传流程 */
async function cleanupUpload(uploadPromise: Promise<void>, chunkSize: number): Promise<void> {
try {
;(globalThis.fetch as any).mockImplementation(async (url: string, options?: any) => {
if (options?.body) {
try {
const body = JSON.parse(options.body)
if (body.uploadId !== undefined && body.totalChunks !== undefined && !body.chunkIndex && body.targetPath) {
return { ok: true, json: async () => ({ success: true }) }
}
} catch { /* 非 JSON body */ }
}
return { ok: true, json: async () => ({ uploadedChunks: [] }) }
})

// 多轮完成,处理批次间的延迟和新 XHR 创建
for (let round = 0; round < 5; round++) {
for (const xhr of xhrInstances) {
Expand All @@ -98,11 +110,6 @@ async function cleanupUpload(uploadPromise: Promise<void>, chunkSize: number): P
}
await new Promise(r => setTimeout(r, 300))
}
// Mock merge
;(globalThis.fetch as any).mockResolvedValue({
ok: true,
json: async () => ({ success: true }),
})
await Promise.race([
uploadPromise,
new Promise((_, rej) => setTimeout(() => rej(new Error('清理超时')), 8000)),
Expand Down
7 changes: 7 additions & 0 deletions client/src/utils/__tests__/chunkUpload.preservation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,11 +419,14 @@ describe('保持行为属性测试 — 非进度计算行为不变', () => {
it('属性4: abort 信号正确中止所有 XHR 请求', async () => {
const FILE_SIZE = 200 * MB
const controller = new AbortController()
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})

const file = createMockFile(FILE_SIZE)
const uploader = new ChunkUploader({
file,
targetPath: '/test',
maxRetries: 1,
signal: controller.signal,
})

Expand All @@ -450,6 +453,10 @@ describe('保持行为属性测试 — 非进度计算行为不变', () => {
// 验证 XHR 实例的 abort 被调用
const abortedCount = xhrBeforeAbort.filter(xhr => xhr._aborted).length
expect(abortedCount).toBe(CONCURRENT_UPLOADS)

await uploadPromise
consoleWarnSpy.mockRestore()
consoleErrorSpy.mockRestore()
})

// ============================================================
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/chunkUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export interface ChunkUploadOptions {
file: File
targetPath: string
chunkSize?: number // 默认50MB
chunkSize?: number // 默认20MB
maxRetries?: number // 单个分片最大重试次数
onProgress?: (progress: number) => void
onChunkProgress?: (chunkIndex: number, totalChunks: number, chunkProgress: number) => void
Expand Down
39 changes: 25 additions & 14 deletions docs/GitHub-Actions-多架构构建说明.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,28 @@

## 概述

项目现已支持通过GitHub Actions自动构建多架构Docker镜像,包括AMD64和ARM64平台。
项目通过 GitHub Actions 分离常规 CI 与发布构建:

- `ci.yml`:PR / `main` 分支常规检查,运行服务端测试与构建、前端测试与构建、根级脚本语法检查。
- `build.yml`:发布构建,支持 Linux / Windows 应用包和 Docker 镜像。

## 可用的构建选项

### 1. 主构建工作流 (build.yml)
### 1. 常规 CI 工作流 (ci.yml)

#### 自动触发:
- PR 打开、重新打开、推送新提交或从 Draft 标记为可审阅
- 推送到 `main` 分支

#### 手动触发:
- 支持 `workflow_dispatch`

#### 检查内容:
- 服务端:`npm ci`、`npm test`、`npm run build`
- 前端:`npm ci`、`npm test`、`npm run build`
- 根级脚本:`npm ci`、`node --check scripts/package.js`、`node --check scripts/resolve-build-version.js`

### 2. 发布构建工作流 (build.yml)

#### 手动触发选项:
- ✅ **构建Linux版本** - 构建Linux应用包
Expand All @@ -23,22 +40,15 @@
> 导致同一版本启动两次构建、并发推送同一 Docker 标签互相冲突。
> 详见 [Release重复触发CI修复说明.md](./Release重复触发CI修复说明.md)。

### 2. 专用多架构工作流 (docker-multiarch.yml)

提供更详细的多架构构建控制:
- 可选择构建平台组合
- 支持自定义镜像标签
- 包含架构验证测试

## 使用方法

### 方法一:GitHub网页操作

1. 进入GitHub仓库页面
2. 点击 **Actions** 标签
3. 选择 **Build Package** 工作流
3. 选择 **Build Package** 工作流进行发布构建,或选择 **CI** 手动运行常规检查
4. 点击 **Run workflow**
5. 选择需要的构建选项:
5. 如果运行 **Build Package**,选择需要的构建选项:
- ☑️ 构建ARM64 Docker镜像
- ☑️ 构建Docker镜像(多架构)
6. 点击 **Run workflow** 开始构建
Expand All @@ -64,8 +74,8 @@ gh workflow run build.yml -f build_docker=true
# 触发所有构建
gh workflow run build.yml -f build_linux=true -f build_windows=true -f build_docker=true -f build_docker_arm=true

# 使用专用多架构工作流
gh workflow run docker-multiarch.yml -f tag=latest -f platforms="linux/amd64,linux/arm64" -f push_to_registry=true
# 手动触发常规 CI
gh workflow run ci.yml
```

## 构建产物
Expand Down Expand Up @@ -138,11 +148,12 @@ docker run --platform linux/amd64 --rm xiaozhu674/gameservermanager:latest uname
1. **开发阶段**:使用ARM64专用构建进行快速测试
2. **发布阶段**:使用多架构构建确保兼容性
3. **标签管理**:为不同版本使用语义化版本标签
4. **缓存优化**:GitHub Actions会自动缓存构建层以加速后续构建
4. **缓存优化**:GitHub Actions 会缓存 npm 下载缓存和 Docker 构建层以加速后续构建

## 更新日志

- **v1.0**: 添加ARM64专用构建选项
- **v1.1**: 支持多架构并行构建
- **v1.2**: 添加构建验证和测试步骤
- **v1.3**: 移除 release 事件触发器,修复发布 Release 时重复启动两次构建的问题
- **v1.4**: 新增 PR/main 常规 CI,并将发布构建依赖安装改为 `npm ci`
45 changes: 27 additions & 18 deletions docs/PR自动语法检查说明.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
# PR 自动语法检查说明
# PR 自动检查说明

## 说明

`pr-auto-check.yml` 现在只负责对 PR 进行代码语法检查,不再执行以下操作:
`.github/workflows/ci.yml` 负责 PR 和 `main` 分支的常规持续集成检查。该流程只做只读校验,不评论 PR、不自动合并,也不修改目标分支。

- 自动切换 PR 的目标分支
- 自动合并 PR
- 自动创建或调整 `feature` 分支
发布构建仍由 `.github/workflows/build.yml` 负责,避免把 PR 校验和 Release 打包推送混在一起。

## 检查范围
## 触发时机

当前流程会在 PR 打开、重新打开、推送新提交或标记为可审阅时触发,并执行以下内容:
- PR 打开、重新打开、推送新提交或从 Draft 标记为可审阅时触发。
- 推送到 `main` 分支时触发。
- 维护者也可以通过 `workflow_dispatch` 手动触发。

1. 读取 PR 变更文件列表,并在评论中提示当前状态
2. 检出 PR 对应提交
3. 判断是否仅包含文档变更
4. 对 `server` 和 `client` 分别执行 `npx tsc --noEmit`
5. 如果检查失败,在 PR 中回复中文错误信息
同一 PR 或同一分支的旧运行会被自动取消,减少重复排队。

## 结果说明
## 检查范围

- 如果 PR 仅修改文档或文本文件,会跳过代码语法检查,并在 PR 中给出提示
- 如果 `server` 或 `client` 的 TypeScript 检查失败,工作流会返回对应的错误输出
- 如果检查通过,工作流不会再自动合并代码,也不会修改 PR 的目标分支
当前 CI 包含三个 job:

1. `Server test and build`
- `cd server && npm ci`
- `cd server && npm test`
- `cd server && npm run build`
2. `Client test and build`
- `cd client && npm ci`
- `cd client && npm test`
- `cd client && npm run build`
3. `Project scripts`
- `npm ci`
- `node --check scripts/package.js`
- `node --check scripts/resolve-build-version.js`
- `node scripts/resolve-build-version.js`

## 维护建议

- 如果后续希望增加其他静态检查,可以在这里继续补充流程说明
- 如果需要恢复自动合并,请单独设计新的工作流,不建议和语法检查混在一起
- CI 中优先使用 `npm ci`,确保 lockfile 和实际安装一致。
- 新增必须通过的检查前,先确认当前 `upstream/main` 基线能稳定通过。
- Release 打包、Docker 推送和上传 GitHub Release 资产应继续放在 `build.yml`,并只在标签推送或手动触发时执行。
Loading