pindo 5.6.6 → 5.7.0

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,161 @@ 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
+ cli = HighLine.new
184
+ workflow_choices = display_workflows.map do |w|
185
+ "#{w['tabName'] || w['tab_name']} (ID: #{w['id'] || w['workflow_id']})"
186
+ end
187
+
188
+ selected = cli.choose do |menu|
189
+ menu.prompt = "请选择 #{type_desc} 工作流:"
190
+ menu.choices(*workflow_choices)
191
+ end
192
+
193
+ # 4. 提取选择的工作流
194
+ selected_index = workflow_choices.index(selected)
195
+ workflow = display_workflows[selected_index]
196
+
197
+ # 5. 返回标准化的工作流信息
198
+ {
199
+ workflow_id: workflow['id'] || workflow['workflow_id'],
200
+ tab_name: workflow['tabName'] || workflow['tab_name'],
201
+ package_name: workflow['packageName'] || workflow['package_name'] || workflow['tabName'] || workflow['tab_name'],
202
+ package_type: package_type,
203
+ manage_type: workflow['manageType'] || workflow['manage_type'] || ""
204
+ }
205
+ end
206
+
207
+ def prepare_upload(working_directory:nil, proj_name:nil, package_type:nil)
54
208
  upload_proj_name = proj_name
55
209
  if upload_proj_name.nil? || upload_proj_name.empty?
56
210
  upload_proj_name = @proj_name
@@ -64,6 +218,40 @@ module Pindo
64
218
  puts
65
219
  end
66
220
 
221
+ # 【核心优化】尝试从 JPSBuildConfig.json 加载配置
222
+ if package_type
223
+ config_result = try_load_jps_build_config(
224
+ working_directory: working_directory,
225
+ package_type: package_type
226
+ )
227
+
228
+ if config_result[:valid]
229
+ # 配置有效,直接使用
230
+ puts "\n使用 JPSBuildConfig.json 配置:"
231
+ puts " 项目: #{config_result[:project_name]}"
232
+ puts " 工作流: #{config_result[:workflow_info][:tab_name]}"
233
+
234
+ # 确保已登录
235
+ unless login
236
+ raise Informative, "请先登录JPS网站"
237
+ end
238
+
239
+ # 获取完整的 app_info_obj
240
+ app_info_obj = find_app_info_with_obj_list(
241
+ proj_name: config_result[:project_name]
242
+ )
243
+
244
+ if app_info_obj
245
+ @proj_name = config_result[:project_name]
246
+ return app_info_obj, config_result[:workflow_info]
247
+ else
248
+ puts "配置的项目不存在,将重新选择"
249
+ end
250
+ end
251
+ end
252
+
253
+ # ===== 配置无效或不存在,进入用户选择流程 =====
254
+
67
255
  app_info_obj = nil
68
256
  if login
69
257
 
@@ -78,7 +266,28 @@ module Pindo
78
266
  context = Pindo::PindoContext.instance
79
267
  context.set_selection(Pindo::PindoContext::SelectionKey::PROJECT_NAME, upload_proj_name)
80
268
  end
81
- return app_info_obj
269
+
270
+ # 如果提供了 package_type,选择工作流并保存配置
271
+ if package_type
272
+ workflow_info = select_workflow_for_project(
273
+ project_id: app_info_obj["id"],
274
+ package_type: package_type,
275
+ working_directory: working_directory
276
+ )
277
+
278
+ save_jps_build_config(
279
+ working_directory: working_directory,
280
+ app_info_obj: app_info_obj,
281
+ workflow_info: workflow_info,
282
+ package_type: package_type
283
+ )
284
+
285
+ @proj_name = upload_proj_name
286
+ return app_info_obj, workflow_info
287
+ else
288
+ @proj_name = upload_proj_name
289
+ return app_info_obj
290
+ end
82
291
  end
83
292
 
84
293
  proj_name_array = []
@@ -137,7 +346,7 @@ module Pindo
137
346
  # 直接使用缓存的选择,跳过后续选择逻辑
138
347
  end
139
348
 
140
-
349
+
141
350
  end
142
351
 
