diff --git a/README.md b/README.md index cc664efe1..850cdfb2e 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ pnpm check:brand pnpm check:ci pnpm check:env pnpm check:secrets +pnpm check:metadata pnpm check:artifacts pnpm check:quality pnpm clean:artifacts diff --git a/docs/operations.md b/docs/operations.md index 43daa35a2..1b88fd00e 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -11,6 +11,7 @@ pnpm check:brand pnpm check:ci pnpm check:env pnpm check:secrets +pnpm check:metadata pnpm exec tsc --noEmit --pretty false git diff --check pnpm check:artifacts @@ -29,7 +30,7 @@ pnpm check:quality ## CI -`.github/workflows/lint.yml` 是基础质量门禁,运行 `pnpm check:quality`,负责 lint、日志治理、公开交付面品牌治理、CI hygiene、环境变量示例卫生、secret hygiene、类型检查、diff hygiene 和 tracked 污染物检查。 +`.github/workflows/lint.yml` 是基础质量门禁,运行 `pnpm check:quality`,负责 lint、日志治理、公开交付面品牌治理、CI hygiene、环境变量示例卫生、secret hygiene、package metadata、类型检查、diff hygiene 和 tracked 污染物检查。 CI 和本地提交前检查复用同一个入口,避免规则漂移。 @@ -85,6 +86,14 @@ pnpm check:secrets `pnpm check:secrets` 会扫描 Git 已追踪文本文件,拦截真实形态的常见 API key、GitHub token、AWS key、Slack token、Authorization/Cookie 长值和私钥块。明显的 `xxxx`、placeholder、example 或 redaction 测试值会被允许,避免误伤文档示例。 +## Package Metadata + +```bash +pnpm check:metadata +``` + +`pnpm check:metadata` 会检查 `package.json` 的项目名、描述、许可证、Node/pnpm 版本和 package manager 声明是否符合当前交付基线。`repository/homepage/bugs` 会等 canonical remote 仓库完成重命名并验证后再添加,避免对外链接撒谎。 + ## 首页性能烟测 ```bash diff --git a/docs/release-readiness.md b/docs/release-readiness.md index 4d2b44026..db9aa8393 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -13,7 +13,7 @@ SMOKE_BASE_URL=http://127.0.0.1:3001 pnpm smoke:routes 预期: -- `pnpm check:quality` 通过 lint、日志脱敏门禁、公开交付面品牌治理、CI hygiene、环境变量示例卫生、secret hygiene、类型检查、diff hygiene 和 tracked 污染物检查。 +- `pnpm check:quality` 通过 lint、日志脱敏门禁、公开交付面品牌治理、CI hygiene、环境变量示例卫生、secret hygiene、package metadata、类型检查、diff hygiene 和 tracked 污染物检查。 - `pnpm smoke:routes` 验证 `/` 返回 200。 - `/classic` 和 `/chat/test-id` 重定向到 `/`。 - `/settings` 未登录重定向到 `/login?callbackUrl=%2Fsettings`。 diff --git a/package.json b/package.json index 150517308..c1f57b4a1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,13 @@ { "name": "yedir-ai", "version": "3.1.0", + "description": "Production-oriented AI design workspace with Lovart-style home, Canvas/Agent workflows, image editing, 3D/CAD, and admin governance.", "private": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20", + "pnpm": "9.12.3" + }, "scripts": { "dev": "next dev --turbo", "postinstall": "node scripts/security/auto-enable-perf-guard.mjs", @@ -16,7 +22,8 @@ "check:ci": "node scripts/check-ci-hygiene.mjs", "check:env": "node scripts/check-env-example.mjs", "check:secrets": "node scripts/check-secrets.mjs", - "check:quality": "pnpm lint && pnpm check:logs && pnpm check:brand && pnpm check:ci && pnpm check:env && pnpm check:secrets && pnpm exec tsc --noEmit --pretty false && git diff --check && pnpm check:artifacts", + "check:metadata": "node scripts/check-package-metadata.mjs", + "check:quality": "pnpm lint && pnpm check:logs && pnpm check:brand && pnpm check:ci && pnpm check:env && pnpm check:secrets && pnpm check:metadata && pnpm exec tsc --noEmit --pretty false && git diff --check && pnpm check:artifacts", "clean:artifacts": "node scripts/clean-local-artifacts.mjs", "smoke:routes": "node scripts/smoke-routes.mjs", "format": "pnpm exec ultracite fix", diff --git a/scripts/check-package-metadata.mjs b/scripts/check-package-metadata.mjs new file mode 100644 index 000000000..4d0eff66c --- /dev/null +++ b/scripts/check-package-metadata.mjs @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +import { readFileSync } from "node:fs"; + +const packageJson = JSON.parse(readFileSync("package.json", "utf8")); +const licenseText = readFileSync("LICENSE", "utf8"); +const violations = []; + +const expectEqual = (path, actual, expected) => { + if (actual !== expected) { + violations.push(`package.json ${path}: expected "${expected}", received "${actual ?? ""}"`); + } +}; + +const expectNonEmpty = (path, actual) => { + if (typeof actual !== "string" || actual.trim().length === 0) { + violations.push(`package.json ${path}: expected a non-empty string`); + } +}; + +expectEqual("name", packageJson.name, "yedir-ai"); +expectNonEmpty("description", packageJson.description); +expectEqual("private", packageJson.private, true); +expectEqual("license", packageJson.license, "Apache-2.0"); +expectEqual("packageManager", packageJson.packageManager, "pnpm@9.12.3"); +expectEqual("engines.node", packageJson.engines?.node, ">=20"); +expectEqual("engines.pnpm", packageJson.engines?.pnpm, "9.12.3"); + +if (!licenseText.includes("Apache License, Version 2.0")) { + violations.push("LICENSE: expected Apache License, Version 2.0 text"); +} + +if (packageJson.repository || packageJson.homepage || packageJson.bugs) { + violations.push( + "package.json: repository/homepage/bugs metadata should be added only after the canonical remote repository is renamed and verified" + ); +} + +if (violations.length > 0) { + console.error("Package metadata check failed."); + console.error(violations.join("\n")); + process.exit(1); +} + +console.log("Package metadata check passed.");