pindo 5.21.5 → 5.22.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4ccbec1218f28dfa10b000e89759db3e8167c0ea598637a0f8436a557ab14bc
4
- data.tar.gz: 737550673bf62315c4ab30652496a82bcda55001991af18c2c5da1b019b9cf83
3
+ metadata.gz: 683bf46a8095e20c20a6fdc339b99a6ae83337dc58431b67fb6b1d00263e42a8
4
+ data.tar.gz: 291caf87a1647dc8af05b26fbbfa94cd588385177869e43ec5983db3e90233f8
5
5
  SHA512:
6
- metadata.gz: 52e831191efa0d0092fdd2333658ece13ff82f004255d5dfe55b5f75a295b5d46356fc56956323b8b526bd6dfdda5fa10b15e5e144e971fb70bd629bb5268e57
7
- data.tar.gz: 6fa3ad96ffe7951bcec719e8c9a3ffad2540e807c4d451ba6a3dc16fbe59e85a2520f874d72ae589b72ebd47828df71c0482dfcedbdb310e58d6c99f49e24efb
6
+ metadata.gz: 1d0176e844e360885e6bf90ebd9024b1982ee7c519ce6d01f3fe994e085110b12107f34271d379283ca4ca27165e0cb1d2187a1daf4cfcbc2ce65b3445688786
7
+ data.tar.gz: 5a94ae4f340b1cc924a0e85e1c0a57272014d58c7498d248f91ab5d3d6bba399f2d3521c1d6d90dec2d5b5fdb578d4d05b1ce02b3bf55fd7ecc3eb90837a4e64
@@ -10,31 +10,31 @@ module Pindo
10
10
  class Jps < Command
11
11
  class Config < Jps
12
12
 
13
- self.summary = '配置当前工程与 JPS 项目及工作流的绑定'
13
+ self.summary = '配置当前工程与 JPS 项目及安装包工具 / Git 工作流的绑定'
14
14
 
15
15
  self.description = <<-DESC
16
- 将当前工程绑定到 JPS 项目及其一个或多个工作流,并写入 JPSBuildConfig.json。
16
+ 将当前工程绑定到 JPS 项目的安装包工具(ipa/apk/zip/mac/exe)和 Git 工作流,并写入 JPSBuildConfig.json。
17
17
 
18
18
  只做配置绑定,不编译、不上传,产出与 pindo ios/and autobuild 一致的配置文件。
19
19
 
20
20
  支持功能:
21
21
 
22
- * 选一次 JPS 项目,一次配置多个工作流类型
22
+ * 选一次 JPS 项目,一次配置多个类型
23
23
  * 交互多选菜单,或用 --type 非交互指定类型
24
24
  * --proj 直接指定项目名,跳过项目选择菜单(非交互场景)
25
- * --workflow_name 按名称指定工作流,用于同类型多工作流消歧(非交互场景)
25
+ * --workflow_name 按名称指定 Git 工作流或安装包工具,用于同类型多个时消歧(非交互场景)
26
26
  * 支持类型: ipa、apk、zip(WebGL)、mac(macOS)、exe(Windows)、git
27
- * --clean 清除已绑定的工作流或整个配置文件
27
+ * --clean 清除已绑定的工具 / 工作流或整个配置文件
28
28
 
29
29
  使用示例:
30
30
 
31
- $ pindo jps config # 交互选择项目和工作流类型
31
+ $ pindo jps config # 交互选择项目和绑定类型
32
32
 
33
33
  $ pindo jps config --type=ipa,apk,git # 非交互配置指定类型(项目仍需交互选)
34
34
 
35
35
  $ pindo jps config --proj=MyApp --type=ipa,git # 全程非交互(agent/CI 适用)
36
36
 
37
- $ pindo jps config --clean # 交互清除已绑定的工作流
37
+ $ pindo jps config --clean # 交互清除已绑定的工具 / 工作流
38
38
 
39
39
  $ pindo jps config --clean --type=ipa # 清除指定类型的绑定
40
40
 
@@ -96,7 +96,7 @@ module Pindo
96
96
 
97
97
  selected_types = resolve_selected_types
98
98
  if selected_types.empty?
99
- puts "未选择任何工作流类型,已取消操作"
99
+ puts "未选择任何绑定类型,已取消操作"
100
100
  return
101
101
  end
102
102
 
@@ -110,9 +110,9 @@ module Pindo
110
110
  raise Informative, "未找到 JPS 项目,请检查项目名称或配置文件"
111
111
  end
112
112
 
113
- # Phase 1: 先选定所有类型的工作流(任一歧义/缺失即报错,此时尚未写任何文件)
113
+ # Phase 1: 先选定所有类型的工具 / 工作流(任一歧义/缺失即报错,此时尚未写任何文件)
114
114
  selections = selected_types.map do |type|
