perf(client): restore bundle guardrails

This commit is contained in:
2026-07-16 19:35:51 +08:00
parent 40c6de5a06
commit 990add97f8
6 changed files with 69 additions and 17 deletions
+33
View File
@@ -0,0 +1,33 @@
name: CI
on:
pull_request:
push:
branches:
- dev
- main
- test
workflow_dispatch:
jobs:
verify:
name: Quality and Bundle Budgets
runs-on: gitea-label
container:
image: cozsweet-act-runner-node24-docker:latest
env:
NEXT_TELEMETRY_DISABLED: "1"
SENTRY_UPLOAD_SOURCEMAPS: "0"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run quality checks
run: pnpm quality
- name: Enforce bundle budgets
run: pnpm perf:bundle:check
-7
View File
@@ -18,16 +18,10 @@ jobs:
SENTRY_UPLOAD_SOURCEMAPS: "0" SENTRY_UPLOAD_SOURCEMAPS: "0"
steps: steps:
- name: Skip quality gate for test branch
if: ${{ github.ref == 'refs/heads/test' }}
run: echo "Quality gate is disabled for the test branch."
- name: Checkout - name: Checkout
if: ${{ github.ref != 'refs/heads/test' }}
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install dependencies - name: Install dependencies
if: ${{ github.ref != 'refs/heads/test' }}
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Run quality checks - name: Run quality checks
@@ -35,7 +29,6 @@ jobs:
run: pnpm quality run: pnpm quality
- name: Enforce bundle budgets - name: Enforce bundle budgets
if: ${{ github.ref != 'refs/heads/test' }}
run: pnpm perf:bundle:check run: pnpm perf:bundle:check
publish: publish:
+4 -4
View File
@@ -8,14 +8,14 @@
| Workflow | 触发时机 | 职责 | | Workflow | 触发时机 | 职责 |
| --- | --- | --- | | --- | --- | --- |
| `.gitea/workflows/ci.yml` | `dev``main``test` push / PR | 安装依赖、Lint、单元测试、Next.js 构建 | | `.gitea/workflows/ci.yml` | `dev``main``test` push / PR | 安装依赖、执行完整质量检查并校验 bundle 预算 |
| `.gitea/workflows/docker-image.yml` | `main``test` push / 手动触发 | 构建 Docker 镜像、推送到镜像仓库,并通过 SSH 部署 | | `.gitea/workflows/docker-image.yml` | `main``test` push / 手动触发 | 构建 Docker 镜像、推送到镜像仓库,并通过 SSH 部署 |
这样可以避免所有开发分支都发布镜像,并确保生产镜像通过发布门禁。 这样可以避免所有开发分支都发布镜像,并确保生产镜像通过发布门禁。
`main` 分支发布前会执行 Docker workflow 内的质量和 bundle 门禁。`test` `main` 分支发布前会执行 Docker workflow 内的完整质量和 bundle 门禁。`test`
分支为快速部署测试环境,会跳过该门禁;独立的 `ci.yml` 仍会照常执行测试分支检查, 分支为快速部署测试环境,会跳过完整质量检查,但仍必须通过 bundle 预算;独立的
但不会阻塞测试镜像构建与部署 `ci.yml` 会同时执行完整检查并提供测试结果
## 必需 Secrets ## 必需 Secrets
+9 -5
View File
@@ -1,6 +1,6 @@
# Performance baseline # Performance baseline
Baseline date: 2026-07-15 Baseline date: 2026-07-16
Next.js: 16.2.7 (Turbopack) Next.js: 16.2.7 (Turbopack)
@@ -30,10 +30,10 @@ Compressed sizes below are Turbopack module estimates. `Eager` is the route's sy
| Route | Direct-import baseline | Current eager baseline | Change | Current async baseline | | Route | Direct-import baseline | Current eager baseline | Change | Current async baseline |
| --- | ---: | ---: | ---: | ---: | | --- | ---: | ---: | ---: | ---: |
| `/splash` | 646.2 KiB | 599.7 KiB | -46.5 KiB | 49.2 KiB | | `/splash` | 646.2 KiB | 599.9 KiB | -46.3 KiB | 49.2 KiB |
| `/chat` | 669.8 KiB | 637.8 KiB | -32.0 KiB | 49.6 KiB | | `/chat` | 669.8 KiB | 640.7 KiB | -29.1 KiB | 49.6 KiB |
| `/subscription` | 658.4 KiB | 616.9 KiB | -41.5 KiB | 20.6 KiB | | `/subscription` | 658.4 KiB | 617.3 KiB | -41.1 KiB | 20.6 KiB |
| `/tip` | 651.6 KiB | 610.9 KiB | -40.7 KiB | 20.6 KiB | | `/tip` | 651.6 KiB | 611.2 KiB | -40.4 KiB | 20.6 KiB |
The earlier direct-import pass replaced root `@/utils` and `@/data/storage` The earlier direct-import pass replaced root `@/utils` and `@/data/storage`
barrel imports with symbol-level module paths. The current Client-boundary pass barrel imports with symbol-level module paths. The current Client-boundary pass
@@ -42,6 +42,10 @@ provider imports, and loads the Dexie-backed chat repository through a dynamic
import. UA Parser remains an intentional eager dependency because the app still import. UA Parser remains an intentional eager dependency because the app still
requires its general browser, OS, and device detection behavior. requires its general browser, OS, and device detection behavior.
The 2026-07-16 verification also prevents runtime imports from the root
repository and storage barrels. This keeps unrelated repositories from turning
the intentionally async Dexie path eager when a new feature is added.
Module loading baseline: Module loading baseline:
| Module | Before | Baseline | | Module | Before | Baseline |
+22
View File
@@ -5,6 +5,28 @@ import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([ const eslintConfig = defineConfig([
...nextVitals, ...nextVitals,
...nextTs, ...nextTs,
{
files: ["src/**/*.{ts,tsx}"],
rules: {
"no-restricted-imports": [
"error",
{
paths: [
{
name: "@/data/repositories",
message:
"Import the concrete repository module so unrelated browser storage stays out of the client graph.",
},
{
name: "@/data/storage",
message:
"Import the concrete storage module so unrelated persistence code stays out of the client graph.",
},
],
},
],
},
},
{ {
files: ["src/app/**/*.{ts,tsx}"], files: ["src/app/**/*.{ts,tsx}"],
ignores: ["src/app/api/**", "src/app/**/route.ts"], ignores: ["src/app/api/**", "src/app/**/route.ts"],
+1 -1
View File
@@ -2,7 +2,7 @@ import type {
FeedbackSubmitResponse, FeedbackSubmitResponse,
SubmitFeedbackInput, SubmitFeedbackInput,
} from "@/data/dto/feedback"; } from "@/data/dto/feedback";
import { getFeedbackRepository } from "@/data/repositories"; import { getFeedbackRepository } from "@/data/repositories/feedback_repository";
import type { Result } from "@/utils/result"; import type { Result } from "@/utils/result";
export function submitFeedback( export function submitFeedback(