pindo 5.6.6 → 5.7.1

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.
@@ -60,6 +60,8 @@ module Pindo
60
60
 
61
61
  # 定义要添加的gitignore规则数组
62
62
  ignore_rules = [
63
+ 'Temp',
64
+ 'Logs',
63
65
  'build_ios.log',
64
66
  'feishu.json',
65
67
  'CHANGELOG.md',
@@ -216,37 +216,11 @@ module Pindo
216
216
 
217
217
  puts "Unity command: #{cmd_args.join(' ')}"
218
218
  puts ""
219
- puts "\e[33m正在执行 Unity 构建,这可能需要几分钟时间,请耐心等待...\e[0m"
220
219
 
221
220
  # 根据平台显示不同的构建步骤
222
221
  platform = additional_args[:platform]
223
- if platform == 'Android'
224
- puts "\e[36m提示: Unity 正在后台处理项目,包括以下步骤:\e[0m"
225
- puts "\e[36m • 编译 C# 脚本\e[0m"
226
- puts "\e[36m • 处理资源文件\e[0m"
227
- puts "\e[36m • 生成 Android 项目\e[0m"
228
- puts "\e[36m • 构建 AAB 文件\e[0m"
229
- elsif platform == 'iOS'
230
- puts "\e[36m提示: Unity 正在后台处理项目,包括以下步骤:\e[0m"
231
- puts "\e[36m • 编译 C# 脚本\e[0m"
232
- puts "\e[36m • 处理资源文件\e[0m"
233
- puts "\e[36m • 生成 iOS 项目\e[0m"
234
- puts "\e[36m • 构建 Xcode 项目\e[0m"
235
- elsif platform == 'WebGL'
236
- puts "\e[36m提示: Unity 正在后台处理项目,包括以下步骤:\e[0m"
237
- puts "\e[36m • 编译 C# 脚本\e[0m"
238
- puts "\e[36m • 处理资源文件\e[0m"
239
- puts "\e[36m • 生成 WebGL 项目\e[0m"
240
- puts "\e[36m • 构建 Web 资源\e[0m"
241
- else
242
- puts "\e[36m提示: Unity 正在后台处理项目,包括以下步骤:\e[0m"
243
- puts "\e[36m • 编译 C# 脚本\e[0m"
244
- puts "\e[36m • 处理资源文件\e[0m"
245
- puts "\e[36m • 生成项目文件\e[0m"
246
- end
222
+ puts "\e[33m正在执行 Unity 构建(#{platform} 平台),这可能需要几分钟时间,长时间无响应请完全终止当前终端后重试\e[0m"
247
223
 
248
- puts "\e[36m如果长时间无响应(Unity 进程可能已卡死),请完全终止当前终端后重试\e[0m"
249
- puts "\e[36m或者,打开 Unity Editor 通过构建工具执行一次项目导出后,关闭 Unity Editor 再执行此命令\e[0m"
250
224
  puts ""
251
225
 
252
226
  # 使用更智能的进度检测机制
@@ -324,15 +298,6 @@ module Pindo
324
298
  end
325
299
  end
326
300
 
327
- # 检查是否长时间无输出(可能卡死)
328
- if Time.now - last_output_time > 300 # 5分钟无输出
329
- puts "\n\e[33m⚠️ Unity 构建已超过5分钟无输出,可能遇到问题\e[0m"
330
- puts "\e[33m建议:\e[0m"
331
- puts "\e[33m 1. 检查 Unity Editor 是否有弹窗需要确认\e[0m"
332
- puts "\e[33m 2. 查看 Unity 日志文件\e[0m"
333
- puts "\e[33m 3. 如确认卡死,可按 Ctrl+C 终止构建\e[0m"
334
- last_output_time = Time.now # 重置计时器
335
- end
336
301
  end
337
302
 
338
303
  # 读取剩余输出
@@ -50,7 +50,176 @@ module Pindo
50
50
  return @has_login
51
51
  end
52
52
 
53
- def prepare_upload(working_directory:nil, proj_name:nil)
53
+ # 尝试从 JPSBuildConfig.json 加载配置
54
+ #
55
+ # @param working_directory [String] 工作目录
56
+ # @param package_type [String] 包类型 ("ipa" | "apk" | "zip")
57
+ # @return [Hash] 结果哈希
58
+ # - valid [Boolean] 配置是否完全有效
59
+ # - project_name [String] 项目名称
60
+ # - project_id [String] 项目ID
61
+ # - workflow_info [Hash] 工作流信息(如果有效)
62
+ def try_load_jps_build_config(working_directory:, package_type:)
63
+ result = {
64
+ valid: false,
65
+ project_name: nil,
66
+ project_id: nil,
67
+ workflow_info: nil
68
+ }
69
+
70
+ # 1. 确定配置文件路径
71
+ config_file = determine_config_file_path(working_directory)
72
+
73
+ unless File.exist?(config_file)
74
+ puts "[JPSConfig] 配置文件不存在" if ENV['DEBUG']
75
+ return result
76
+ end
77
+
78
+ # 2. 读取并解析配置
79
+ begin
80
+ config = JSON.parse(File.read(config_file))
81
+ rescue => e
82
+ puts "[JPSConfig] 配置文件解析失败: #{e.message}"
83
+ return result
84
+ end
85
+
86
+ # 3. 提取项目信息
87
+ result[:project_name] = config['project_name']
88
+ result[:project_id] = config['project_id']
89
+
90
+ # 4. 验证项目ID
91
+ if result[:project_id].nil? || result[:project_id].to_s.empty?
92
+ puts "[JPSConfig] 项目ID为空" if ENV['DEBUG']
93
+ return result
94
+ end
95
+
96
+ # 5. 确定 workflow_key
97
+ workflow_key = case package_type
98
+ when 'ipa' then 'ipa_workflow'
99
+ when 'apk' then 'apk_workflow'
100
+ when 'zip' then 'webgl_workflow'
101
+ else
102
+ puts "[JPSConfig] 不支持的 package_type: #{package_type}"
103
+ return result
104
+ end
105
+
106
+ # 6. 验证工作流配置
107
+ workflow = config[workflow_key]
108
+
109
+ unless workflow_valid?(workflow)
110
+ puts "[JPSConfig] #{workflow_key} 配置无效或不完整" if ENV['DEBUG']
111
+ return result
112
+ end
113
+
114
+ # 7. 所有验证通过
115
+ result[:valid] = true
116
+ result[:workflow_info] = {
117
+ workflow_id: workflow['workflow_id'],
118
+ tab_name: workflow['tab_name'],
119
+ package_name: workflow['package_name'],
120
+ package_type: workflow['package_type'],
121
+ manage_type: workflow['manage_type'] || ""
122
+ }
123
+
124
+ puts "[JPSConfig] 配置验证通过" if ENV['DEBUG']
125
+ return result
126
+ end
127
+
128
+ # 为项目选择工作流
129
+ #
130
+ # @param project_id [String] 项目ID
131
+ # @param package_type [String] 包类型
132
+ # @param working_directory [String] 工作目录
133
+ # @return [Hash] 选择的工作流信息
134
+ def select_workflow_for_project(project_id:, package_type:, working_directory:)
135
+ # 1. 从 JPS API 获取可用工作流列表
136
+ Funlog.instance.fancyinfo_start("正在获取可用工作流...")
137
+
138
+ workflow_result = @pgyer_client.get_project_workflows(project_id: project_id)
139
+
140
+ if workflow_result.nil? || workflow_result['data'].nil?
141
+ raise Informative, "获取工作流列表失败"
142
+ end
143
+
144
+ workflows = workflow_result['data']
145
+
146
+ if workflows.empty?
147
+ raise Informative, "该项目没有可用的工作流"
148
+ end
149
+
150
+ Funlog.instance.fancyinfo_success("获取工作流列表成功,共 #{workflows.size} 个工作流")
151
+
152
+ # 2. 根据 package_type 过滤工作流(如果 API 返回的数据中有 packageType 字段)
153
+ filtered_workflows = workflows.select do |w|
154
+ # 如果工作流有 packageType 字段,进行过滤
155
+ if w['packageType']
156
+ case package_type
157
+ when 'ipa'
158
+ w['packageType'] == 'ipa'
159
+ when 'apk'
160
+ w['packageType'] == 'apk'
161
+ when 'zip'
162
+ w['packageType'] == 'zip'
163
+ else
164
+ true
165
+ end
166
+ else
167
+ # 如果没有 packageType 字段,显示所有工作流
168
+ true
169
+ end
170
+ end
171
+
172
+ # 如果过滤后没有结果,使用所有工作流
173
+ display_workflows = filtered_workflows.empty? ? workflows : filtered_workflows
174
+
175
+ # 3. 根据匹配数量决定选择方式
176
+ type_desc = case package_type
177
+ when 'ipa' then 'iOS IPA'
178
+ when 'apk' then 'Android APK'
179
+ when 'zip' then 'WebGL'
180
+ else package_type
181
+ end
182
+
183
+ workflow = nil
184
+
185
+ if display_workflows.empty?
186
+ # 情况1: 没有匹配的工作流
187
+ raise Informative, "没有找到匹配 #{type_desc} 的工作流"
188
+
189
+ elsif display_workflows.size == 1
190
+ # 情况2: 只有1个匹配的工作流,自动选择
191
+ workflow = display_workflows.first
192
+ workflow_name = workflow['tabName'] || workflow['tab_name']
193
+ Funlog.instance.fancyinfo_success("自动选择唯一匹配的工作流: #{workflow_name}")
194
+
195
+ else
196
+ # 情况3: 有多个匹配的工作流,让用户选择
197
+ cli = HighLine.new
198
+ workflow_choices = display_workflows.map do |w|
199
+ "#{w['tabName'] || w['tab_name']} (ID: #{w['id'] || w['workflow_id']})"
200
+ end
201
+
202
+ selected = cli.choose do |menu|
203
+ menu.prompt = "请选择 #{type_desc} 工作流:"
204
+ menu.choices(*workflow_choices)
205
+ end
206
+
207
+ # 提取选择的工作流
208
+ selected_index = workflow_choices.index(selected)
209
+ workflow = display_workflows[selected_index]
210
+ end
211
+
212
+ # 4. 返回标准化的工作流信息
213
+ {
214
+ workflow_id: workflow['id'] || workflow['workflow_id'],
215
+ tab_name: workflow['tabName'] || workflow['tab_name'],
216
+ package_name: workflow['packageName'] || workflow['package_name'] || workflow['tabName'] || workflow['tab_name'],
217
+ package_type: package_type,
218
+ manage_type: workflow['manageType'] || workflow['manage_type'] || ""
219
+ }
220
+ end
221
+
222
+ def prepare_upload(working_directory:nil, proj_name:nil, package_type:nil)
54
223
  upload_proj_name = proj_name
55
224
  if upload_proj_name.nil? || upload_proj_name.empty?
56
225
  upload_proj_name = @proj_name
@@ -64,6 +233,40 @@ module Pindo
64
233
  puts
65
234
  end
66
235
 
236
+ # 【核心优化】尝试从 JPSBuildConfig.json 加载配置
237
+ if package_type
238
+ config_result = try_load_jps_build_config(
239
+ working_directory: working_directory,
240
+ package_type: package_type
241
+ )
242
+
243
+ if config_result[:valid]
244
+ # 配置有效,直接使用
245
+ puts "\n使用 JPSBuildConfig.json 配置:"
246
+ puts " 项目: #{config_result[:project_name]}"
247
+ puts " 工作流: #{config_result[:workflow_info][:tab_name]}"
248
+
249
+ # 确保已登录
250
+ unless login
251
+ raise Informative, "请先登录JPS网站"
252
+ end
253
+
254
+ # 获取完整的 app_info_obj
255
+ app_info_obj = find_app_info_with_obj_list(
256
+ proj_name: config_result[:project_name]
257
+ )
258
+
259
+ if app_info_obj
260
+ @proj_name = config_result[:project_name]
261
+ return app_info_obj, config_result[:workflow_info]
262
+ else
263
+ puts "配置的项目不存在,将重新选择"
264
+ end
265
+ end
266
+ end
267
+
268
+ # ===== 配置无效或不存在,进入用户选择流程 =====
269
+
67
270
  app_info_obj = nil
68
271
  if login
69
272
 
@@ -78,7 +281,28 @@ module Pindo
78
281
  context = Pindo::PindoContext.instance
79
282
  context.set_selection(Pindo::PindoContext::SelectionKey::PROJECT_NAME, upload_proj_name)
80
283
  end
81
- return app_info_obj
284
+
285
+ # 如果提供了 package_type,选择工作流并保存配置
286
+ if package_type
287
+ workflow_info = select_workflow_for_project(
288
+ project_id: app_info_obj["id"],
289
+ package_type: package_type,
290
+ working_directory: working_directory
291
+ )
292
+
293
+ save_jps_build_config(
294
+ working_directory: working_directory,
295
+ app_info_obj: app_info_obj,
296
+ workflow_info: workflow_info,
297
+ package_type: package_type
298
+ )
299
+
300
+ @proj_name = upload_proj_name
301
+ return app_info_obj, workflow_info
302
+ else
303
+ @proj_name = upload_proj_name
304
+ return app_info_obj
305
+ end
82
306
  end
83
307
 
84
308
  proj_name_array = []
@@ -137,7 +361,7 @@ module Pindo
137
361
  # 直接使用缓存的选择,跳过后续选择逻辑
138
362
  end
139
363
 
140
-
364
+
141
365
  end
142
366
 
143
367
  # 只有在没有缓存或缓存无效,且没有环境变量时才显示选择菜单
@@ -178,8 +402,24 @@ module Pindo
178
402
  # 记录上次上传的项目
179
403
  PindoUserLocalConfig.instance.write_last_work_project(proj_name:app_info_obj["projectName"])
180
404
 
181
- # 保存项目信息到 JPSBuildConfig.json
182
- save_jps_build_config(working_directory: working_directory, app_info_obj: app_info_obj)
405
+ # 如果提供了 package_type,选择工作流并保存配置
406
+ if package_type
407
+ workflow_info = select_workflow_for_project(
408
+ project_id: app_info_obj["id"],
409
+ package_type: package_type,
410
+ working_directory: working_directory
411
+ )
412
+
413
+ save_jps_build_config(
414
+ working_directory: working_directory,
415
+ app_info_obj: app_info_obj,
416
+ workflow_info: workflow_info,
417
+ package_type: package_type
418
+ )
419
+
420
+ @proj_name = upload_proj_name
421
+ return app_info_obj, workflow_info
422
+ end
183
423
  end
184
424
 
185
425
 
@@ -869,76 +1109,145 @@ module Pindo
869
1109
 
870
1110
  private
871
1111
 
872
- # 保存项目信息到 JPSBuildConfig.json
873
- # Unity 工程:保存到仓库根目录的 ProjectSettings/JPSBuildConfig.json
874
- # 其他工程:保存到仓库根目录的 JPSBuildConfig.json
875
- def save_jps_build_config(working_directory: nil, app_info_obj: nil)
876
- return if working_directory.nil? || app_info_obj.nil?
877
-
878
- # 获取 Git 仓库根目录
879
- repo_root_dir = nil
880
- if is_git_directory?(local_repo_dir: working_directory)
881
- repo_root_dir = git_root_directory(local_repo_dir: working_directory)
882
- end
883
-
884
- # 如果不是 Git 仓库,使用 working_directory
885
- repo_root_dir ||= working_directory
886
-
887
- # 判断工程类型并确定配置文件路径
888
- # Unity 工程:仓库根目录/ProjectSettings/JPSBuildConfig.json
889
- # iOS/Android 工程:仓库根目录/JPSBuildConfig.json
890
- config_file = nil
891
- project_type = nil
892
-
893
- # 检查是否是 Unity 工程(仓库根目录存在 ProjectSettings 目录)
894
- if File.directory?(File.join(repo_root_dir, 'ProjectSettings'))
895
- config_file = File.join(repo_root_dir, 'ProjectSettings', 'JPSBuildConfig.json')
896
- project_type = 'Unity'
897
- else
898
- # iOS/Android 工程,保存到仓库根目录
899
- config_file = File.join(repo_root_dir, 'JPSBuildConfig.json')
900
- project_type = 'iOS/Android'
901
- end
1112
+ # 确定 JPSBuildConfig.json 文件路径
1113
+ # Unity 工程:仓库根目录/ProjectSettings/JPSBuildConfig.json
1114
+ # iOS/Android 工程:仓库根目录/JPSBuildConfig.json
1115
+ #
1116
+ # @param working_directory [String] 工作目录
1117
+ # @return [String] 配置文件完整路径
1118
+ def determine_config_file_path(working_directory)
1119
+ # 获取 Git 仓库根目录
1120
+ repo_root_dir = nil
1121
+ if is_git_directory?(local_repo_dir: working_directory)
1122
+ repo_root_dir = git_root_directory(local_repo_dir: working_directory)
1123
+ end
1124
+ repo_root_dir ||= working_directory
1125
+
1126
+ # 判断工程类型
1127
+ if File.directory?(File.join(repo_root_dir, 'ProjectSettings'))
1128
+ File.join(repo_root_dir, 'ProjectSettings', 'JPSBuildConfig.json')
1129
+ else
1130
+ File.join(repo_root_dir, 'JPSBuildConfig.json')
1131
+ end
1132
+ end
902
1133
 
903
- # 准备要保存的数据
904
- new_config = {
905
- "project_name" => app_info_obj["projectName"],
906
- "project_scheme" => app_info_obj["scheme"] || app_info_obj["projectName"].to_s.downcase.strip.gsub(/[\s\-_]/, ''),
907
- "project_id" => app_info_obj["id"]
908
- }
1134
+ # 验证工作流配置是否有效
1135
+ #
1136
+ # @param workflow [Hash] 工作流配置对象
1137
+ # @return [Boolean] 是否有效
1138
+ def workflow_valid?(workflow)
1139
+ return false if workflow.nil?
1140
+ return false if workflow['workflow_id'].nil? || workflow['workflow_id'].to_s.empty?
1141
+ return false if workflow['package_name'].nil? || workflow['package_name'].empty?
1142
+ true
1143
+ end
909
1144
 
910
- # 检查文件是否已存在
911
- if File.exist?(config_file)
912
- begin
913
- # 读取现有配置
914
- existing_content = File.read(config_file)
915
- existing_config = JSON.parse(existing_content)
916
-
917
- # 如果 project_name 一样,则不需要重新保存
918
- if existing_config['project_name'] == new_config['project_name']
919
- puts "[PgyerHelper] JPSBuildConfig.json 项目名称未变化,跳过保存 (#{project_type})" if ENV['DEBUG']
920
- return
921
- end
922
- rescue => e
923
- puts "[PgyerHelper] 读取现有 JPSBuildConfig.json 失败: #{e.message}" if ENV['DEBUG']
924
- # 继续保存新配置
925
- end
926
- else
927
- # 文件不存在,需要创建目录(如果是 Unity 工程)
928
- if project_type == 'Unity'
929
- project_settings_dir = File.join(working_directory, 'ProjectSettings')
930
- FileUtils.mkdir_p(project_settings_dir) unless File.directory?(project_settings_dir)
931
- end
932
- end
1145
+ # 创建新的 JPSBuildConfig.json 配置文件
1146
+ #
1147
+ # @param config_file [String] 配置文件路径
1148
+ # @param project_id [String] 项目ID
1149
+ # @param project_name [String] 项目名称
1150
+ # @param project_scheme [String] 项目scheme
1151
+ # @param workflow_key [String] 工作流键名
1152
+ # @param workflow_data [Hash] 工作流数据
1153
+ def create_new_config(config_file, project_id, project_name, project_scheme, workflow_key, workflow_data)
1154
+ new_config = {
1155
+ "project_name" => project_name,
1156
+ "project_scheme" => project_scheme,
1157
+ "project_id" => project_id,
1158
+ workflow_key => workflow_data
1159
+ }
1160
+
1161
+ # 确保目录存在
1162
+ config_dir = File.dirname(config_file)
1163
+ FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
1164
+
1165
+ File.write(config_file, JSON.pretty_generate(new_config))
1166
+ puts "已创建 JPSBuildConfig.json"
1167
+ end
933
1168
 
934
- # 保存配置到文件
935
- begin
936
- File.write(config_file, JSON.pretty_generate(new_config))
937
- puts "[PgyerHelper] 已保存项目信息到 JPSBuildConfig.json (#{project_type})" if ENV['DEBUG']
938
- puts "项目信息已保存: #{new_config['project_name']}"
939
- rescue => e
940
- puts "[PgyerHelper] 保存 JPSBuildConfig.json 失败: #{e.message}"
941
- end
1169
+ # 保存项目信息到 JPSBuildConfig.json
1170
+ # 支持新的工作流格式,根据 project_id 和 workflow_id 判断是否需要更新
1171
+ #
1172
+ # @param working_directory [String] 工作目录
1173
+ # @param app_info_obj [Hash] 项目信息对象
1174
+ # @param workflow_info [Hash] 工作流信息
1175
+ # @param package_type [String] 包类型 ("ipa" | "apk" | "zip")
1176
+ def save_jps_build_config(working_directory:, app_info_obj:, workflow_info:, package_type:)
1177
+ return if working_directory.nil? || app_info_obj.nil? || workflow_info.nil? || package_type.nil?
1178
+
1179
+ # 1. 确定配置文件路径
1180
+ config_file = determine_config_file_path(working_directory)
1181
+
1182
+ # 2. 确定 workflow_key
1183
+ workflow_key = case package_type
1184
+ when 'ipa' then 'ipa_workflow'
1185
+ when 'apk' then 'apk_workflow'
1186
+ when 'zip' then 'webgl_workflow'
1187
+ else raise Informative, "不支持的 package_type: #{package_type}"
1188
+ end
1189
+
1190
+ # 3. 准备新的项目基础信息
1191
+ new_project_id = app_info_obj["id"]
1192
+ new_project_name = app_info_obj["projectName"]
1193
+ new_project_scheme = app_info_obj["scheme"] ||
1194
+ app_info_obj["projectName"].to_s.downcase.strip.gsub(/[\s\-_]/, '')
1195
+
1196
+ # 4. 准备新的工作流信息
1197
+ new_workflow_data = {
1198
+ "workflow_id" => workflow_info[:workflow_id],
1199
+ "tab_name" => workflow_info[:tab_name],
1200
+ "package_type" => package_type,
1201
+ "package_name" => workflow_info[:package_name],
1202
+ "manage_type" => workflow_info[:manage_type] || ""
1203
+ }
1204
+
1205
+ # 5. 配置文件不存在 → 创建新配置
1206
+ if !File.exist?(config_file)
1207
+ create_new_config(config_file, new_project_id, new_project_name,
1208
+ new_project_scheme, workflow_key, new_workflow_data)
1209
+ return
1210
+ end
1211
+
1212
+ # 6. 读取现有配置
1213
+ begin
1214
+ existing_config = JSON.parse(File.read(config_file))
1215
+ rescue => e
1216
+ puts "[PgyerHelper] 读取配置文件失败: #{e.message},将创建新配置"
1217
+ create_new_config(config_file, new_project_id, new_project_name,
1218
+ new_project_scheme, workflow_key, new_workflow_data)
1219
+ return
1220
+ end
1221
+
1222
+ # 7. Project ID 变更 → 重写整个配置
1223
+ if existing_config['project_id'].nil? ||
1224
+ existing_config['project_id'].to_s.empty? ||
1225
+ existing_config['project_id'] != new_project_id
1226
+
1227
+ puts "项目ID变更,重新写入完整配置"
1228
+ create_new_config(config_file, new_project_id, new_project_name,
1229
+ new_project_scheme, workflow_key, new_workflow_data)
1230
+ return
1231
+ end
1232
+
1233
+ # 8. Project ID 不变,检查工作流是否需要更新
1234
+ existing_workflow = existing_config[workflow_key]
1235
+ existing_workflow_id = existing_workflow&.dig('workflow_id')
1236
+
1237
+ if existing_workflow_id.nil? ||
1238
+ existing_workflow_id.to_s.empty? ||
1239
+ existing_workflow_id != workflow_info[:workflow_id]
1240
+
1241
+ # Workflow ID 变更 → 仅更新对应的 workflow
1242
+ puts "#{workflow_key} 变更,更新工作流配置"
1243
+ existing_config[workflow_key] = new_workflow_data
1244
+ File.write(config_file, JSON.pretty_generate(existing_config))
1245
+ puts "已更新 #{workflow_key}"
1246
+ return
1247
+ end
1248
+
1249
+ # 9. Project ID 和 Workflow ID 都不变 → 不修改
1250
+ puts "配置未变更,跳过保存" if ENV['DEBUG']
942
1251
  end
943
1252
  end
944
1253