143
352
  # 只有在没有缓存或缓存无效,且没有环境变量时才显示选择菜单
@@ -178,8 +387,24 @@ module Pindo
178
387
  # 记录上次上传的项目
179
388
  PindoUserLocalConfig.instance.write_last_work_project(proj_name:app_info_obj["projectName"])
180
389
 
181
- # 保存项目信息到 JPSBuildConfig.json
182
- save_jps_build_config(working_directory: working_directory, app_info_obj: app_info_obj)
390
+ # 如果提供了 package_type,选择工作流并保存配置
391
+ if package_type
392
+ workflow_info = select_workflow_for_project(
393
+ project_id: app_info_obj["id"],
394
+ package_type: package_type,
395
+ working_directory: working_directory
396
+ )
397
+
398
+ save_jps_build_config(
399
+ working_directory: working_directory,
400
+ app_info_obj: app_info_obj,
401
+ workflow_info: workflow_info,
402
+ package_type: package_type
403
+ )
404
+
405
+ @proj_name = upload_proj_name
406
+ return app_info_obj, workflow_info
407
+ end
183
408
  end
184
409
 
185
410
 
@@ -869,76 +1094,145 @@ module Pindo
869
1094
 
870
1095
  private
871
1096
 
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
1097
+ # 确定 JPSBuildConfig.json 文件路径
1098
+ # Unity 工程:仓库根目录/ProjectSettings/JPSBuildConfig.json
1099
+ # iOS/Android 工程:仓库根目录/JPSBuildConfig.json
1100
+ #
1101
+ # @param working_directory [String] 工作目录
1102
+ # @return [String] 配置文件完整路径
1103
+ def determine_config_file_path(working_directory)
1104
+ # 获取 Git 仓库根目录
1105
+ repo_root_dir = nil
1106
+ if is_git_directory?(local_repo_dir: working_directory)
1107
+ repo_root_dir = git_root_directory(local_repo_dir: working_directory)
1108
+ end
1109
+ repo_root_dir ||= working_directory
1110
+
1111
+ # 判断工程类型
1112
+ if File.directory?(File.join(repo_root_dir, 'ProjectSettings'))
1113
+ File.join(repo_root_dir, 'ProjectSettings', 'JPSBuildConfig.json')
1114
+ else
1115
+ File.join(repo_root_dir, 'JPSBuildConfig.json')
1116
+ end
1117
+ end
902
1118
 
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
- }
1119
+ # 验证工作流配置是否有效
1120
+ #
1121
+ # @param workflow [Hash] 工作流配置对象
1122
+ # @return [Boolean] 是否有效
1123
+ def workflow_valid?(workflow)
1124
+ return false if workflow.nil?
1125
+ return false if workflow['workflow_id'].nil? || workflow['workflow_id'].to_s.empty?
1126
+ return false if workflow['package_name'].nil? || workflow['package_name'].empty?
1127
+ true
1128
+ end
909
1129
 
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
1130
+ # 创建新的 JPSBuildConfig.json 配置文件
1131
+ #
1132
+ # @param config_file [String] 配置文件路径
1133
+ # @param project_id [String] 项目ID
1134
+ # @param project_name [String] 项目名称
1135
+ # @param project_scheme [String] 项目scheme
1136
+ # @param workflow_key [String] 工作流键名
1137
+ # @param workflow_data [Hash] 工作流数据
1138
+ def create_new_config(config_file, project_id, project_name, project_scheme, workflow_key, workflow_data)
1139
+ new_config = {
1140
+ "project_name" => project_name,
1141
+ "project_scheme" => project_scheme,
1142
+ "project_id" => project_id,
1143
+ workflow_key => workflow_data
1144
+ }
1145
+
1146
+ # 确保目录存在
1147
+ config_dir = File.dirname(config_file)
1148
+ FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
1149
+
1150
+ File.write(config_file, JSON.pretty_generate(new_config))
1151
+ puts "已创建 JPSBuildConfig.json"
1152
+ end
933
1153
 
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
1154
+ # 保存项目信息到 JPSBuildConfig.json
1155
+ # 支持新的工作流格式,根据 project_id 和 workflow_id 判断是否需要更新
1156
+ #
1157
+ # @param working_directory [String] 工作目录
1158
+ # @param app_info_obj [Hash] 项目信息对象
1159
+ # @param workflow_info [Hash] 工作流信息
1160
+ # @param package_type [String] 包类型 ("ipa" | "apk" | "zip")
1161
+ def save_jps_build_config(working_directory:, app_info_obj:, workflow_info:, package_type:)
1162
+ return if working_directory.nil? || app_info_obj.nil? || workflow_info.nil? || package_type.nil?
1163
+
1164
+ # 1. 确定配置文件路径
1165
+ config_file = determine_config_file_path(working_directory)
1166
+
1167
+ # 2. 确定 workflow_key
1168
+ workflow_key = case package_type
1169
+ when 'ipa' then 'ipa_workflow'
1170
+ when 'apk' then 'apk_workflow'
1171
+ when 'zip' then 'webgl_workflow'
1172
+ else raise Informative, "不支持的 package_type: #{package_type}"
1173
+ end
1174
+
1175
+ # 3. 准备新的项目基础信息
1176
+ new_project_id = app_info_obj["id"]
1177
+ new_project_name = app_info_obj["projectName"]
1178
+ new_project_scheme = app_info_obj["scheme"] ||
1179
+ app_info_obj["projectName"].to_s.downcase.strip.gsub(/[\s\-_]/, '')
1180
+
1181
+ # 4. 准备新的工作流信息
1182
+ new_workflow_data = {
1183
+ "workflow_id" => workflow_info[:workflow_id],
1184
+ "tab_name" => workflow_info[:tab_name],
1185
+ "package_type" => package_type,
1186
+ "package_name" => workflow_info[:package_name],
1187
+ "manage_type" => workflow_info[:manage_type] || ""
1188
+ }
1189
+
1190
+ # 5. 配置文件不存在 → 创建新配置
1191
+ if !File.exist?(config_file)
1192
+ create_new_config(config_file, new_project_id, new_project_name,
1193
+ new_project_scheme, workflow_key, new_workflow_data)
1194
+ return
1195
+ end
1196
+
1197
+ # 6. 读取现有配置
1198
+ begin
1199
+ existing_config = JSON.parse(File.read(config_file))
1200
+ rescue => e
1201
+ puts "[PgyerHelper] 读取配置文件失败: #{e.message},将创建新配置"
1202
+ create_new_config(config_file, new_project_id, new_project_name,
1203
+ new_project_scheme, workflow_key, new_workflow_data)
1204
+ return
1205
+ end
1206
+
1207
+ # 7. Project ID 变更 → 重写整个配置
1208
+ if existing_config['project_id'].nil? ||
1209
+ existing_config['project_id'].to_s.empty? ||
1210
+ existing_config['project_id'] != new_project_id
1211
+
1212
+ puts "项目ID变更,重新写入完整配置"
1213
+ create_new_config(config_file, new_project_id, new_project_name,
1214
+ new_project_scheme, workflow_key, new_workflow_data)
1215
+ return
1216
+ end
1217
+
1218
+ # 8. Project ID 不变,检查工作流是否需要更新
1219
+ existing_workflow = existing_config[workflow_key]
1220
+ existing_workflow_id = existing_workflow&.dig('workflow_id')
1221
+
1222
+ if existing_workflow_id.nil? ||
1223
+ existing_workflow_id.to_s.empty? ||
1224
+ existing_workflow_id != workflow_info[:workflow_id]
1225
+
1226
+ # Workflow ID 变更 → 仅更新对应的 workflow
1227
+ puts "#{workflow_key} 变更,更新工作流配置"
1228
+ existing_config[workflow_key] = new_workflow_data
1229
+ File.write(config_file, JSON.pretty_generate(existing_config))
1230
+ puts "已更新 #{workflow_key}"
1231
+ return
1232
+ end
1233
+
1234
+ # 9. Project ID 和 Workflow ID 都不变 → 不修改
1235
+ puts "配置未变更,跳过保存" if ENV['DEBUG']
942
1236
  end
943
1237
  end
944
1238