ci(v3): publish frontend from v3

This commit is contained in:
jxxghp
2026-08-07 22:08:37 +08:00
parent 6afa2bdc8a
commit 95674fa47a
8 changed files with 31 additions and 20 deletions

View File

@@ -1,10 +1,10 @@
name: Build Moviepilot-Frontend v2
name: Build Moviepilot-Frontend v3
on:
workflow_dispatch:
push:
branches:
- v2
- v3
paths:
- 'package.json'

View File

@@ -3,10 +3,10 @@ name: Frontend Tests
on:
pull_request:
branches:
- v2
- v3
push:
branches:
- v2
- v3
permissions:
contents: read

View File

@@ -105,7 +105,7 @@ Prettier 初始 CI 只检查 Pull Request 新增或修改的受支持文件,
3. 仅把 Prettier 支持且由仓库管理的文件传给 `prettier --check`
4. 文件集合为空时正常通过。
本地直接运行 `yarn format``yarn format:check` 时,脚本按 `refs/remotes/upstream/v2``refs/remotes/origin/v2``refs/heads/v2` 的顺序选择与当前提交具有共同祖先的基线并合并当前工作区与未跟踪文件。完整引用可避免同名标签造成歧义CI 使用显式提交 SHA 保证输入可复现:
本地直接运行 `yarn format``yarn format:check` 时,脚本按 `refs/remotes/upstream/v3``refs/remotes/origin/v3``refs/heads/v3` 的顺序选择与当前提交具有共同祖先的基线并合并当前工作区与未跟踪文件。完整引用可避免同名标签造成歧义CI 使用显式提交 SHA 保证输入可复现:
```sh
yarn format:check --base <base-sha> --head <head-sha>

View File

@@ -92,4 +92,4 @@ yarn lint
yarn build
```
`Frontend Tests` 工作流使用 Node 24 LTS 和 frozen lockfile在面向 `v2` 的 Pull Request 和推送到 `v2` 时运行。`typecheck-and-coverage` job 依次执行类型检查和覆盖率门禁;独立的 `lint` job 执行全仓只读 ESLint 检查。变更文件格式检查依赖 Pull Request 的 base/head SHA因此只在 Pull Request 事件运行。Prettier 和 Node 兼容范围按[前端代码质量工具链演进](code-quality.md)继续渐进接入,新增测试代码不得引入新的 lint 或格式问题。
`Frontend Tests` 工作流使用 Node 24 LTS 和 frozen lockfile在面向 `v3` 的 Pull Request 和推送到 `v3` 时运行。`typecheck-and-coverage` job 依次执行类型检查和覆盖率门禁;独立的 `lint` job 执行全仓只读 ESLint 检查。变更文件格式检查依赖 Pull Request 的 base/head SHA因此只在 Pull Request 事件运行。Prettier 和 Node 兼容范围按[前端代码质量工具链演进](code-quality.md)继续渐进接入,新增测试代码不得引入新的 lint 或格式问题。

View File

