diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 00000000..8c55d095 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -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 diff --git a/.gitea/workflows/docker-image.yml b/.gitea/workflows/docker-image.yml index 90fa1d8d..dbfaeac3 100644 --- a/.gitea/workflows/docker-image.yml +++ b/.gitea/workflows/docker-image.yml @@ -18,16 +18,10 @@ jobs: SENTRY_UPLOAD_SOURCEMAPS: "0" 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 - if: ${{ github.ref != 'refs/heads/test' }} uses: actions/checkout@v4 - name: Install dependencies - if: ${{ github.ref != 'refs/heads/test' }} run: pnpm install --frozen-lockfile - name: Run quality checks @@ -35,7 +29,6 @@ jobs: run: pnpm quality - name: Enforce bundle budgets - if: ${{ github.ref != 'refs/heads/test' }} run: pnpm perf:bundle:check publish: diff --git a/docs/docker-image-ci.md b/docs/docker-image-ci.md index 5765b8f2..4f36acc5 100644 --- a/docs/docker-image-ci.md +++ b/docs/docker-image-ci.md @@ -8,14 +8,14 @@ | 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 部署 | 这样可以避免所有开发分支都发布镜像,并确保生产镜像通过发布门禁。 -`main` 分支发布前会执行 Docker workflow 内的质量和 bundle 门禁。`test` -分支为快速部署测试环境,会跳过该门禁;独立的 `ci.yml` 仍会照常执行测试分支检查, -但不会阻塞测试镜像构建与部署。 +`main` 分支发布前会执行 Docker workflow 内的完整质量和 bundle 门禁。`test` +分支为快速部署测试环境,会跳过完整质量检查,但仍必须通过 bundle 预算;独立的 +`ci.yml` 会同时执行完整检查并提供测试结果。 ## 必需 Secrets diff --git a/docs/performance-baseline.md b/docs/performance-baseline.md index f7a96df0..f9ba72d3 100644 --- a/docs/performance-baseline.md +++ b/docs/performance-baseline.md @@ -1,6 +1,6 @@ # Performance baseline -Baseline date: 2026-07-15 +Baseline date: 2026-07-16 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 | | --- | ---: | ---: | ---: | ---: | -| `/splash` | 646.2 KiB | 599.7 KiB | -46.5 KiB | 49.2 KiB | -| `/chat` | 669.8 KiB | 637.8 KiB | -32.0 KiB | 49.6 KiB | -| `/subscription` | 658.4 KiB | 616.9 KiB | -41.5 KiB | 20.6 KiB | -| `/tip` | 651.6 KiB | 610.9 KiB | -40.7 KiB | 20.6 KiB | +| `/splash` | 646.2 KiB | 599.9 KiB | -46.3 KiB | 49.2 KiB | +| `/chat` | 669.8 KiB | 640.7 KiB | -29.1 KiB | 49.6 KiB | +| `/subscription` | 658.4 KiB | 617.3 KiB | -41.1 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` 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 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 | Before | Baseline | diff --git a/eslint.config.mjs b/eslint.config.mjs index 6217fd56..25206302 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,6 +5,28 @@ import nextTs from "eslint-config-next/typescript"; const eslintConfig = defineConfig([ ...nextVitals, ...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}"], ignores: ["src/app/api/**", "src/app/**/route.ts"], diff --git a/src/lib/feedback/submit_feedback.ts b/src/lib/feedback/submit_feedback.ts index f1947865..e4a5cddc 100644 --- a/src/lib/feedback/submit_feedback.ts +++ b/src/lib/feedback/submit_feedback.ts @@ -2,7 +2,7 @@ import type { FeedbackSubmitResponse, SubmitFeedbackInput, } from "@/data/dto/feedback"; -import { getFeedbackRepository } from "@/data/repositories"; +import { getFeedbackRepository } from "@/data/repositories/feedback_repository"; import type { Result } from "@/utils/result"; export function submitFeedback(