flu-cli 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -6
- package/CLI.md +1 -0
- package/README.md +139 -67
- package/config/templates.js +6 -127
- package/index.js +100 -50
- package/lib/commands/add.js +8 -8
- package/lib/commands/assets.js +183 -0
- package/lib/commands/config.js +41 -1
- package/lib/commands/newClack.js +89 -116
- package/lib/commands/snippets.js +50 -9
- package/lib/commands/template.js +1 -1
- package/lib/templates/templateCopier.js +14 -275
- package/lib/templates/templateManager.js +20 -163
- package/lib/utils/config.js +13 -51
- package/lib/utils/flutterHelper.js +7 -82
- package/lib/utils/i18n.js +7 -0
- package/lib/utils/templateSelectorEnquirer.js +70 -43
- package/locales/en-US.json +59 -0
- package/locales/zh-CN.json +59 -0
- package/package.json +2 -2
- package/release.sh +380 -32
- package/scripts/sync-base-to-templates.js +6 -6
- package/scripts/workspace-clone-all.sh +4 -4
- package/scripts/workspace-status-all.sh +3 -3
- package/lib/generators/project_generator.js +0 -96
- package/lib/generators/state_manager_generator.js +0 -402
- package/templates/README.md +0 -138
- package/templates/base_files/base_list_page.dart.template +0 -174
- package/templates/base_files/base_list_viewmodel.dart.template +0 -134
- package/templates/base_files/base_page.dart.template +0 -251
- package/templates/base_files/base_viewmodel.dart.template +0 -77
- package/templates/base_files/theme/status_views_theme.dart.template +0 -46
- package/templates/snippets/dart.code-snippets +0 -392
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.description": "🚀 Flutter MVVM Scaffolding Tool",
|
|
3
|
+
"cmd.new.desc": "Create Flutter project with real-time template preview",
|
|
4
|
+
"cmd.new.opt.template": "Template type: lite, modular, clean",
|
|
5
|
+
"cmd.new.opt.state": "State manager: default, provider, getx",
|
|
6
|
+
"cmd.new.opt.dir": "Project storage directory",
|
|
7
|
+
"cmd.new.opt.no_cache": "Do not use cache, force pull from Git",
|
|
8
|
+
"cmd.new.opt.remote": "Use remote Gitee templates (default uses local)",
|
|
9
|
+
"cmd.sync.desc": "Sync the latest standard code snippets to the current project",
|
|
10
|
+
"cmd.add.desc": "Add Flutter code component (page/p, widget/w, component/c, ...)",
|
|
11
|
+
"cmd.add.opt.feature": "Target feature module (modular/clean mode only)",
|
|
12
|
+
"cmd.add.opt.stateful": "Create StatefulWidget (StatelessWidget by default)",
|
|
13
|
+
"cmd.add.opt.stateless": "Force create StatelessWidget (even if with ViewModel, not recommended)",
|
|
14
|
+
"cmd.add.opt.list_page": "Create list page (BaseListPage)",
|
|
15
|
+
"cmd.add.opt.no_vm": "Do not generate ViewModel (page type only)",
|
|
16
|
+
"cmd.add.opt.json": "Generate from JSON file (model type only)",
|
|
17
|
+
"cmd.add.opt.list": "View supported types list",
|
|
18
|
+
"cmd.templates.desc": "View all templates or specific template details",
|
|
19
|
+
"cmd.update.desc": "Update template cache",
|
|
20
|
+
"cmd.update.opt.force": "Force refresh and clean untracked files",
|
|
21
|
+
"cmd.cache.desc": "Cache management (action: clean)",
|
|
22
|
+
"cmd.assets.desc": "Configure app icon and splash screen",
|
|
23
|
+
"cmd.template.desc": "Manage custom project templates",
|
|
24
|
+
"cmd.template.list.desc": "List all templates",
|
|
25
|
+
"cmd.template.add.desc": "Add custom template (id: unique ID, source: Git URL or local path)",
|
|
26
|
+
"cmd.template.add.opt.local": "Add local template (source is a path)",
|
|
27
|
+
"cmd.template.add.opt.name": "Display name",
|
|
28
|
+
"cmd.template.add.opt.branch": "Git branch (default is main)",
|
|
29
|
+
"cmd.template.add.opt.desc": "Description information",
|
|
30
|
+
"cmd.template.add.opt.force": "Overwrite existing template",
|
|
31
|
+
"cmd.template.remove.desc": "Delete custom template",
|
|
32
|
+
"cmd.completion.desc": "Generate Shell auto-completion script (bash/zsh)",
|
|
33
|
+
"cmd.config.desc": "Manage project generation config (.flu-cli.json)",
|
|
34
|
+
"cmd.config.init.desc": "Initialize config file (based on current project structure)",
|
|
35
|
+
"assets.intro": "🎨 Configure App Assets (Icon & Splash)",
|
|
36
|
+
"assets.setup_icon": "Do you want to set up an App Icon?",
|
|
37
|
+
"assets.icon_path": "Enter icon path (suggested 1024x1024 PNG)",
|
|
38
|
+
"assets.setup_splash": "Do you want to set up a Splash Screen?",
|
|
39
|
+
"assets.splash_logo": "Enter Splash Logo path",
|
|
40
|
+
"assets.bg_color": "Enter Splash background color (Hex)",
|
|
41
|
+
"assets.use_bg_image": "Use a background image for Splash?",
|
|
42
|
+
"assets.bg_image_path": "Enter background image path",
|
|
43
|
+
"assets.setup_dark": "Configure Splash for Dark Mode?",
|
|
44
|
+
"assets.dark_bg_color": "Enter Dark Mode background color (Hex)",
|
|
45
|
+
"assets.use_dark_logo": "Use a separate Logo for Dark Mode?",
|
|
46
|
+
"assets.dark_logo_path": "Enter Dark Mode Logo path",
|
|
47
|
+
"assets.no_assets": "No assets selected, operation ended",
|
|
48
|
+
"assets.configuring": "Configuring assets...",
|
|
49
|
+
"assets.success": "✓ Assets configured successfully",
|
|
50
|
+
"assets.done": "✨ App assets mapping completed!",
|
|
51
|
+
"assets.failed": "Failed to configure assets",
|
|
52
|
+
"assets.error_outro": "❌ Error occurred during configuration, please check logs",
|
|
53
|
+
"assets.not_flutter": "pubspec.yaml not found. Please run this command in a Flutter project root.",
|
|
54
|
+
"common.cancel": "Operation cancelled",
|
|
55
|
+
"common.bye": "Goodbye",
|
|
56
|
+
"snippets.sync_success": "✅ Code snippets synced successfully!",
|
|
57
|
+
"snippets.detect_config": "ℹ️ Project config detected, filtering snippets...",
|
|
58
|
+
"snippets.applied_filter": "Architecture-aware filtering applied (BasePage: {basePage}, ViewModel: {viewModel})"
|
|
59
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.description": "🚀 Flutter MVVM 脚手架工具",
|
|
3
|
+
"cmd.new.desc": "创建 Flutter 项目(实时模板预览)",
|
|
4
|
+
"cmd.new.opt.template": "模板类型: lite, modular, clean",
|
|
5
|
+
"cmd.new.opt.state": "状态管理器: default, provider, getx",
|
|
6
|
+
"cmd.new.opt.dir": "项目存放目录",
|
|
7
|
+
"cmd.new.opt.no_cache": "不使用缓存,强制从 Git 拉取",
|
|
8
|
+
"cmd.new.opt.remote": "使用远程 Gitee 模板(默认使用本地模板)",
|
|
9
|
+
"cmd.sync.desc": "同步最新版本的标准代码片段到当前项目",
|
|
10
|
+
"cmd.add.desc": "添加 Flutter 代码组件 (page/p, widget/w, component/c, ...)",
|
|
11
|
+
"cmd.add.opt.feature": "所属功能模块(仅 modular/clean 模式)",
|
|
12
|
+
"cmd.add.opt.stateful": "创建 StatefulWidget(默认 StatelessWidget)",
|
|
13
|
+
"cmd.add.opt.stateless": "强制创建 StatelessWidget(即使有 ViewModel,不推荐)",
|
|
14
|
+
"cmd.add.opt.list_page": "创建列表页 (BaseListPage)",
|
|
15
|
+
"cmd.add.opt.no_vm": "不生成 ViewModel(仅 page 类型)",
|
|
16
|
+
"cmd.add.opt.json": "从 JSON 文件生成(仅 model 类型)",
|
|
17
|
+
"cmd.add.opt.list": "查看支持的类型列表",
|
|
18
|
+
"cmd.templates.desc": "查看所有模板或指定模板详情",
|
|
19
|
+
"cmd.update.desc": "更新模板缓存",
|
|
20
|
+
"cmd.update.opt.force": "强制刷新并清理未跟踪文件",
|
|
21
|
+
"cmd.cache.desc": "缓存管理 (action: clean)",
|
|
22
|
+
"cmd.assets.desc": "配置应用图标和启动图",
|
|
23
|
+
"cmd.template.desc": "管理自定义项目模板",
|
|
24
|
+
"cmd.template.list.desc": "列出所有模板",
|
|
25
|
+
"cmd.template.add.desc": "添加自定义模板 (id: 唯一标识, source: Git URL 或本地路径)",
|
|
26
|
+
"cmd.template.add.opt.local": "添加本地模板 (source 为路径)",
|
|
27
|
+
"cmd.template.add.opt.name": "显示名称",
|
|
28
|
+
"cmd.template.add.opt.branch": "Git 分支 (默认 main)",
|
|
29
|
+
"cmd.template.add.opt.desc": "描述信息",
|
|
30
|
+
"cmd.template.add.opt.force": "覆盖已存在的模板",
|
|
31
|
+
"cmd.template.remove.desc": "删除自定义模板",
|
|
32
|
+
"cmd.completion.desc": "生成 Shell 自动补全脚本 (bash/zsh)",
|
|
33
|
+
"cmd.config.desc": "管理项目生成配置 (.flu-cli.json)",
|
|
34
|
+
"cmd.config.init.desc": "初始化配置文件 (基于当前项目结构)",
|
|
35
|
+
"assets.intro": "🎨 配置应用资源 (图标 & 启动图)",
|
|
36
|
+
"assets.setup_icon": "是否要设置应用图标 (App Icon)?",
|
|
37
|
+
"assets.icon_path": "请输入图标路径 (建议 1024x1024 PNG)",
|
|
38
|
+
"assets.setup_splash": "是否要设置启动图 (Splash Screen)?",
|
|
39
|
+
"assets.splash_logo": "请输入启动页 Logo 路径",
|
|
40
|
+
"assets.bg_color": "请输入启动页背景颜色 (Hex)",
|
|
41
|
+
"assets.use_bg_image": "是否使用启动页背景图?",
|
|
42
|
+
"assets.bg_image_path": "请输入背景图路径",
|
|
43
|
+
"assets.setup_dark": "是否配置暗黑模式下的启动页?",
|
|
44
|
+
"assets.dark_bg_color": "请输入暗黑模式背景颜色 (Hex)",
|
|
45
|
+
"assets.use_dark_logo": "是否使用独立的暗黑模式 Logo?",
|
|
46
|
+
"assets.dark_logo_path": "请输入暗黑模式 Logo 路径",
|
|
47
|
+
"assets.no_assets": "未选择任何资源,操作结束",
|
|
48
|
+
"assets.configuring": "正在配置资源...",
|
|
49
|
+
"assets.success": "✓ 资源配置成功",
|
|
50
|
+
"assets.done": "✨ 应用资源映射完成!",
|
|
51
|
+
"assets.failed": "资源配置失败",
|
|
52
|
+
"assets.error_outro": "❌ 配置过程中出现错误,请检查日志",
|
|
53
|
+
"assets.not_flutter": "未检测到 pubspec.yaml,请在 Flutter 项目根目录下运行此命令",
|
|
54
|
+
"common.cancel": "操作已取消",
|
|
55
|
+
"common.bye": "再见",
|
|
56
|
+
"snippets.sync_success": "✅ 代码片段同步成功!",
|
|
57
|
+
"snippets.detect_config": "ℹ️ 检测到项目配置,正在按需过滤代码片段...",
|
|
58
|
+
"snippets.applied_filter": "已应用架构感知过滤 (BasePage: {basePage}, ViewModel: {viewModel})"
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flu-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Flutter MVVM 脚手架工具",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@clack/prompts": "^0.11.0",
|
|
40
|
-
"
|
|
40
|
+
"flu-cli-core": "^1.0.0",
|
|
41
41
|
"chalk": "^4.1.2",
|
|
42
42
|
"commander": "^11.1.0",
|
|
43
43
|
"date-fns": "^4.1.0",
|
package/release.sh
CHANGED
|
@@ -12,6 +12,7 @@ echo "===================="
|
|
|
12
12
|
GREEN='\033[0;32m'
|
|
13
13
|
YELLOW='\033[1;33m'
|
|
14
14
|
RED='\033[0;31m'
|
|
15
|
+
BLUE='\033[0;34m'
|
|
15
16
|
NC='\033[0m' # No Color
|
|
16
17
|
|
|
17
18
|
# 检查当前目录
|
|
@@ -33,6 +34,287 @@ fi
|
|
|
33
34
|
# 获取当前版本
|
|
34
35
|
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
35
36
|
echo -e "当前版本: ${GREEN}v${CURRENT_VERSION}${NC}"
|
|
37
|
+
PREV_VERSION_TAG="cli-v${CURRENT_VERSION}"
|
|
38
|
+
|
|
39
|
+
# 同步远端 tags
|
|
40
|
+
git fetch --tags >/dev/null 2>&1 || true
|
|
41
|
+
|
|
42
|
+
# 确保工作区干净
|
|
43
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
44
|
+
echo -e "${RED}错误: Git 工作区不干净,请先提交或清理变更后再发布${NC}"
|
|
45
|
+
git status --porcelain
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
PRE_RELEASE_HEAD=$(git rev-parse HEAD)
|
|
50
|
+
ROLLBACK_ENABLED=0
|
|
51
|
+
TAG_CREATED=0
|
|
52
|
+
PUBLISH_SUCCEEDED=0
|
|
53
|
+
NEW_VERSION=""
|
|
54
|
+
|
|
55
|
+
rollback_release() {
|
|
56
|
+
if [ "$ROLLBACK_ENABLED" != "1" ]; then
|
|
57
|
+
return
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
echo -e "${YELLOW}正在回滚版本变更...${NC}"
|
|
61
|
+
|
|
62
|
+
if [ "$TAG_CREATED" = "1" ] && [ -n "$NEW_VERSION" ]; then
|
|
63
|
+
if git rev-parse "cli-v${NEW_VERSION}" >/dev/null 2>&1; then
|
|
64
|
+
git tag -d "cli-v${NEW_VERSION}" >/dev/null 2>&1 || true
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
git reset --hard "$PRE_RELEASE_HEAD" >/dev/null 2>&1 || true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
open_entry_in_editor() {
|
|
72
|
+
local file="$1"
|
|
73
|
+
if command -v code >/dev/null 2>&1; then
|
|
74
|
+
code --wait "$file"
|
|
75
|
+
return
|
|
76
|
+
fi
|
|
77
|
+
if [ -n "$EDITOR" ]; then
|
|
78
|
+
"$EDITOR" "$file"
|
|
79
|
+
return
|
|
80
|
+
fi
|
|
81
|
+
vi "$file"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
on_exit() {
|
|
85
|
+
local code="$1"
|
|
86
|
+
if [ "$code" -ne 0 ]; then
|
|
87
|
+
if [ "$PUBLISH_SUCCEEDED" = "1" ]; then
|
|
88
|
+
echo -e "${YELLOW}检测到脚本中断或失败,但发布已完成,跳过自动回滚${NC}"
|
|
89
|
+
return
|
|
90
|
+
fi
|
|
91
|
+
echo -e "${YELLOW}检测到脚本中断或失败,正在回滚版本变更...${NC}"
|
|
92
|
+
rollback_release
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
trap 'on_exit $?' EXIT
|
|
97
|
+
trap 'exit 130' INT TERM
|
|
98
|
+
|
|
99
|
+
get_prev_tag() {
|
|
100
|
+
if git rev-parse "$PREV_VERSION_TAG" >/dev/null 2>&1; then
|
|
101
|
+
echo "$PREV_VERSION_TAG"
|
|
102
|
+
return
|
|
103
|
+
fi
|
|
104
|
+
# 尝试查找最近的 cli-v* tag
|
|
105
|
+
git describe --tags --match "cli-v*" --abbrev=0 2>/dev/null || true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
format_commit_subject() {
|
|
109
|
+
local subject="$1"
|
|
110
|
+
local lower
|
|
111
|
+
lower=$(echo "$subject" | tr '[:upper:]' '[:lower:]')
|
|
112
|
+
|
|
113
|
+
if echo "$lower" | grep -qE '^feat(\([^)]+\))?:'; then
|
|
114
|
+
echo "✨ $(echo "$subject" | sed -E 's/^[Ff][Ee][Aa][Tt](\([^)]+\))?:[[:space:]]*//')"
|
|
115
|
+
return
|
|
116
|
+
fi
|
|
117
|
+
if echo "$lower" | grep -qE '^fix(\([^)]+\))?:'; then
|
|
118
|
+
echo "🐛 $(echo "$subject" | sed -E 's/^[Ff][Ii][Xx](\([^)]+\))?:[[:space:]]*//')"
|
|
119
|
+
return
|
|
120
|
+
fi
|
|
121
|
+
if echo "$lower" | grep -qE '^perf(\([^)]+\))?:'; then
|
|
122
|
+
echo "⚡️ $(echo "$subject" | sed -E 's/^[Pp][Ee][Rr][Ff](\([^)]+\))?:[[:space:]]*//')"
|
|
123
|
+
return
|
|
124
|
+
fi
|
|
125
|
+
if echo "$lower" | grep -qE '^refactor(\([^)]+\))?:'; then
|
|
126
|
+
echo "♻️ $(echo "$subject" | sed -E 's/^[Rr][Ee][Ff][Aa][Cc][Tt][Oo][Rr](\([^)]+\))?:[[:space:]]*//')"
|
|
127
|
+
return
|
|
128
|
+
fi
|
|
129
|
+
if echo "$lower" | grep -qE '^docs(\([^)]+\))?:'; then
|
|
130
|
+
echo "📝 $(echo "$subject" | sed -E 's/^[Dd][Oo][Cc][Ss](\([^)]+\))?:[[:space:]]*//')"
|
|
131
|
+
return
|
|
132
|
+
fi
|
|
133
|
+
if echo "$lower" | grep -qE '^test(\([^)]+\))?:'; then
|
|
134
|
+
echo "✅ $(echo "$subject" | sed -E 's/^[Tt][Ee][Ss][Tt](\([^)]+\))?:[[:space:]]*//')"
|
|
135
|
+
return
|
|
136
|
+
fi
|
|
137
|
+
if echo "$lower" | grep -qE '^build(\([^)]+\))?:'; then
|
|
138
|
+
echo "📦 $(echo "$subject" | sed -E 's/^[Bb][Uu][Ii][Ll][Dd](\([^)]+\))?:[[:space:]]*//')"
|
|
139
|
+
return
|
|
140
|
+
fi
|
|
141
|
+
if echo "$lower" | grep -qE '^ci(\([^)]+\))?:'; then
|
|
142
|
+
echo "👷 $(echo "$subject" | sed -E 's/^[Cc][Ii](\([^)]+\))?:[[:space:]]*//')"
|
|
143
|
+
return
|
|
144
|
+
fi
|
|
145
|
+
if echo "$lower" | grep -qE '^chore(\([^)]+\))?:'; then
|
|
146
|
+
echo "🧹 $(echo "$subject" | sed -E 's/^[Cc][Hh][Oo][Rr][Ee](\([^)]+\))?:[[:space:]]*//')"
|
|
147
|
+
return
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
echo "$subject"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
is_user_facing_subject() {
|
|
154
|
+
local subject="$1"
|
|
155
|
+
local lower
|
|
156
|
+
lower=$(echo "$subject" | tr '[:upper:]' '[:lower:]')
|
|
157
|
+
|
|
158
|
+
if echo "$lower" | grep -qE '^feat(\([^)]+\))?:'; then
|
|
159
|
+
return 0
|
|
160
|
+
fi
|
|
161
|
+
if echo "$lower" | grep -qE '^fix(\([^)]+\))?:'; then
|
|
162
|
+
return 0
|
|
163
|
+
fi
|
|
164
|
+
if echo "$lower" | grep -qE '^perf(\([^)]+\))?:'; then
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
if echo "$lower" | grep -qE '^refactor(\([^)]+\))?:'; then
|
|
168
|
+
return 0
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
return 1
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# 检查提交是否影响 packages/v2
|
|
175
|
+
commit_affects_v2() {
|
|
176
|
+
local sha="$1"
|
|
177
|
+
|
|
178
|
+
local files
|
|
179
|
+
files=$(git show --name-only --pretty=format: "$sha" 2>/dev/null || true)
|
|
180
|
+
if [ -z "$files" ]; then
|
|
181
|
+
return 1
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
local relevant=1
|
|
185
|
+
local non_ignored_seen=1
|
|
186
|
+
|
|
187
|
+
while IFS= read -r file; do
|
|
188
|
+
if [ -z "$file" ]; then
|
|
189
|
+
continue
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# 忽略自身脚本和 CHANGELOG
|
|
193
|
+
if echo "$file" | grep -qE '^packages/v2/(release\.sh|CHANGELOG\.md)$'; then
|
|
194
|
+
continue
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
non_ignored_seen=0
|
|
198
|
+
|
|
199
|
+
# 只关注 packages/v2 目录下的变更,或者 packages/core (核心库变更也视为 CLI 变更)
|
|
200
|
+
if echo "$file" | grep -qE '^packages/v2/'; then
|
|
201
|
+
relevant=0
|
|
202
|
+
break
|
|
203
|
+
fi
|
|
204
|
+
if echo "$file" | grep -qE '^packages/core/'; then
|
|
205
|
+
relevant=0
|
|
206
|
+
break
|
|
207
|
+
fi
|
|
208
|
+
done <<< "$files"
|
|
209
|
+
|
|
210
|
+
if [ "$relevant" -eq 0 ]; then
|
|
211
|
+
return 0
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
if [ "$non_ignored_seen" -ne 0 ]; then
|
|
215
|
+
return 1
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
return 1
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
is_changelog_noise_subject() {
|
|
222
|
+
local subject="$1"
|
|
223
|
+
local lower
|
|
224
|
+
lower=$(echo "$subject" | tr '[:upper:]' '[:lower:]')
|
|
225
|
+
|
|
226
|
+
if echo "$lower" | grep -qE '^chore\(release\):'; then
|
|
227
|
+
return 0
|
|
228
|
+
fi
|
|
229
|
+
if echo "$lower" | grep -qE '(回退|回滚).*(版本号|版本)'; then
|
|
230
|
+
return 0
|
|
231
|
+
fi
|
|
232
|
+
if echo "$lower" | grep -qE '(revert|rollback).*(version)'; then
|
|
233
|
+
return 0
|
|
234
|
+
fi
|
|
235
|
+
if echo "$lower" | grep -qE 'bump(ing)? version'; then
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
if echo "$lower" | grep -qE 'chore\(cli\): release'; then
|
|
239
|
+
return 0
|
|
240
|
+
fi
|
|
241
|
+
if echo "$lower" | grep -qE '^release v[0-9]+'; then
|
|
242
|
+
return 0
|
|
243
|
+
fi
|
|
244
|
+
if echo "$lower" | grep -qE '^更新 flu-cli 版本号'; then
|
|
245
|
+
return 0
|
|
246
|
+
fi
|
|
247
|
+
if echo "$lower" | grep -qE '^优化.*(发布流程|配置)'; then
|
|
248
|
+
# 这种比较宽泛,但也可能是噪音,视情况而定
|
|
249
|
+
# 暂时保留,由用户手动剔除
|
|
250
|
+
return 1
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
return 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
build_changelog_bullets_from_git() {
|
|
257
|
+
local max_count="$1"
|
|
258
|
+
local prev_tag
|
|
259
|
+
prev_tag=$(get_prev_tag)
|
|
260
|
+
|
|
261
|
+
local range
|
|
262
|
+
if [ -n "$prev_tag" ]; then
|
|
263
|
+
range="${prev_tag}..HEAD"
|
|
264
|
+
else
|
|
265
|
+
# range="HEAD"
|
|
266
|
+
# 如果找不到上一版本 tag,说明可能是首次使用此脚本或 tag 丢失
|
|
267
|
+
# 此时只取最近的 commit,避免列出整个历史
|
|
268
|
+
echo -e "${YELLOW}提示: 未找到上一版本 tag (cli-v*),将从最近提交中提取日志${NC}" >&2
|
|
269
|
+
range="HEAD"
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
local output=""
|
|
273
|
+
local count=0
|
|
274
|
+
local commits
|
|
275
|
+
if [ -n "$prev_tag" ]; then
|
|
276
|
+
commits=$(git rev-list --no-merges "$range" 2>/dev/null || true)
|
|
277
|
+
else
|
|
278
|
+
# 无 tag 时,限制最大扫描深度,避免噪音过多
|
|
279
|
+
# 默认扫描最近 50 条,再由 max_count 截断
|
|
280
|
+
commits=$(git rev-list --no-merges -n 50 HEAD 2>/dev/null || true)
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
while IFS= read -r sha; do
|
|
284
|
+
if [ -z "$sha" ]; then
|
|
285
|
+
continue
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
local subject
|
|
289
|
+
subject=$(git log -1 --pretty=format:%s "$sha" 2>/dev/null || true)
|
|
290
|
+
if [ -z "$subject" ]; then
|
|
291
|
+
continue
|
|
292
|
+
fi
|
|
293
|
+
if is_changelog_noise_subject "$subject"; then
|
|
294
|
+
continue
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# 检查是否影响 v2
|
|
298
|
+
if ! commit_affects_v2 "$sha"; then
|
|
299
|
+
continue
|
|
300
|
+
fi
|
|
301
|
+
|
|
302
|
+
# 默认包含所有变更,但可以通过 is_user_facing_subject 过滤非用户关注的
|
|
303
|
+
# 这里为了更全面的日志,暂时放宽,只要影响 v2 且不是 noise 就展示
|
|
304
|
+
# 但为了美观,如果是 feat/fix/perf/refactor 等,会优先展示
|
|
305
|
+
|
|
306
|
+
local formatted
|
|
307
|
+
formatted=$(format_commit_subject "$subject")
|
|
308
|
+
output="${output}- ${formatted}"$'\n'
|
|
309
|
+
count=$((count + 1))
|
|
310
|
+
if [ "$count" -ge "$max_count" ]; then
|
|
311
|
+
break
|
|
312
|
+
fi
|
|
313
|
+
done <<< "$commits"
|
|
314
|
+
|
|
315
|
+
echo "$output"
|
|
316
|
+
}
|
|
317
|
+
|
|
36
318
|
|
|
37
319
|
# 询问发布类型
|
|
38
320
|
echo ""
|
|
@@ -54,6 +336,9 @@ case $version_choice in
|
|
|
54
336
|
;;
|
|
55
337
|
esac
|
|
56
338
|
|
|
339
|
+
# 开启回滚保护
|
|
340
|
+
ROLLBACK_ENABLED=1
|
|
341
|
+
|
|
57
342
|
# 更新版本号
|
|
58
343
|
if [ -n "$VERSION_TYPE" ]; then
|
|
59
344
|
echo ""
|
|
@@ -73,47 +358,100 @@ npm test || echo -e "${YELLOW}警告: 测试未通过或未配置${NC}"
|
|
|
73
358
|
# 更新 CHANGELOG
|
|
74
359
|
if [ -n "$VERSION_TYPE" ]; then
|
|
75
360
|
echo ""
|
|
76
|
-
echo -e "${
|
|
77
|
-
echo -e "
|
|
78
|
-
|
|
361
|
+
echo -e "${BLUE}更新内容来源:${NC}"
|
|
362
|
+
echo -e " 1) 自动生成(基于 git 提交日志)"
|
|
363
|
+
echo -e " 2) 手动输入(多条请用分号分隔)"
|
|
364
|
+
echo -e " 3) 跳过(不更新 CHANGELOG)"
|
|
365
|
+
read -r -p "请选择 (1/2/3,默认 1): " changelog_mode
|
|
366
|
+
if [ -z "$changelog_mode" ]; then
|
|
367
|
+
changelog_mode="1"
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
RELEASE_DATE=$(date +"%Y-%m-%d")
|
|
371
|
+
NEW_ENTRY=$(printf "## [%s] - %s\n\n### 更新内容" "$NEW_VERSION" "$RELEASE_DATE")
|
|
79
372
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
373
|
+
changelog_bullets=""
|
|
374
|
+
|
|
375
|
+
if [ "$changelog_mode" = "1" ]; then
|
|
376
|
+
read -r -p "最多取多少条提交(默认 30): " max_commits
|
|
377
|
+
if [ -z "$max_commits" ]; then
|
|
378
|
+
max_commits="30"
|
|
379
|
+
fi
|
|
380
|
+
changelog_bullets=$(build_changelog_bullets_from_git "$max_commits")
|
|
86
381
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
382
|
+
if [ -n "$changelog_bullets" ]; then
|
|
383
|
+
echo ""
|
|
384
|
+
echo -e "${BLUE}自动生成的更新内容预览:${NC}"
|
|
385
|
+
echo -e "$changelog_bullets"
|
|
386
|
+
echo ""
|
|
387
|
+
read -r -p "是否使用该内容写入 CHANGELOG? (y/n): " use_auto
|
|
388
|
+
if [ "$use_auto" != "y" ]; then
|
|
389
|
+
changelog_bullets=""
|
|
94
390
|
fi
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
391
|
+
fi
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
if [ "$changelog_mode" != "3" ] && ([ "$changelog_mode" = "2" ] || [ -z "$changelog_bullets" ]); then
|
|
395
|
+
echo ""
|
|
396
|
+
echo -e "${BLUE}请输入本次更新的主要内容(多条请用分号分隔):${NC}"
|
|
397
|
+
read -r -p "更新内容: " changelog_content
|
|
398
|
+
if [ -n "$changelog_content" ]; then
|
|
399
|
+
IFS=';' read -ra CHANGES <<< "$changelog_content"
|
|
400
|
+
for change in "${CHANGES[@]}"; do
|
|
401
|
+
change=$(echo "$change" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
|
|
402
|
+
if [ -n "$change" ]; then
|
|
403
|
+
changelog_bullets="${changelog_bullets}- ${change}"$'\n'
|
|
404
|
+
fi
|
|
405
|
+
done
|
|
406
|
+
fi
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# 确保末尾有换行
|
|
410
|
+
if [ -n "$changelog_bullets" ] && [ "${changelog_bullets: -1}" != $'\n' ]; then
|
|
411
|
+
changelog_bullets="${changelog_bullets}"$'\n'
|
|
412
|
+
fi
|
|
413
|
+
|
|
414
|
+
if [ "$changelog_mode" != "3" ] && [ -n "$changelog_bullets" ]; then
|
|
415
|
+
ENTRY_FILE=$(mktemp)
|
|
416
|
+
printf "%s\n\n" "$NEW_ENTRY" > "$ENTRY_FILE"
|
|
417
|
+
printf "%s" "$changelog_bullets" >> "$ENTRY_FILE"
|
|
418
|
+
printf "\n" >> "$ENTRY_FILE"
|
|
419
|
+
|
|
420
|
+
echo ""
|
|
421
|
+
read -r -p "是否打开编辑器检查/编辑后再写入 CHANGELOG? (y/n,默认 y): " edit_entry
|
|
422
|
+
if [ -z "$edit_entry" ]; then
|
|
423
|
+
edit_entry="y"
|
|
424
|
+
fi
|
|
425
|
+
if [ "$edit_entry" = "y" ]; then
|
|
426
|
+
open_entry_in_editor "$ENTRY_FILE"
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
echo ""
|
|
430
|
+
echo -e "${BLUE}最终将写入 CHANGELOG 的内容预览:${NC}"
|
|
431
|
+
cat "$ENTRY_FILE"
|
|
432
|
+
echo ""
|
|
100
433
|
|
|
101
|
-
#
|
|
434
|
+
# 写入 CHANGELOG.md
|
|
102
435
|
if [ -f "CHANGELOG.md" ]; then
|
|
103
|
-
|
|
436
|
+
TEMP_FILE=$(mktemp)
|
|
437
|
+
awk -v entryFile="$ENTRY_FILE" '
|
|
104
438
|
/^## / && !inserted {
|
|
105
|
-
|
|
439
|
+
while ((getline line < entryFile) > 0) {
|
|
440
|
+
print line
|
|
441
|
+
}
|
|
442
|
+
close(entryFile)
|
|
106
443
|
inserted=1
|
|
107
444
|
}
|
|
108
445
|
{print}
|
|
109
446
|
' CHANGELOG.md > "$TEMP_FILE"
|
|
110
447
|
mv "$TEMP_FILE" CHANGELOG.md
|
|
448
|
+
rm -f "$ENTRY_FILE"
|
|
111
449
|
echo -e "${GREEN}✓ CHANGELOG.md 已更新${NC}"
|
|
112
450
|
else
|
|
113
|
-
echo -e "${YELLOW}警告: CHANGELOG.md
|
|
451
|
+
echo -e "${YELLOW}警告: CHANGELOG.md 不存在,创建新文件${NC}"
|
|
452
|
+
cat "$ENTRY_FILE" > CHANGELOG.md
|
|
453
|
+
rm -f "$ENTRY_FILE"
|
|
114
454
|
fi
|
|
115
|
-
else
|
|
116
|
-
echo -e "${YELLOW}跳过 CHANGELOG 更新${NC}"
|
|
117
455
|
fi
|
|
118
456
|
fi
|
|
119
457
|
|
|
@@ -147,23 +485,33 @@ fi
|
|
|
147
485
|
echo ""
|
|
148
486
|
echo -e "${YELLOW}发布到 NPM...${NC}"
|
|
149
487
|
npm publish
|
|
488
|
+
PUBLISH_SUCCEEDED=1
|
|
150
489
|
|
|
151
490
|
# 提交代码并打标签
|
|
152
491
|
if [ -n "$VERSION_TYPE" ]; then
|
|
153
492
|
echo ""
|
|
154
493
|
echo -e "${YELLOW}提交代码并打标签...${NC}"
|
|
155
494
|
|
|
156
|
-
|
|
157
|
-
git add .
|
|
495
|
+
# 提交 package.json 和 CHANGELOG.md
|
|
496
|
+
git add package.json CHANGELOG.md
|
|
158
497
|
git commit -m "chore(cli): release v${NEW_VERSION}"
|
|
159
|
-
|
|
498
|
+
|
|
499
|
+
# 打 Tag (使用 cli-v 前缀)
|
|
500
|
+
TAG_NAME="cli-v${NEW_VERSION}"
|
|
501
|
+
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
|
|
502
|
+
echo -e "${YELLOW}警告: Tag $TAG_NAME 已存在,跳过创建${NC}"
|
|
503
|
+
else
|
|
504
|
+
git tag -a "$TAG_NAME" -m "chore(release): v${NEW_VERSION}"
|
|
505
|
+
TAG_CREATED=1
|
|
506
|
+
fi
|
|
507
|
+
ROLLBACK_ENABLED=0
|
|
160
508
|
|
|
161
509
|
echo -e "${GREEN}✓ 代码已提交并打标签${NC}"
|
|
162
510
|
|
|
163
511
|
read -r -p "是否推送到远程仓库? (y/n): " push_confirm
|
|
164
512
|
if [ "$push_confirm" = "y" ]; then
|
|
165
513
|
git push origin main
|
|
166
|
-
git push origin "
|
|
514
|
+
git push origin "$TAG_NAME"
|
|
167
515
|
echo -e "${GREEN}✓ 已推送到远程仓库${NC}"
|
|
168
516
|
fi
|
|
169
517
|
fi
|
|
@@ -171,7 +519,7 @@ fi
|
|
|
171
519
|
echo ""
|
|
172
520
|
echo -e "${GREEN}===================="
|
|
173
521
|
echo "✅ 发布完成!"
|
|
174
|
-
echo "
|
|
522
|
+
echo "===================="
|
|
175
523
|
|
|
176
524
|
echo ""
|
|
177
525
|
echo "下一步:"
|
|
@@ -20,16 +20,16 @@ const sourceDir = join(rootDir, 'templates', 'base_files');
|
|
|
20
20
|
// 目标模板配置
|
|
21
21
|
const templates = [
|
|
22
22
|
{
|
|
23
|
-
name: '
|
|
24
|
-
basePath: join(rootDir, '..', '
|
|
23
|
+
name: 'template_lite',
|
|
24
|
+
basePath: join(rootDir, '..', 'template_lite', 'lib', 'base')
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
name: '
|
|
28
|
-
basePath: join(rootDir, '..', '
|
|
27
|
+
name: 'template_modular',
|
|
28
|
+
basePath: join(rootDir, '..', 'template_modular', 'lib', 'core', 'base')
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
name: '
|
|
32
|
-
basePath: join(rootDir, '..', '
|
|
31
|
+
name: 'template_clean',
|
|
32
|
+
basePath: join(rootDir, '..', 'template_clean', 'lib', 'core', 'base')
|
|
33
33
|
}
|
|
34
34
|
];
|
|
35
35
|
|
|
@@ -62,9 +62,9 @@ if [ ${#REPOS[@]} -eq 0 ]; then
|
|
|
62
62
|
"flu-cli-docs"
|
|
63
63
|
"flu-core"
|
|
64
64
|
"flu-cli-examples"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
65
|
+
"template_lite"
|
|
66
|
+
"template_modular"
|
|
67
|
+
"template_clean"
|
|
68
68
|
)
|
|
69
69
|
fi
|
|
70
70
|
|
|
@@ -98,4 +98,4 @@ echo ""
|
|
|
98
98
|
echo -e "${BLUE}💡 下一步:${NC}"
|
|
99
99
|
echo "1. cd flu-cli-v2 && npm install"
|
|
100
100
|
echo "2. cd flu-cli-docs && npm install"
|
|
101
|
-
echo "3. cd
|
|
101
|
+
echo "3. cd template_lite && ./init.sh"
|