@@ -1,6 +1,6 @@
{
"name": "moviepilot",
"version": "2.15.5",
"version": "3.0.0",
"private": true,
"type": "module",
"bin": "dist/service.js",

View File

@@ -5,7 +5,7 @@ import process from 'node:process'
import { fileURLToPath, pathToFileURL } from 'node:url'
import * as prettier from 'prettier'
const DEFAULT_BASE_REFS = ['refs/remotes/upstream/v2', 'refs/remotes/origin/v2', 'refs/heads/v2']
const DEFAULT_BASE_REFS = ['refs/remotes/upstream/v3', 'refs/remotes/origin/v3', 'refs/heads/v3']
const GIT_OUTPUT_MAX_BUFFER = 64 * 1024 * 1024
const PRETTIER_ARGUMENT_BUDGET = 24_000
const prettierCliPath = fileURLToPath(new URL('../node_modules/prettier/bin/prettier.cjs', import.meta.url))
@@ -47,14 +47,14 @@ function hasMergeBase(cwd, baseCommit, headCommit) {
return spawnSync('git', ['merge-base', baseCommit, headCommit], { cwd, stdio: 'ignore' }).status === 0
}
/** 为本地命令选择与当前提交具有共同祖先的 v2 基线。 */
/** 为本地命令选择与当前提交具有共同祖先的 v3 基线。 */
function resolveDefaultBase(cwd, headCommit) {
for (const ref of DEFAULT_BASE_REFS) {
const commit = tryResolveCommit(cwd, ref)
if (commit && hasMergeBase(cwd, commit, headCommit)) return commit
}
throw new Error('无法定位可比较的 v2 基线,请通过 --base <ref> 显式指定比较起点。')
throw new Error('无法定位可比较的 v3 基线,请通过 --base <ref> 显式指定比较起点。')
}
/**

View File

@@ -20,7 +20,7 @@ function write(repository: string, filePath: string, content: string) {
function createRepository() {
const repository = mkdtempSync(resolve(tmpdir(), 'moviepilot-format-changed-'))
temporaryRepositories.push(repository)
git(repository, 'init', '--initial-branch=v2')
git(repository, 'init', '--initial-branch=v3')
git(repository, 'config', 'user.name', 'Format Test')
git(repository, 'config', 'user.email', 'format-test@example.invalid')
write(repository, '.gitignore', 'coverage/\n')
@@ -73,7 +73,7 @@ describe('Prettier 变更文件选择器', () => {
git(repository, 'add', '-A')
git(repository, 'commit', '-m', 'feature')
expect(listSelected(repository, '--base', 'v2', '--head', 'HEAD')).toEqual([
expect(listSelected(repository, '--base', 'v3', '--head', 'HEAD')).toEqual([
'src/line\nbreak.ts',
'src/modified.ts',
'src/new file.vue',
@@ -137,7 +137,7 @@ describe('Prettier 变更文件选择器', () => {
write(repository, 'src/modified.ts', "export const value='feature'\n")
git(repository, 'add', 'src/modified.ts')
git(repository, 'commit', '-m', 'feature')
git(repository, 'tag', 'v2', 'HEAD')
git(repository, 'tag', 'v3', 'HEAD')
expect(listSelected(repository)).toEqual(['src/modified.ts'])
})
@@ -154,8 +154,8 @@ describe('Prettier 变更文件选择器', () => {
cwd: repository,
encoding: 'utf8',
}).trim()
git(repository, 'update-ref', 'refs/remotes/upstream/v2', unrelatedCommit)
git(repository, 'update-ref', 'refs/remotes/origin/v2', baseCommit)
git(repository, 'update-ref', 'refs/remotes/upstream/v3', unrelatedCommit)
git(repository, 'update-ref', 'refs/remotes/origin/v3', baseCommit)
expect(listSelected(repository)).toEqual(['src/modified.ts'])
})
@@ -174,7 +174,7 @@ describe('Prettier 变更文件选择器', () => {
git(repository, 'add', '-A')
git(repository, 'commit', '-m', 'type changes')
expect(listSelected(repository, '--base', 'v2', '--head', 'HEAD')).toEqual(['src/to-regular.ts'])
expect(listSelected(repository, '--base', 'v3', '--head', 'HEAD')).toEqual(['src/to-regular.ts'])
})
it('Git 路径输出超过默认缓冲区时仍能完成选择', () => {

View File

@@ -3,19 +3,20 @@ import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
const workflowPath = resolve(process.cwd(), '.github/workflows/test.yml')
const releaseWorkflowPath = resolve(process.cwd(), '.github/workflows/build.yml')
const testingGuidePath = resolve(process.cwd(), 'docs/testing.md')
const codeQualityGuidePath = resolve(process.cwd(), 'docs/code-quality.md')
describe('前端测试 workflow', () => {
it('在 PR 与 v2 push 上运行,并将变更文件格式检查限制为 PR', () => {
it('在 PR 与 v3 push 上运行,并将变更文件格式检查限制为 PR', () => {
const workflow = readFileSync(workflowPath, 'utf8')
const formatJob = workflow.match(/\n {2}format:\n(?<job>[\s\S]*?)(?=\n {2}[\w-]+:\n|$)/)?.groups?.job
const qualityJob = workflow.match(/\n {2}typecheck-and-coverage:\n(?<job>[\s\S]*?)(?=\n {2}[\w-]+:\n|$)/)?.groups
?.job
expect(workflow).toContain('permissions:\n contents: read')
expect(workflow).toContain('pull_request:\n branches:\n - v2')
expect(workflow).toContain('push:\n branches:\n - v2')
expect(workflow).toContain('pull_request:\n branches:\n - v3')
expect(workflow).toContain('push:\n branches:\n - v3')
expect(workflow).toContain('group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}')
expect(formatJob).toBeDefined()
expect(formatJob).toContain("if: github.event_name == 'pull_request'")
@@ -37,10 +38,20 @@ describe('前端测试 workflow', () => {
const codeQualityGuide = readFileSync(codeQualityGuidePath, 'utf8')
expect(testingGuide).toContain('`typecheck-and-coverage` job')
expect(testingGuide).toContain('推送到 `v2`')
expect(testingGuide).toContain('推送到 `v3`')
expect(testingGuide).toContain('只在 Pull Request 事件运行')
expect(codeQualityGuide).toContain('lint 与 `typecheck-and-coverage` 使用不同 job')
expect(testingGuide).not.toContain('`unit-tests`')
expect(codeQualityGuide).not.toContain('`unit-tests`')
})
it('V3 前端发布只由 v3 分支上的版本变更触发', () => {
const workflow = readFileSync(releaseWorkflowPath, 'utf8')
expect(workflow).toContain('name: Build Moviepilot-Frontend v3')
expect(workflow).toContain('push:\n branches:\n - v3')
expect(workflow).toContain(" - 'package.json'")
expect(workflow).toContain('echo "frontend_version=v$frontend_version"')
expect(workflow).not.toContain(' - v2')
})
})