115
- puts "\n选择工作流: #{type[:name]}"
115
+ puts "\n选择#{binding_label(type)}: #{type[:name]}"
116
116
  workflow_info = pgyer_helper.select_workflow_for_project(
117
117
  project_id: app_info_obj["id"],
118
118
  package_type: type[:package_type],
@@ -169,7 +169,7 @@ module Pindo
169
169
  end
170
170
 
171
171
  def select_types_interactive
172
- puts "\n请选择要配置的工作流类型(可多选):"
172
+ puts "\n请选择要配置的绑定类型(安装包为工具,git 为工作流,可多选):"
173
173
  puts DIVIDER
174
174
  WORKFLOW_TYPES.each_with_index do |type, index|
175
175
  puts " #{index + 1}. #{type[:name]}"
@@ -343,11 +343,20 @@ module Pindo
343
343
  WORKFLOW_TYPES.each do |type|
344
344
  workflow = config[type[:workflow_key]]
345
345
  next if workflow.nil?
346
- puts " #{type[:name]}: #{workflow['tab_name']} (ID: #{workflow['workflow_id']})"
346
+ if type[:manage_type] == 'git'
347
+ puts " #{type[:name]}: 工作流: #{workflow['tab_name']} (ID: #{workflow['workflow_id']})"
348
+ else
349
+ puts " #{type[:name]}: 工具: #{workflow['tab_name']} (packageId: #{workflow['package_id']})"
350
+ end
347
351
  end
348
352
  puts DIVIDER
349
353
  end
350
354
 
355
+ # 安装包类型绑定的是「工具」,Git 绑定的是「工作流」
356
+ def binding_label(type)
357
+ type[:manage_type] == 'git' ? '工作流' : '工具'
358
+ end
359
+
351
360
  end
352
361
  end
353
362
  end
@@ -156,6 +156,7 @@ module Pindo
156
156
  # 7. 所有验证通过
157
157
  result[:valid] = true
158
158
  result[:workflow_info] = {
159
+ package_id: workflow['package_id'].to_s,
159
160
  workflow_id: workflow['workflow_id'],
160
161
  tab_name: workflow['tab_name'],
161
162
  package_name: workflow['package_name'],
@@ -166,6 +167,7 @@ module Pindo
166
167
  if ENV['PINDO_DEBUG']
167
168
  puts "[PINDO_DEBUG] ✓✓✓ 配置验证通过 (#{workflow_key})"
168
169
  puts "[PINDO_DEBUG] 返回 valid=true,workflow_info:"
170
+ puts " package_id: #{result[:workflow_info][:package_id]}"
169
171
  puts " workflow_id: #{result[:workflow_info][:workflow_id]}"
170
172
  puts " tab_name: #{result[:workflow_info][:tab_name]}"
171
173
  puts " manage_type: #{result[:workflow_info][:manage_type]}"
@@ -174,86 +176,44 @@ module Pindo
174
176
  return result
175
177
  end
176
178
 
177
- # 为项目选择工作流
179
+ # 为项目选择工作流(Git)或安装包工具(ipa/apk/zip/mac/exe)
180
+ #
181
+ # JPS V1.3.94 起,workflow/list 不再返回产物工作流;安装包入口改为项目的 packageTools,
182
+ # 因此非 git 类型一律从 project/detail 的 packageTools 中选择。
178
183
  #
179
184
  # @param project_id [String] 项目ID
180
185
  # @param package_type [String] 包类型
181
186
  # @param working_directory [String] 工作目录
182
- # @param manage_type [String] 管理类型(默认为空,"git" 时筛选 git 工作流)
183
- # @return [Hash] 选择的工作流信息
187
+ # @param manage_type [String] 管理类型(默认为空,"git" 时选择 Git 工作流)
188
+ # @param workflow_name [String, nil] 按名称精确选择(Git 匹配 tabName;工具匹配 name、subtitle 或 name(subtitle))
189
+ # @return [Hash] 选择的工作流/工具信息(symbol key 结构)
184
190
  def select_workflow_for_project(project_id:, package_type:, working_directory:, manage_type: "", workflow_name: nil)
185
- # 1. 从 JPS API 获取可用工作流列表
191
+ if manage_type == "git"
192
+ return select_git_workflow_for_project(project_id: project_id, package_type: package_type, workflow_name: workflow_name)
193
+ end
194
+
195
+ select_package_tool_for_project(project_id: project_id, package_type: package_type, workflow_name: workflow_name)
196
+ end
197
+
198
+ # 选择 Git 仓库工作流(仍通过 workflow/list 获取)
199
+ def select_git_workflow_for_project(project_id:, package_type: nil, workflow_name: nil)
186
200
  Funlog.instance.fancyinfo_start("正在获取可用工作流...")
187
201
 
188
202
  workflow_result = @pgyer_client.get_project_workflows(project_id: project_id)
189
-
190
203
  if workflow_result.nil? || workflow_result['data'].nil?
191
204
  raise Informative, "获取工作流列表失败"
192
205
  end
193
206
 
194
207
  workflows = workflow_result['data']
195
-
196
- if workflows.empty?
197
- raise Informative, "该项目没有可用的工作流"
208
+ type_desc = 'Git 管理类型'
209
+ # 严格筛选 manage_type == "git",不降级到其它类型
210
+ display_workflows = workflows.select do |w|
211
+ (w['manageType'] == 'git') || (w['manage_type'] == 'git')
198
212
  end
199
213
 
200
- puts "package_type #{package_type} manage_type: #{manage_type}"
201
-
202
- # 2. 根据 manage_type 或 package_type 过滤工作流
203
- if manage_type == "git"
204
- # manage_type 为 git 时,只筛选 manage_type == "git" 的工作流,不看 package_type
205
- # 严格筛选,不包括 manage_type 为空或其他类型的工作流
206
- filtered_workflows = workflows.select do |w|
207
- (w['manageType'] == 'git') || (w['manage_type'] == 'git')
208
- end
209
- type_desc = 'Git 管理类型'
210
- # manage_type == "git" 时严格使用筛选结果,不降级
211
- display_workflows = filtered_workflows
212
- else
213
- # 按 package_type 过滤工作流
214
- filtered_workflows = workflows.select do |w|
215
- # 如果工作流有 packageType 字段,进行过滤
216
- if w['packageType']
217
- case package_type
218
- when 'ipa'
219
- w['packageType'] == 'ipa'
220
- when 'apk'
221
- w['packageType'] == 'apk'
222
- when 'zip'
223
- w['packageType'] == 'zip'
224
- when 'mac'
225
- w['packageType'] == 'mac'
226
- when 'exe'
227
- w['packageType'] == 'exe'
228
- else
229
- true
230
- end
231
- else
232
- # 如果没有 packageType 字段,显示所有工作流
233
- true
234
- end
235
- end
236
-
237
- type_desc = case package_type
238
- when 'ipa' then 'iOS IPA'
239
- when 'apk' then 'Android APK'
240
- when 'zip' then 'WebGL'
241
- when 'mac' then 'macOS App'
242
- when 'exe' then 'Windows EXE'
243
- else package_type
244
- end
245
-
246
- # 非 git 模式:过滤结果即为候选。若为空则交由下方“没有找到匹配工作流”报错,
247
- # 不再回退到全部工作流——否则会把其它类型的工作流误当作请求类型(写错 workflow_key)
248
- display_workflows = filtered_workflows
249
- end
250
-
251
- # 过滤后再报数:只报总数会让人以为剩下的工作流被漏掉了,
252
- # 实际是按 manage_type / package_type 筛掉的,两个数字一起给出才不歧义
253
214
  Funlog.instance.fancyinfo_success("获取工作流列表成功,共 #{workflows.size} 个工作流,其中 #{type_desc} #{display_workflows.size} 个")
254
215
 
255
- # 2.5 若指定了 workflow_name,按名称精确匹配(忽略大小写与首尾空格)确定性选择。
256
- # 只接受完整精确匹配,不做子串模糊匹配,避免 "Production" 误命中 "Legacy Production Upload"。
216
+ # 指定 workflow_name 时只接受完整精确匹配(忽略大小写与首尾空格),不做子串模糊匹配
257
217
  if workflow_name && !workflow_name.to_s.strip.empty? && !display_workflows.empty?
258
218
  key_name = workflow_name.to_s.strip.downcase
259
219
  name_matched = display_workflows.select do |w|
@@ -266,44 +226,23 @@ module Pindo
266
226
  display_workflows = name_matched
267
227
  end
268
228
 
269
- # 3. 根据匹配数量决定选择方式
270
- workflow = nil
271
-
272
229
  if display_workflows.empty?
273
- # 情况1: 没有匹配的工作流
274
- error_msg = if manage_type == "git"
275
- "没有找到匹配 #{type_desc} 的工作流,请在 JPS 后台创建 Git 类型工作流(项目ID: #{project_id})"
276
- else
277
- "没有找到匹配 #{type_desc} 的工作流,请在 JPS 后台配置(项目ID: #{project_id})"
278
- end
279
- raise Informative, error_msg
280
-
281
- elsif display_workflows.size == 1
282
- # 情况2: 只有1个匹配的工作流,自动选择
283
- workflow = display_workflows.first
284
- workflow_name = workflow['tabName'] || workflow['tab_name']
285
- Funlog.instance.fancyinfo_success("自动选择唯一匹配的工作流: #{workflow_name}")
230
+ raise Informative, "没有找到匹配 #{type_desc} 的工作流,请在 JPS 后台创建 Git 类型工作流(项目ID: #{project_id})"
231
+ end
286
232
 
233
+ workflow = if display_workflows.size == 1
234
+ Funlog.instance.fancyinfo_success("自动选择唯一匹配的工作流: #{display_workflows.first['tabName'] || display_workflows.first['tab_name']}")
235
+ display_workflows.first
287
236
  else
288
- # 情况3: 有多个匹配的工作流,让用户选择
289
- cli = HighLine.new
290
- workflow_choices = display_workflows.map do |w|
237
+ choices = display_workflows.map do |w|
291
238
  "#{w['tabName'] || w['tab_name']} (ID: #{w['id'] || w['workflow_id']})"
292
239
  end
293
-
294
- selected = cli.choose do |menu|
295
- menu.prompt = "请选择 #{type_desc} 工作流:"
296
- menu.choices(*workflow_choices)
297
- end
298
-
299
- # 提取选择的工作流
300
- selected_index = workflow_choices.index(selected)
301
- workflow = display_workflows[selected_index]
240
+ choose_from_menu(prompt: "请选择 #{type_desc} 工作流:", choices: choices, items: display_workflows)
302
241
  end
303
242
 
304
- # 4. 返回标准化的工作流信息
305
243
  {
306
244
  workflow_id: workflow['id'] || workflow['workflow_id'],
245
+ package_id: "",
307
246
  tab_name: workflow['tabName'] || workflow['tab_name'],
308
247
  package_name: workflow['packageName'] || workflow['package_name'] || workflow['tabName'] || workflow['tab_name'],
309
248
  package_type: package_type,
@@ -311,96 +250,73 @@ module Pindo
311
250
  }
312
251
  end
313
252
 
314
- # AdHoc 专用工作流选择
315
- # 筛选条件:
316
- # 1. package_type 匹配
317
- # 2. tab_name 必须包含 "提交包"
318
- #
319
- # @param project_id [String] 项目ID
320
- # @param package_type [String] 包类型(ipa/apk/zip/app)
321
- # @return [Hash] 选择的工作流信息
322
- def select_workflow_for_adhoc(project_id:, package_type:)
323
- # 1. 从 JPS API 获取可用工作流列表
324
- workflow_result = @pgyer_client.get_project_workflows(project_id: project_id)
253
+ # 选择安装包工具(来自 project/detail 的 packageTools)
254
+ def select_package_tool_for_project(project_id:, package_type:, workflow_name: nil)
255
+ Funlog.instance.fancyinfo_start("正在获取项目工具列表...")
325
256
 
326
- if workflow_result.nil? || workflow_result['data'].nil?
327
- raise Informative, "获取工作流列表失败"
328
- end
257
+ tools = get_project_package_tools(project_id: project_id, package_type: package_type)
258
+ type_desc = package_type_desc(package_type)
329
259
 
330
- workflows = workflow_result['data']
260
+ Funlog.instance.fancyinfo_success("获取工具列表成功,#{type_desc} 工具共 #{tools.size} 个")
331
261
 
332
- if workflows.empty?
333
- raise Informative, "该项目没有可用的工作流"
262
+ if workflow_name && !workflow_name.to_s.strip.empty? && !tools.empty?
263
+ tools = match_package_tools_by_name(tools: tools, name: workflow_name, type_desc: type_desc)
334
264
  end
335
265
 
336
- # 2. 第一层过滤:根据 package_type
337
- filtered_by_type = workflows.select do |w|
338
- if w['packageType']
339
- case package_type
340
- when 'ipa'
341
- w['packageType'] == 'ipa'
342
- when 'apk'
343
- w['packageType'] == 'apk'
344
- when 'zip'
345
- w['packageType'] == 'zip'
346
- when 'mac'
347
- w['packageType'] == 'mac'
348
- else
349
- true
350
- end
351
- else
352
- # 如果没有 packageType 字段,保留(后续通过名称筛选)
353
- true
354
- end
355
- end
356
-
357
- # 3. 第二层过滤:tab_name 必须包含 "提交包"
358
- adhoc_workflows = filtered_by_type.select do |w|
359
- tab_name = w['tabName'] || w['tab_name'] || ''
360
- tab_name.include?('提交包')
266
+ if tools.empty?
267
+ raise Informative, "项目中没有可用的 #{type_desc} 工具,请到 JPS 项目的「工具」页新建(项目ID: #{project_id})"
361
268
  end
362
269
 
363
- if adhoc_workflows.empty?
364
- raise Informative, "未找到符合条件的 AdHoc 工作流\n" \
365
- "要求:package_type=#{package_type} 且 tab_name 包含 '提交包'\n" \
366
- "请检查 JPS 工作流配置"
270
+ tool = if tools.size == 1
271
+ Funlog.instance.fancyinfo_success("自动选择唯一匹配的工具: #{package_tool_display_name(tools.first)}")
272
+ tools.first
273
+ else
274
+ choices = tools.map { |t| "#{package_tool_display_name(t)} (版本数: #{t['packageCount'] || 0})" }
275
+ choose_from_menu(prompt: "请选择 #{type_desc} 工具:", choices: choices, items: tools)
367
276
  end
368
277
 
369
- # 4. 根据匹配数量决定选择方式
370
- workflow = nil
278
+ package_tool_to_workflow_info(tool, package_type)
279
+ end
371
280
 
372
- if adhoc_workflows.size == 1
373
- # 只有 1 个匹配,自动选择
374
- workflow = adhoc_workflows.first
375
- else
376
- # 有多个匹配,让用户选择
377
- require 'highline/import'
378
- cli = HighLine.new
281
+ # 按名称精确匹配工具(忽略大小写与首尾空格):显示名 name(subtitle)、subtitle(原 tab 名)、name 任一相等
282
+ def match_package_tools_by_name(tools:, name:, type_desc:)
283
+ key = name.to_s.strip.downcase
284
+ matched = tools.select do |t|
285
+ [package_tool_display_name(t), t["subtitle"], t["name"]].any? { |v| v.to_s.strip.downcase == key }
286
+ end
287
+ if matched.empty?
288
+ available = tools.map { |t| package_tool_display_name(t) }.join('、')
289
+ raise Informative, "未找到名称精确匹配 \"#{name}\" 的 #{type_desc} 工具,可选: #{available}"
290
+ end
291
+ matched
292
+ end
379
293
 
380
- workflow_choices = adhoc_workflows.map do |w|
381
- tab_name = w['tabName'] || w['tab_name']
382
- workflow_id = w['id'] || w['workflow_id']
383
- "#{tab_name} (ID: #{workflow_id})"
384
- end
294
+ # AdHoc 专用工具选择
295
+ # 筛选条件:packageType 匹配,且副标题(原 tab 名)或名称包含 "提交包"
296
+ #
297
+ # @param project_id [String] 项目ID
298
+ # @param package_type [String] 包类型(ipa/apk/zip/mac)
299
+ # @return [Hash] 选择的工具信息
300
+ def select_workflow_for_adhoc(project_id:, package_type:)
301
+ tools = get_project_package_tools(project_id: project_id, package_type: package_type)
302
+ adhoc_tools = tools.select do |t|
303
+ t["subtitle"].to_s.include?('提交包') || t["name"].to_s.include?('提交包')
304
+ end
385
305
 
386
- selected = cli.choose do |menu|
387
- menu.prompt = "请选择 AdHoc 工作流:"
388
- menu.choices(*workflow_choices)
389
- end
306
+ if adhoc_tools.empty?
307
+ raise Informative, "未找到符合条件的 AdHoc 工具\n" \
308
+ "要求:packageType=#{package_type} 且副标题包含 '提交包'\n" \
309
+ "请到 JPS 项目的「工具」页检查"
310
+ end
390
311
 
391
- # 提取选择的工作流
392
- selected_index = workflow_choices.index(selected)
393
- workflow = adhoc_workflows[selected_index]
312
+ tool = if adhoc_tools.size == 1
313
+ adhoc_tools.first
314
+ else
315
+ choices = adhoc_tools.map { |t| package_tool_display_name(t) }
316
+ choose_from_menu(prompt: "请选择 AdHoc 工具:", choices: choices, items: adhoc_tools)
394
317
  end
395
318
 
396
- # 5. 返回标准化的工作流信息
397
- {
398
- workflow_id: workflow['id'] || workflow['workflow_id'],
399
- tab_name: workflow['tabName'] || workflow['tab_name'],
400
- package_type: workflow['packageType'] || package_type,
401
- package_name: workflow['packageName'] || workflow['package_name'] || '',
402
- manage_type: workflow['manageType'] || workflow['manage_type'] || ''
403
- }
319
+ package_tool_to_workflow_info(tool, package_type)
404
320
  end
405
321
 
406
322
  # 获取 AdHoc 上传信息
@@ -463,19 +379,19 @@ module Pindo
463
379
  raise Informative, "未找到项目: #{upload_proj_name},请检查项目名称是否正确"
464
380
  end
465
381
 
466
- # 4. 获取 AdHoc 工作流(调用专用函数)
382
+ # 4. 获取 AdHoc 工具(调用专用函数)
467
383
  workflow_info = select_workflow_for_adhoc(
468
384
  project_id: app_info_obj["id"],
469
385
  package_type: package_type
470
386
  )
471
387
 
472
388
  unless workflow_info
473
- raise Informative, "未能获取 AdHoc 工作流信息"
389
+ raise Informative, "未能获取 AdHoc 工具信息"
474
390
  end
475
391
 
476
- # 5. 输出最终选择的项目和工作流
392
+ # 5. 输出最终选择的项目和工具
477
393
  puts "\n✓ 项目: #{app_info_obj['projectName']}"
478
- puts "✓ 工作流: #{workflow_info[:tab_name]}"
394
+ puts "✓ 工具: #{workflow_info[:tab_name]}"
479
395
 
480
396
  # 6. 保存项目名称到实例变量
481
397
  @proj_name = upload_proj_name
@@ -532,9 +448,10 @@ module Pindo
532
448
  puts "[PINDO_DEBUG] 配置加载结果: valid=#{config_result[:valid]}" if ENV['PINDO_DEBUG']
533
449
 
534
450
  if config_result[:valid]
451
+ is_git_config = manage_type.to_s == "git"
535
452
  puts "\n使用 JPSBuildConfig.json 配置:"
536
453
  puts " 项目: #{config_result[:project_name]}"
537
- puts " 工作流: #{config_result[:workflow_info][:tab_name]}"
454
+ puts " #{is_git_config ? '工作流' : '工具'}: #{config_result[:workflow_info][:tab_name]}"
538
455
 
539
456
  if ENV['PINDO_DEBUG']
540
457
  puts "[PINDO_DEBUG] 配置详细信息:"
@@ -547,6 +464,27 @@ module Pindo
547
464
  raise Informative, "JPS 未登录或登录已失效,请执行 pindo jps login 重新登录"
548
465
  end
549
466
 
467
+ # 安装包配置只有 workflow_id(旧格式)时,换算成 package_id 并写回;
468
+ # 找不到对应工具则落到下方交互重选流程
469
+ unless is_git_config
470
+ migrated = migrate_legacy_workflow_info!(
471
+ config_path: config_path,
472
+ project_id: config_result[:project_id],
473
+ package_type: package_type,
474
+ workflow_info: config_result[:workflow_info]
475
+ )
476
+ if migrated.nil?
477
+ puts "旧工作流(ID: #{config_result[:workflow_info][:workflow_id]})未找到对应工具,将重新选择"
478
+ config_result[:valid] = false
479
+ # 项目已知,只需重选工具:走下方 --proj 非交互项目解析分支
480
+ proj_name = config_result[:project_name]
481
+ else
482
+ config_result[:workflow_info] = migrated
483
+ end
484
+ end
485
+ end
486
+
487
+ if config_result[:valid]
550
488
  puts "[PINDO_DEBUG] 正在查找项目: #{config_result[:project_name]}" if ENV['PINDO_DEBUG']
551
489
  lookup_bundle_id = resolve_lookup_bundle_id(
552
490
  bundle_name: bundle_name,
@@ -740,9 +678,11 @@ module Pindo
740
678
 
741
679
  def start_upload(app_info_obj:nil, ipa_file_upload:nil, description:nil, workflow_info:nil)
742
680
 
743
- # 验证 workflow_info 必传
744
- if workflow_info.nil? || workflow_info[:workflow_id].nil?
745
- raise Informative, "workflow_info 和 workflow_id 是必传参数,请确保调用 prepare_upload 时传入了 package_type"
681
+ # 验证工具信息必传:package_id 优先,旧配置换算不到时才用 workflow_id 兼容
682
+ package_id = workflow_info && workflow_info[:package_id].to_s.strip
683
+ workflow_id = workflow_info && workflow_info[:workflow_id]
684
+ if workflow_info.nil? || (package_id.to_s.empty? && (workflow_id.nil? || workflow_id.to_s.empty?))
685
+ raise Informative, "缺少工具信息(package_id),请确保调用 prepare_upload 时传入了 package_type"
746
686
  end
747
687
 
748
688
  if !ipa_file_upload.nil? &&
@@ -901,13 +841,13 @@ module Pindo
901
841
 
902
842
  puts attachFileUrls
903
843
 
904
- # 准备上传参数(workflowId 必传)
905
- upload_params = {
844
+ upload_params = build_package_upload_params(
906
845
  description: description,
907
- packageUrl: upload_res,
908
- attachFileUrls: attachFileUrls,
909
- workflowId: workflow_info[:workflow_id] # workflowId 必传
910
- }
846
+ package_url: upload_res,
847
+ attach_file_urls: attachFileUrls,
848
+ package_id: package_id,
849
+ workflow_id: workflow_id
850
+ )
911
851
 
912
852
  # 提交包信息到服务器
913
853
  Funlog.instance.fancyinfo_start("正在提交包信息到服务器...")
@@ -926,7 +866,9 @@ module Pindo
926
866
  if result_data && result_data["data"] && result_data["data"]["id"]
927
867
  Funlog.instance.fancyinfo_success("提交包信息完成!")
928
868
  else
929
- error_msg = result_data&.dig('msg') || '未知错误'
869
+ error_msg = result_data&.dig('msg') || result_data&.dig('meta', 'message') || '未知错误'
870
+ hint = package_upload_error_hint(result_data)
871
+ error_msg = "#{error_msg}(#{hint})" if hint
930
872
  Funlog.instance.fancyinfo_error("提交包信息失败: #{error_msg}")
931
873
  raise Informative, "上传失败:未获取到有效的返回数据"
932
874
  end
@@ -945,6 +887,37 @@ module Pindo
945
887
 
946
888
  end
947
889
 
890
+ # 项目包上传错误码 → 处理提示
891
+ PACKAGE_UPLOAD_ERROR_HINTS = {
892
+ 97931 => "未传 packageId,请重新执行 pindo jps config 绑定工具",
893
+ 97992 => "packageId 与 workflowId 冲突,只应传 packageId",
894
+ 97953 => "该工具不是安装包类型,不能走项目包上传"
895
+ }.freeze
896
+
897
+ # 组装项目包上传参数:packageId 与 workflowId 只传一个(同时传且不匹配会报 97992)
898
+ def build_package_upload_params(description:, package_url:, attach_file_urls:, package_id:, workflow_id:)
899
+ params = {
900
+ description: description,
901
+ packageUrl: package_url,
902
+ attachFileUrls: attach_file_urls
903
+ }
904
+ if !package_id.to_s.strip.empty?
905
+ params.merge(packageId: package_id.to_s.strip)
906
+ else
907
+ # 换算不到工具的旧配置:按 workflowId 兼容上传(JPS 长期兼容)
908
+ Funlog.instance.fancyinfo_warning("未绑定工具,按旧工作流ID兼容上传: #{workflow_id}")
909
+ params.merge(workflowId: workflow_id)
910
+ end
911
+ end
912
+
913
+ # 从上传返回体中取错误码提示(码可能在 code 或 meta.code)
914
+ def package_upload_error_hint(result_data)
915
+ return nil unless result_data.is_a?(Hash)
916
+ code = result_data['code'] || result_data.dig('meta', 'code')
917
+ return nil if code.nil?
918
+ PACKAGE_UPLOAD_ERROR_HINTS[code.to_i]
919
+ end
920
+
948
921
  def package_windows_exe_for_upload(exe_file_upload)
949
922
  windows_exe_path = File.expand_path(exe_file_upload)
950
923
  windows_build_dir = File.dirname(windows_exe_path)
@@ -1435,6 +1408,67 @@ module Pindo
1435
1408
  result&.dig("data")
1436
1409
  end
1437
1410
 
1411
+ # 取项目下可上传的安装包工具(project/detail 的 packageTools)
1412
+ # 按 package_type 筛选,去掉已下架(lifecycle=deprecated)和缺 packageId 的,保持接口返回的排序
1413
+ #
1414
+ # @param project_id [String] 项目ID
1415
+ # @param package_type [String] 包类型(ipa/apk/zip/mac/exe)
1416
+ # @return [Array<Hash>] packageTools 原始项
1417
+ def get_project_package_tools(project_id:, package_type:)
1418
+ detail = get_project_detail(project_id: project_id)
1419
+ tools = detail.is_a?(Hash) ? detail["packageTools"] : nil
1420
+ return [] unless tools.is_a?(Array)
1421
+
1422
+ tools.select do |t|
1423
+ t.is_a?(Hash) &&
1424
+ t["packageType"].to_s.downcase == package_type.to_s.downcase &&
1425
+ t["lifecycle"].to_s != "deprecated" &&
1426
+ !t["packageId"].to_s.strip.empty?
1427
+ end
1428
+ end
1429
+
1430
+ # 工具显示名:name(subtitle),与 GoodUnityBuild 写入的 tab_name 保持一致
1431
+ def package_tool_display_name(tool)
1432
+ name = tool["name"].to_s
1433
+ subtitle = tool["subtitle"].to_s
1434
+ subtitle.strip.empty? ? name : "#{name}(#{subtitle})"
1435
+ end
1436
+
1437
+ # 工具 → workflow_info(沿用 symbol key 结构,调用方无需改动)
1438
+ def package_tool_to_workflow_info(tool, package_type)
1439
+ {
1440
+ package_id: tool["packageId"].to_s,
1441
+ workflow_id: tool["workflowId"], # 老工具才有;新工具为 nil,仅用于兼容
1442
+ tab_name: package_tool_display_name(tool),
1443
+ package_name: tool["name"],
1444
+ package_type: package_type,
1445
+ manage_type: "package",
1446
+ subtitle: tool["subtitle"],
1447
+ bundle_id: tool["bundleId"]
1448
+ }
1449
+ end
1450
+
1451
+ # 旧配置(只有 workflow_id、没有 package_id)换算成工具并写回配置文件
1452
+ # 已有 package_id → 原样返回、不发请求;只有 workflow_id → 在 packageTools 里按 workflowId 查找
1453
+ #
1454
+ # @return [Hash, nil] 换算后的 workflow_info;找不到对应工具返回 nil
1455
+ def migrate_legacy_workflow_info!(config_path:, project_id:, package_type:, workflow_info:)
1456
+ return workflow_info unless workflow_info[:package_id].to_s.strip.empty?
1457
+ return nil if workflow_info[:workflow_id].nil? || workflow_info[:workflow_id].to_s.empty?
1458
+
1459
+ tool = get_project_package_tools(project_id: project_id, package_type: package_type)
1460
+ .find { |t| t["workflowId"].to_s == workflow_info[:workflow_id].to_s }
1461
+ return nil if tool.nil?
1462
+
1463
+ new_info = package_tool_to_workflow_info(tool, package_type)
1464
+ config = JSON.parse(File.read(config_path))
1465
+ key = jps_workflow_key(package_type: package_type)
1466
+ config[key] = build_workflow_data(workflow_info: new_info, package_type: package_type)
1467
+ atomic_write(config_path, JSON.pretty_generate(config))
1468
+ puts "已将旧工作流(ID: #{workflow_info[:workflow_id]})换算为工具: #{new_info[:tab_name]}"
1469
+ new_info
1470
+ end
1471
+
1438
1472
  PROJECT_PAGE_SIZE = 100
1439
1473
  # 安全上限:最多翻 200 页(约 2 万项目),防止服务端异常(如忽略 pageNo 恒返满页)导致死循环
1440
1474
  MAX_PROJECT_PAGES = 200
@@ -2210,7 +2244,7 @@ module Pindo
2210
2244
  end
2211
2245
 
2212
2246
  atomic_write(config_file, JSON.pretty_generate(config) + "\n")
2213
- puts "已写入 JPSBuildConfig.json(#{entries.size} 个工作流)"
2247
+ puts "已写入 JPSBuildConfig.json(#{entries.size} 项绑定)"
2214
2248
  config
2215
2249
  end
2216
2250
 
@@ -2252,10 +2286,34 @@ module Pindo
2252
2286
  end
2253
2287
  end
2254
2288
 
2255
- # 构建单个工作流的写入结构
2289
+ # 包类型 → 显示名
2290
+ def package_type_desc(package_type)
2291
+ case package_type
2292
+ when 'ipa' then 'iOS IPA'
2293
+ when 'apk' then 'Android APK'
2294
+ when 'zip' then 'WebGL'
2295
+ when 'mac' then 'macOS App'
2296
+ when 'exe' then 'Windows EXE'
2297
+ else package_type.to_s
2298
+ end
2299
+ end
2300
+
2301
+ # HighLine 菜单选择,返回 items 中与所选 choice 同下标的元素
2302
+ def choose_from_menu(prompt:, choices:, items:)
2303
+ cli = HighLine.new
2304
+ selected = cli.choose do |menu|
2305
+ menu.prompt = prompt
2306
+ menu.choices(*choices)
2307
+ end
2308
+ items[choices.index(selected)]
2309
+ end
2310
+
2311
+ # 构建单个工作流/工具的写入结构(与 GoodUnityBuild 共用的 JPSBuildConfig.json 格式)
2312
+ # 安装包工具:package_id 为上传依据,workflow_id 仅兼容(新工具为 nil);Git 工作流:package_id 为空串
2256
2313
  def build_workflow_data(workflow_info:, package_type:)
2257
2314
  {
2258
2315
  "workflow_id" => workflow_info[:workflow_id],
2316
+ "package_id" => workflow_info[:package_id].to_s,
2259
2317
  "tab_name" => workflow_info[:tab_name],
2260
2318
  "package_type" => package_type,
2261
2319
  "package_name" => workflow_info[:package_name],
@@ -2268,20 +2326,24 @@ module Pindo
2268
2326
  # @param workflow [Hash] 工作流配置对象
2269
2327
  # @return [Boolean] 是否有效
2270
2328
  def workflow_valid?(workflow)
2271
- return false if workflow.nil?
2329
+ return false unless workflow.is_a?(Hash)
2272
2330
 
2273
- # 检查 workflow_id
2274
- return false if workflow['workflow_id'].nil? || workflow['workflow_id'].to_s.empty?
2275
-
2276
- # git 类型的 workflow 不需要 package_name(管理 git commit,不是包文件)
2277
- # 其他类型的 workflow 需要 package_name
2278
2331
  is_git_workflow = workflow['manage_type'] == 'git'
2279
- unless is_git_workflow
2280
- return false if workflow['package_name'].nil? || workflow['package_name'].empty?
2332
+ has_workflow_id = !workflow['workflow_id'].nil? && !workflow['workflow_id'].to_s.empty?
2333
+ has_package_id = !workflow['package_id'].to_s.strip.empty?
2334
+
2335
+ if is_git_workflow
2336
+ # Git 工作流必须有 workflow_id,不需要 package_name
2337
+ return false unless has_workflow_id
2338
+ else
2339
+ # 安装包工具:package_id / workflow_id 有一个即可(只有 workflow_id 的旧配置在 prepare_upload 中换算)
2340
+ return false unless has_package_id || has_workflow_id
2341
+ return false if workflow['package_name'].nil? || workflow['package_name'].to_s.empty?
2281
2342
  end
2282
2343
 
2283
2344
  if ENV['PINDO_DEBUG']
2284
2345
  puts "[PINDO_DEBUG] workflow_valid? 检查通过:"
2346
+ puts " package_id: #{workflow['package_id']}"
2285
2347
  puts " workflow_id: #{workflow['workflow_id']}"
2286
2348
  puts " manage_type: #{workflow['manage_type']}"
2287
2349
  puts " is_git_workflow: #{is_git_workflow}"
@@ -2395,14 +2457,18 @@ module Pindo
2395
2457
  end
2396
2458
 
2397
2459
  # 8. Project ID 不变,检查工作流是否需要更新
2460
+ # 安装包按 package_id 比较(新工具 workflow_id 都是 nil);Git 按 workflow_id 比较
2398
2461
  existing_workflow = existing_config[workflow_key]
2399
- existing_workflow_id = existing_workflow&.dig('workflow_id')
2400
-
2401
- if existing_workflow_id.nil? ||
2402
- existing_workflow_id.to_s.empty? ||
2403
- existing_workflow_id != workflow_info[:workflow_id]
2462
+ changed = if manage_type.to_s == 'git'
2463
+ existing_workflow_id = existing_workflow.is_a?(Hash) ? existing_workflow['workflow_id'] : nil
2464
+ existing_workflow_id.to_s.empty? || existing_workflow_id.to_s != workflow_info[:workflow_id].to_s
2465
+ else
2466
+ existing_package_id = existing_workflow.is_a?(Hash) ? existing_workflow['package_id'] : nil
2467
+ existing_package_id.to_s.empty? || existing_package_id.to_s != workflow_info[:package_id].to_s
2468
+ end
2404
2469
 
2405
- # Workflow ID 变更 → 更新对应的 workflow 和项目信息
2470
+ if changed
2471
+ # 工作流/工具变更 → 更新对应的 workflow 和项目信息
2406
2472
  puts "#{workflow_key} 变更,更新工作流配置"
2407
2473
  existing_config[workflow_key] = new_workflow_data
2408
2474
  existing_config["bundle_group_name"] = new_bundle_group_name
@@ -2412,7 +2478,7 @@ module Pindo
2412
2478
  return
2413
2479
  end
2414
2480
 
2415
- # 9. Project ID 和 Workflow ID 都不变 → 不修改
2481
+ # 9. Project ID 和工作流/工具都不变 → 不修改
2416
2482
  puts "配置未变更,跳过保存" if ENV['PINDO_DEBUG']
2417
2483
  end
2418
2484
  end
@@ -7,6 +7,9 @@ module Pindo
7
7
  class NugetUploadTask < NugetTask
8
8
 
9
9
  DEFAULT_NUGET_BUCKET_NAME = 'nuget-resource'.freeze
10
+ # NuGet 登记项目:JPS「Unity 组件」项目
11
+ # (原「Nuget 组件库」5e6b36f61b614ed88086373ef874616c 已停止接收 NuGet 登记,返回 90006)
12
+ NUGET_PROJECT_ID = '1b9f111539ba4715abb2011fbfe46fd8'.freeze
10
13
 
11
14
  attr_reader :nupkg_file
12
15
 
@@ -132,8 +135,8 @@ module Pindo
132
135
  puts "✅ 登录成功"
133
136
  puts
134
137
 
135
- # 2. 使用固定的 Nuget 组件库项目 ID
136
- nuget_project_id = '5e6b36f61b614ed88086373ef874616c'
138
+ # 2. 使用固定的 NuGet 登记项目(Unity 组件)
139
+ nuget_project_id = NUGET_PROJECT_ID
137
140
  puts
138
141
 
139
142
  # 3. 上传文件到存储
@@ -177,13 +177,13 @@ module Pindo
177
177
 
178
178
  workflow_name: OptionItem.new(
179
179
  key: :workflow_name,
180
- name: '工作流名称',
181
- description: '按名称指定工作流,用于某类型匹配到多个工作流时消歧(非交互场景使用)',
180
+ name: '工作流/工具名称',
181
+ description: '按名称精确选择 Git 工作流或安装包工具(工具可匹配 name、subtitle 或 name(subtitle)),用于同类型多个时消歧(非交互场景使用)',
182
182
  type: String,
183
183
  env_name: 'PINDO_JPS_WORKFLOW_NAME',
184
184
  optional: true,
185
185
  cacheable: false,
186
- example: 'pindo jps config --proj=MyApp --type=ipa --workflow_name="iOS IPA Upload"'
186
+ example: 'pindo jps config --proj=MyApp --type=ipa --workflow_name="iOS提交包"'
187
187
  )
188
188
  }
189
189
  end
data/lib/pindo/version.rb CHANGED
@@ -6,7 +6,7 @@ require 'time'
6
6
 
7
7
  module Pindo
8
8
 
9
- VERSION = "5.21.5"
9
+ VERSION = "5.22.0"
10
10
 
11
11
  class VersionCheck
12
12
  RUBYGEMS_API = 'https://rubygems.org/api/v1/gems/pindo.json'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pindo
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.21.5
4
+ version: 5.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - wade
@@ -536,7 +536,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
536
536
  - !ruby/object:Gem::Version
537
537
  version: 3.0.0
538
538
  requirements: []
539
- rubygems_version: 4.0.11
539
+ rubygems_version: 4.0.10
540
540
  specification_version: 4
541
541
  summary: easy work
542
542
  test_files: []