From 16817febe2b0d1a6a1b01694feb315750866c640 Mon Sep 17 00:00:00 2001 From: fantengteng <593886467@qq.com> Date: Fri, 24 Apr 2026 12:15:32 +0800 Subject: [PATCH] chore(public): sync main for v2.0.4 --- .cursorrules | 74 ------- .gitignore | 5 + documentation/internal/GIT_WORKFLOW.md | 53 ----- documentation/internal/RELEASE_CHECKLIST.md | 67 ------- packages/core/.agent/skills/app/SKILL.md | 204 -------------------- packages/core/.cursor/rules/flu-project.mdc | 64 ------ packages/core/AI_RULES.md | 62 ------ packages/core/CLAUDE.md | 57 ------ public-extra/.gitkeep | 1 - scripts/sync-public.ts | 99 ++++++++-- 10 files changed, 90 insertions(+), 596 deletions(-) delete mode 100644 .cursorrules delete mode 100644 documentation/internal/GIT_WORKFLOW.md delete mode 100644 documentation/internal/RELEASE_CHECKLIST.md delete mode 100644 packages/core/.agent/skills/app/SKILL.md delete mode 100644 packages/core/.cursor/rules/flu-project.mdc delete mode 100644 packages/core/AI_RULES.md delete mode 100644 packages/core/CLAUDE.md delete mode 100644 public-extra/.gitkeep diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index 9381e18..0000000 --- a/.cursorrules +++ /dev/null @@ -1,74 +0,0 @@ -# Git Commit Message Rules - -当为该项目生成 Git 提交消息时,必须严格遵循以下 Conventional Commits 规范: - -## 格式 (Format) -`(): ` - -## 类型 (Types) -- **feat**: 新功能 (New feature) -- **fix**: 修补 Bug (Bug fix) -- **docs**: 文档变更 (Documentation changes) -- **style**: 代码格式变更 (Formatting, missing semi-colons, etc; no code change) -- **refactor**: 重构 (Code change that neither fixes a bug nor adds a feature) -- **perf**: 性能优化 (Performance improvements) -- **test**: 测试 (Adding missing tests) -- **chore**: 构建过程或辅助工具的变动 (Changes to the build process or auxiliary tools and libraries) - -## 规范细节 (Details) -- **Scope**: 可选,推荐使用包名,如 `(core)`, `(app-ship)`, `(vs)`。 -- **Description**: 使用简洁的中文或英文描述,首字母无需大写,结尾不加句号。 -- **禁止内容**: 严禁输出任何 Markdown 格式或解释性文字。 - -## 示例 (Examples) -- `feat(core): 支持多平台上传分发` -- `fix(app-ship): 修复华为平台上传超时问题` -- `perf(vs): 优化 Webview 加载性能` -- `docs: 更新自动化发布指南` - ---- - -# AI Workspace Rules (SOLO) - -本段规则用于让 AI 在本仓库内稳定使用 `.agent/` 的技能与上下文。 - -## 0. 规则优先级 - -1. 当用户明确要求“生成 Git 提交消息 / commit message / 写提交信息”时: - - 只输出一行符合 Conventional Commits 的提交信息 - - 严禁输出 Markdown、解释性文字或额外内容 - - 忽略本段 AI Workspace Rules -2. 其余任何需求(代码、排错、规划、解释、重构等):遵循本段规则。 - -## 1. 默认入口 Skill(策略 A) - -在开始处理本仓库任何非提交信息类任务前,你必须先读取并遵循: -- `.agent/skills/flu-cli-expert/SKILL.md` - -并将其作为“路由器”决定后续要不要读取索引文件。 - -## 2. 索引文件按需加载(由 Skill 协议触发) - -当进入具体任务(编码/排错/实现功能/改动文件/给出下一步行动等)时,必须读取: -- `.agent/project-context.md` -- `.agent/MEMORY.md` - -当用户问题属于规划/路线图/阶段性演进(例如:Roadmap、里程碑、阶段拆解、下一版本)时,额外读取: -- `.agent/ROADMAP.md` - -## 3. 冷启动应答(方向类问题) - -当用户使用 `/flu-cli-expert` 或询问“下一步做啥/目前做到哪/现在什么状态/接下来怎么推进”且未附带具体任务时: -- 必须先读取 `.agent/MEMORY.md` 与 `.agent/project-context.md` -- 禁止仅凭历史印象或本 Skill 的旧段落猜测进度 -- 输出结构必须为: - 1) 当前阶段(引用 MEMORY 的阶段/里程碑表述) - 2) 建议下一步(最多 3 条,按优先级;默认先验收测试,再做路线图未交付项) - 3) 若无法读取上述文件:说明原因并请用户 `@` 相关文件后再问 - -## 4. 任务收口与归档 - -当某个功能模块“已完成且实测通过”或用户明确要求收口时,按 Skill 的归档协议维护: -- `.agent/MEMORY.md` -- `.agent/project-context.md` -- `.agent/ROADMAP.md`(如影响优先级/范围/路线) diff --git a/.gitignore b/.gitignore index 9828a95..781860d 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,9 @@ publishapi_demo/* /\.agent/skills/* !/.agent/skills/flu-cli-expert/ !/.agent/skills/flu-cli-expert/** +# 防止在工具仓 packages/core 目录误跑 sync-rules 产生测试文件 +/packages/core/AI_RULES.md +/packages/core/CLAUDE.md +/packages/core/.cursor/ +/packages/core/.agent/ /packages/app-ship/ diff --git a/documentation/internal/GIT_WORKFLOW.md b/documentation/internal/GIT_WORKFLOW.md deleted file mode 100644 index cc78c71..0000000 --- a/documentation/internal/GIT_WORKFLOW.md +++ /dev/null @@ -1,53 +0,0 @@ -# Flu-CLI 仓库分支规约(私有 + 公有) - -## 1. 结论(必须遵守) - -- 主干分支统一为 `main` -- `master` 退役:不再作为持续更新分支 -- 发版只允许从 `main` 触发 -- 功能开发一律使用 `feat/*` 或 `fix/*` 分支,通过 PR/MR 合并回 `main` - ---- - -## 2. 私有仓库(origin) - -- 默认分支:`main` -- 禁止直接在 `main` 上开发与提交(只允许合并) -- 分支命名: - - `feat/`:新功能 - - `fix/`:修复 - - `docs/`:文档 - ---- - -## 3. 公有仓库(public) - -- 默认分支:`main` -- 使用白名单导出同步:`npm run sync:public` -- 同步流程:推送 `sync/*` 分支并通过 PR 合并到 `public/main` -- 公开仓库不承载私有内容,不依赖 `.gitignore` 做“遮挡” - ---- - -## 4. 分支清理策略(是否要“发布时自动删分支”?) - -结论:**不建议把“删除开发分支”绑定到发布脚本里做自动化**,原因: - -- 发布动作通常在 `main` 上执行,此时已无法准确关联“要删哪个开发分支” -- 删除分支属于协作治理,误删的成本高(尤其是 hotfix/并行开发分支) -- 最稳的做法是:在 PR/MR 合并时由平台自动/半自动清理 - -推荐策略: - -1. PR/MR 合并回 `main` 后,勾选“删除源分支”(或由维护者手动删除远程分支) -2. 本地分支在确认远程删除且无需回溯后再删 -3. 例外:长期分支(如确实需要 `dev`)必须明确 owner 与用途,否则不允许存在 - ---- - -## 5. 同步脚本约束 - -- `sync:public` 不直接写入 `public/main`,只推送 `sync/*` 分支 -- `public/main` 通过 PR 合并更新 -- 标签建议在 PR 合并后再创建(避免标签指向未合并提交) -- `sync:public` 使用白名单导出,不会同步私有协作文件(例如 `.agent/**`) diff --git a/documentation/internal/RELEASE_CHECKLIST.md b/documentation/internal/RELEASE_CHECKLIST.md deleted file mode 100644 index 3a4a05b..0000000 --- a/documentation/internal/RELEASE_CHECKLIST.md +++ /dev/null @@ -1,67 +0,0 @@ -# 发版清单(npm + VSCode + 官方文档 + 公众号) - -## 0. 适用范围 - -- npm:`flu-cli-core`、`flu-cli` -- VSCode:`flu-cli-vscode`(Marketplace + 可选 Open VSX) -- 文档:VitePress(`docs/flu-cil-docs`) -- 公众号:文章源文件在 `docs/huoye-ink/series/skill-tips/04-Flu-CLI-AI-Skill-让AI懂你项目/output/wechat.md` - ---- - -## 1. 预检(发版前必须) - -- 跑验收清单:[TEST_PLAN_VSCODE.md](../../docs/testing/TEST_PLAN_VSCODE.md) -- 本地构建: - - `npm run vs`(根目录) -- 确认工作区干净(无未提交变更) -- 分支规约确认(两仓一致): - - 私有仓库默认分支为 `main` - - 公有仓库默认分支为 `main` - - `master` 退役:不再更新(`sync:public` 改为推 `sync/*` 分支并 PR 合并 `public/main`) - ---- - -## 2. 更新官方文档(VitePress) - -- 新增/更新页面(按本次版本调整): - - `docs/flu-cil-docs/guide/ai-skill.md` - - `docs/flu-cil-docs/cli/commands/init-ai-base.md` - - `docs/flu-cil-docs/cli/commands/sync-rules.md` -- 本地预览(在 `docs/flu-cil-docs`): - - `npm install` - - `npm run dev` -- 部署(在 `docs/flu-cil-docs`): - - `./auto-deploy.sh` - ---- - -## 3. 发布 npm + VSCode(自动化脚本) - -### 3.1 一键入口 - -- 根目录执行:`npm run release` -- 选择要发布的项目(建议顺序): - - `flu-cli-core` - - `flu-cli` - - `flu-cli-vscode` - -### 3.2 发布脚本位置 - -- Monorepo 总管:`scripts/release.ts` -- VSCode 发布:`packages/vscode-extension/scripts/release.ts` - -### 3.3 环境变量(VSCode) - -- `VSCE_PAT`:发布 Marketplace -- `OVSX_PAT`:发布 Open VSX(可选) - ---- - -## 4. 公众号文章 - -- 文章位置: - - `docs/huoye-ink/series/skill-tips/04-Flu-CLI-AI-Skill-让AI懂你项目/output/wechat.md` -- 发布建议: - - 发布当天:附上“本次版本新增能力”与“3 步上手命令” - - 末尾放上官方文档链接与安装方式 diff --git a/packages/core/.agent/skills/app/SKILL.md b/packages/core/.agent/skills/app/SKILL.md deleted file mode 100644 index 399e7c4..0000000 --- a/packages/core/.agent/skills/app/SKILL.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -name: app-flutter-project -description: >- - app 项目架构与代码生成规范(由 flu sync-rules 根据 .flu-cli.json 生成)。在本仓库生成 Page/ViewModel/Service/Widget 等 Dart 代码时必须遵循;示例与 flu-cli TemplateGenerator 同源。 ---- - -# app — Flu-CLI 项目规范(Agent Skill) - -未设置 template 元数据 -配置来源(解析): `builtin` - -## 启动协议 - -处理本仓库具体任务前,**必须先**遵守下方「生成器层规范」与「架构约定」;生成新文件时与 `flu gen` / VSCode 插件保持同一结构。 - -## Flutter 可运行工程(从空目录生成骨架时必读) - -若任务包含「创建最小可运行 Flutter 工程」或生成 `pubspec.yaml` / `lib/main.dart`: - -1. **`pubspec.yaml` 的 `name`** 必须与 `.flu-cli.json` 的 `packageName` **完全一致**(小写、下划线、无拼写错误),且符合 Dart 包名规则(仅 `[a-z0-9_]`,不得以数字开头)。 -2. **入口文件必须是 `lib/main.dart`**(`void main()` 与 `runApp` 放于此文件)。**禁止**把 `main.dart` 放在 `lib/widgets/` 等子目录充当入口,否则默认 `flutter run` 找不到入口。 -3. 若用户目录名含大写(例如 `aiDemo`),在目录内执行 `flutter create .` 会失败;应使用合法包名,例如:`flutter create --project-name <与 packageName 一致> .` 或先重命名目录为小写。 -4. 生成完成后,用户应运行:`flutter pub get`,再 `flutter run`。 - -## 单文件原生模式(覆盖基类约定) - -若**某一文件**需使用 Flutter 原生组件、**不要**继承项目 BasePage / BaseViewModel 等,请在该 **Dart 文件首行**添加注释: - -```dart -// flu:native -``` - -AI 在生成或修改该文件时**不得**添加项目约定的基类与 Mixin;其它文件仍受 `.flu-cli.json` 约束。 - ---- - -### Page (页面) 层规范 - -- **文件路径**: `lib/pages` -- **约束模式**: 🆓 自由模式 - -- Page (页面) 未配置基类约束,可自由选择继承方式 - -### ViewModel 层规范 - -- **文件路径**: `lib/viewmodels` -- **约束模式**: 🆓 自由模式 - -- ViewModel 未配置基类约束,可自由选择继承方式 - -### Widget 层规范 - -- **文件路径**: `lib/widgets` -- **约束模式**: 🆓 自由模式 - -- Widget 未配置基类约束,可自由选择继承方式 - -### Model 层规范 - -- **文件路径**: `lib/models` -- **约束模式**: 🆓 自由模式 - -- Model 未配置基类约束,可自由选择继承方式 - -### Component 层规范 - -- **文件路径**: `lib/components` -- **约束模式**: 🆓 自由模式 - -- Component 未配置基类约束,可自由选择继承方式 - -### Service 层规范 - -- **文件路径**: `lib/services` -- **约束模式**: 🆓 自由模式 - -- Service 未配置基类约束,可自由选择继承方式 - ---- - -## 参考代码(与 flu-cli TemplateGenerator / 生成器同源) - -以下为 **Sample** 占位名称的示例;生成时请替换为真实业务名,并保持相同结构与 import 风格。 - -### Page(简单 Stateful,无 Base) - -```dart -import 'package:flutter/material.dart'; - -class SamplePage extends StatefulWidget { - const SamplePage({super.key}); - - @override - State createState() => _SamplePageState(); -} - -class _SamplePageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('示例'), - ), - body: const Center( - child: Text('SamplePage'), - ), - ); - } -} - -``` - -### ViewModel(简单) - -```dart -import 'package:flutter/foundation.dart'; - -class SampleViewModel extends ChangeNotifier { - bool _isLoading = false; - bool get isLoading => _isLoading; - - void setLoading(bool loading) { - _isLoading = loading; - notifyListeners(); - } - - // TODO: Add your logic here -} - -``` - -### Widget - -```dart -import 'package:flutter/material.dart'; - -class SampleWidget extends StatelessWidget { - const SampleWidget({super.key}); - - @override - Widget build(BuildContext context) { - return const SizedBox.shrink(); - } -} - -``` - -### Model - -```dart -class SampleModel { - final String id; - final String name; - - SampleModel({ - required this.id, - required this.name, - }); - - factory SampleModel.fromJson(Map json) { - return SampleModel( - id: json['id'] as String, - name: json['name'] as String, - ); - } - - Map toJson() { - return { - 'id': id, - 'name': name, - }; - } -} - -``` - -### Component - -```dart -import 'package:flutter/material.dart'; - -class SampleComponent extends StatelessWidget { - const SampleComponent({super.key}); - - @override - Widget build(BuildContext context) { - return const SizedBox.shrink(); - } -} - -``` - -### Service - -```dart -class SampleService extends BaseService { - SampleService(); -} - -``` - ---- - -_本文件由 `flu sync-rules` 自动生成,请勿手工编辑;修改 `.flu-cli.json` 后请重新运行。_ diff --git a/packages/core/.cursor/rules/flu-project.mdc b/packages/core/.cursor/rules/flu-project.mdc deleted file mode 100644 index 4b84ece..0000000 --- a/packages/core/.cursor/rules/flu-project.mdc +++ /dev/null @@ -1,64 +0,0 @@ ---- -description: Flu-CLI 项目架构规范(自动生成,请勿手动修改) -globs: ["lib/**/*.dart"] -alwaysApply: true ---- - -# Flu-CLI 项目规范 - -> 模板类型: `(未检测到,已使用推断值)` | 由 `flu sync-rules` 自动生成,修改 `.flu-cli.json` 后重新运行 - -### Page (页面) 层规范 - -- **文件路径**: `lib/pages` -- **约束模式**: 🆓 自由模式 - -- Page (页面) 未配置基类约束,可自由选择继承方式 - - -### ViewModel 层规范 - -- **文件路径**: `lib/viewmodels` -- **约束模式**: 🆓 自由模式 - -- ViewModel 未配置基类约束,可自由选择继承方式 - - -### Widget 层规范 - -- **文件路径**: `lib/widgets` -- **约束模式**: 🆓 自由模式 - -- Widget 未配置基类约束,可自由选择继承方式 - - -### Model 层规范 - -- **文件路径**: `lib/models` -- **约束模式**: 🆓 自由模式 - -- Model 未配置基类约束,可自由选择继承方式 - - -### Component 层规范 - -- **文件路径**: `lib/components` -- **约束模式**: 🆓 自由模式 - -- Component 未配置基类约束,可自由选择继承方式 - - -### Service 层规范 - -- **文件路径**: `lib/services` -- **约束模式**: 🆓 自由模式 - -- Service 未配置基类约束,可自由选择继承方式 - - -## Flutter 代码风格约定 - -- Widget 嵌套层级 ≤ 2,复杂结构拆分为 `_buildXxx()` 私有方法 -- 优先使用 `const` 构造 -- 所有注释使用中文 -- 文件命名:snake_case,类命名:PascalCase diff --git a/packages/core/AI_RULES.md b/packages/core/AI_RULES.md deleted file mode 100644 index 4357e8f..0000000 --- a/packages/core/AI_RULES.md +++ /dev/null @@ -1,62 +0,0 @@ -# AI_RULES.md — Flu-CLI 项目架构规范(人机共读版) - -> 本文件由 `flu sync-rules` 根据 `.flu-cli.json` 自动生成,请勿手动修改。 -> 配置来源:`builtin` | 模板类型:`(未检测到,已使用推断值)` - ---- - -## 使用说明 - -本文件定义了本项目的代码生成规范,适用于: - -1. **AI 助手**(Cursor、Claude):AI 应严格遵守以下规则生成代码 -2. **团队成员**:了解项目的架构约定,避免手动创建不符合规范的文件 -3. **Code Review**:作为审查生成代码是否合规的参考依据 - ---- - -### Page (页面) 层规范 - -- **文件路径**: `lib/pages` -- **约束模式**: 🆓 自由模式 - -- Page (页面) 未配置基类约束,可自由选择继承方式 - -### ViewModel 层规范 - -- **文件路径**: `lib/viewmodels` -- **约束模式**: 🆓 自由模式 - -- ViewModel 未配置基类约束,可自由选择继承方式 - -### Widget 层规范 - -- **文件路径**: `lib/widgets` -- **约束模式**: 🆓 自由模式 - -- Widget 未配置基类约束,可自由选择继承方式 - -### Model 层规范 - -- **文件路径**: `lib/models` -- **约束模式**: 🆓 自由模式 - -- Model 未配置基类约束,可自由选择继承方式 - -### Component 层规范 - -- **文件路径**: `lib/components` -- **约束模式**: 🆓 自由模式 - -- Component 未配置基类约束,可自由选择继承方式 - -### Service 层规范 - -- **文件路径**: `lib/services` -- **约束模式**: 🆓 自由模式 - -- Service 未配置基类约束,可自由选择继承方式 - ---- - -_文件由 [Flu-CLI](https://gitee.com/flu-cli/flu-cli) 自动生成 · 让 Flutter 研发像呼吸一样顺畅_ diff --git a/packages/core/CLAUDE.md b/packages/core/CLAUDE.md deleted file mode 100644 index 78f7ef0..0000000 --- a/packages/core/CLAUDE.md +++ /dev/null @@ -1,57 +0,0 @@ -# CLAUDE.md — Flu-CLI 项目架构规范 - -> 模板类型: `(未检测到,已使用推断值)` | 由 `flu sync-rules` 自动生成 -> 修改 `.flu-cli.json` 后重新运行 `flu sync-rules` 以更新此文件 - ---- - -### Page (页面) 层规范 - -- **文件路径**: `lib/pages` -- **约束模式**: 🆓 自由模式 - -- Page (页面) 未配置基类约束,可自由选择继承方式 - -### ViewModel 层规范 - -- **文件路径**: `lib/viewmodels` -- **约束模式**: 🆓 自由模式 - -- ViewModel 未配置基类约束,可自由选择继承方式 - -### Widget 层规范 - -- **文件路径**: `lib/widgets` -- **约束模式**: 🆓 自由模式 - -- Widget 未配置基类约束,可自由选择继承方式 - -### Model 层规范 - -- **文件路径**: `lib/models` -- **约束模式**: 🆓 自由模式 - -- Model 未配置基类约束,可自由选择继承方式 - -### Component 层规范 - -- **文件路径**: `lib/components` -- **约束模式**: 🆓 自由模式 - -- Component 未配置基类约束,可自由选择继承方式 - -### Service 层规范 - -- **文件路径**: `lib/services` -- **约束模式**: 🆓 自由模式 - -- Service 未配置基类约束,可自由选择继承方式 - ---- - -## 代码风格约定 - -- Widget 嵌套层级 ≤ 2,复杂结构拆分为 `_buildXxx()` 私有方法 -- 优先使用 `const` 构造 -- 注释使用中文 -- 文件命名:snake_case,类命名:PascalCase diff --git a/public-extra/.gitkeep b/public-extra/.gitkeep deleted file mode 100644 index 8b13789..0000000 --- a/public-extra/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/scripts/sync-public.ts b/scripts/sync-public.ts index 3ab549a..c157612 100644 --- a/scripts/sync-public.ts +++ b/scripts/sync-public.ts @@ -22,11 +22,24 @@ const PUBLIC_EXPORT_PATHS = [ '.editorconfig', '.eslintrc.js', '.prettierrc', - '.cursorrules', '.nvmrc', '.env.example', '.gitignore', ] +const PUBLIC_EXCLUDE_PATHS = [ + 'documentation/internal', + 'packages/core/.agent', + 'packages/core/.cursor', + 'packages/core/AI_RULES.md', + 'packages/core/CLAUDE.md', +] +const PRIVATE_ARTIFACT_DIR_NAMES = new Set(['.agent', '.cursor', '.trae', '.vscode']) +const PRIVATE_ARTIFACT_FILE_NAMES = new Set([ + 'AI_RULES.md', + 'CLAUDE.md', + 'AGENTS.md', + '.cursorrules', +]) const LOCAL_SYNC_LOG_PATH = path.join(process.cwd(), '.agent', '.local', 'PUBLIC_SYNC_LOG.md') const CHANGELOG_CANDIDATES = [ 'CHANGELOG.md', @@ -119,6 +132,59 @@ function sanitizeBranchName(name: string): string { .replace(/^-+|-+$/g, '') } +function removeExportPath(rootDir: string, relativePath: string): boolean { + const target = path.join(rootDir, relativePath) + if (!fs.existsSync(target)) return false + fs.rmSync(target, { recursive: true, force: true }) + return true +} + +function applyPublicExcludes(exportRoot: string): string[] { + const removed: string[] = [] + for (const relativePath of PUBLIC_EXCLUDE_PATHS) { + if (removeExportPath(exportRoot, relativePath)) removed.push(relativePath) + } + return removed +} + +function collectPrivateArtifactPaths( + rootDir: string, + relativeDir = '', + collected: string[] = [], +): string[] { + const dirPath = path.join(rootDir, relativeDir) + if (!fs.existsSync(dirPath)) return collected + const entries = fs.readdirSync(dirPath, { withFileTypes: true }) + + for (const entry of entries) { + const relativePath = relativeDir ? path.join(relativeDir, entry.name) : entry.name + + if (entry.isDirectory()) { + if (PRIVATE_ARTIFACT_DIR_NAMES.has(entry.name)) { + collected.push(relativePath) + continue + } + collectPrivateArtifactPaths(rootDir, relativePath, collected) + continue + } + + if (PRIVATE_ARTIFACT_FILE_NAMES.has(entry.name)) { + collected.push(relativePath) + } + } + + return collected +} + +function applyPrivateArtifactSweep(exportRoot: string): string[] { + const candidates = collectPrivateArtifactPaths(exportRoot) + const removed: string[] = [] + for (const relativePath of candidates) { + if (removeExportPath(exportRoot, relativePath)) removed.push(relativePath) + } + return removed +} + function defaultSyncBranchName(version: string): string { const now = new Date() const stamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String( @@ -200,18 +266,6 @@ async function main() { const diff = publicMainRef ? GitUtils.run(`git diff --stat ${publicMainRef}..${sourceBranch} -- ${whitelistArgs}`) : '首次同步' - - if (publicMainRef && !diff) { - UIUtils.info('✨ 与 public/main 的白名单内容一致,无需同步。') - appendSyncLog({ - status: 'skipped', - sourceBranch, - sourceCommit, - version, - reason: '与 public/main 白名单内容一致', - }) - return - } if (diff !== '首次同步') console.log(diff) // 4. 白名单导出并推送 @@ -231,8 +285,22 @@ async function main() { const publicExtraDir = path.join(extractDir, 'public-extra') if (fs.existsSync(publicExtraDir)) { + const rootGitkeepPath = path.join(extractDir, '.gitkeep') + const hadRootGitkeepBeforeMerge = fs.existsSync(rootGitkeepPath) + const hadPublicExtraGitkeep = fs.existsSync(path.join(publicExtraDir, '.gitkeep')) GitUtils.runInherit(`cp -R "${publicExtraDir}/." "${extractDir}/"`) GitUtils.runInherit(`rm -rf "${publicExtraDir}"`) + // 仅用于占位的 public-extra/.gitkeep 不应污染公开仓根目录 + if (hadPublicExtraGitkeep && !hadRootGitkeepBeforeMerge && fs.existsSync(rootGitkeepPath)) { + fs.rmSync(rootGitkeepPath, { force: true }) + } + } + const removedByPathExcludes = applyPublicExcludes(extractDir) + const removedByArtifactSweep = applyPrivateArtifactSweep(extractDir) + const removedPaths = Array.from(new Set([...removedByPathExcludes, ...removedByArtifactSweep])) + if (removedPaths.length > 0) { + UIUtils.info('\n🧹 已从公开快照剔除以下私有/测试产物:') + removedPaths.forEach((relativePath) => UIUtils.info(` - ${relativePath}`)) } UIUtils.info('\n🌿 正在基于公开仓 main 创建同步分支...') @@ -262,7 +330,10 @@ async function main() { sourceBranch, sourceCommit, version, - reason: '公开仓 main 与导出白名单内容一致', + reason: + removedPaths.length > 0 + ? `公开仓 main 与导出白名单内容一致(已应用过滤:${removedPaths.join(', ')})` + : '公开仓 main 与导出白名单内容一致', }) fs.rmSync(exportDir, { recursive: true, force: true }) return -- Gitee