pindo 5.13.9 → 5.13.11
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 +4 -4
- data/lib/pindo/base/funlog.rb +13 -0
- data/lib/pindo/base/git_handler.rb +247 -42
- data/lib/pindo/command/android/autobuild.rb +72 -30
- data/lib/pindo/command/android/autoresign.rb +23 -322
- data/lib/pindo/command/android/keystore.rb +7 -130
- data/lib/pindo/command/appstore/adhocbuild.rb +5 -14
- data/lib/pindo/command/appstore/autobuild.rb +64 -14
- data/lib/pindo/command/appstore/autoresign.rb +1 -3
- data/lib/pindo/command/ios/autobuild.rb +71 -53
- data/lib/pindo/command/ios/build.rb +8 -186
- data/lib/pindo/command/jps/media.rb +146 -0
- data/lib/pindo/command/jps/upload.rb +48 -20
- data/lib/pindo/command/jps.rb +1 -0
- data/lib/pindo/command/unity/autobuild.rb +99 -27
- data/lib/pindo/command/unity/packpush.rb +5 -8
- data/lib/pindo/command/utils/repoinit.rb +0 -2
- data/lib/pindo/command/utils/tag.rb +58 -26
- data/lib/pindo/command/utils.rb +0 -1
- data/lib/pindo/command/web/autobuild.rb +71 -37
- data/lib/pindo/command.rb +0 -56
- data/lib/pindo/config/build_info_manager.rb +7 -8
- data/lib/pindo/module/android/android_config_helper.rb +2 -11
- data/lib/pindo/module/appselect.rb +15 -41
- data/lib/pindo/module/appstore/itcapp_helper.rb +3 -6
- data/lib/pindo/module/build/build_helper.rb +28 -18
- data/lib/pindo/module/build/git_repo_helper.rb +284 -405
- data/lib/pindo/module/cert/pem_helper.rb +3 -6
- data/lib/pindo/module/pgyer/pgyerhelper.rb +193 -25
- data/lib/pindo/module/task/model/appstore/appstore_task.rb +5 -0
- data/lib/pindo/module/task/model/build/android_build_adhoc_task.rb +13 -187
- data/lib/pindo/module/task/model/build/android_build_dev_task.rb +36 -34
- data/lib/pindo/module/task/model/build/android_build_gplay_task.rb +13 -187
- data/lib/pindo/module/task/model/build/ios_build_adhoc_task.rb +9 -6
- data/lib/pindo/module/task/model/build/ios_build_appstore_task.rb +9 -6
- data/lib/pindo/module/task/model/build/ios_build_dev_task.rb +37 -32
- data/lib/pindo/module/task/model/build/web_build_dev_task.rb +7 -5
- data/lib/pindo/module/task/model/build_task.rb +8 -11
- data/lib/pindo/module/task/model/git/git_commit_task.rb +118 -0
- data/lib/pindo/module/task/model/git/git_tag_task.rb +125 -0
- data/lib/pindo/module/task/model/git_task.rb +75 -0
- data/lib/pindo/module/task/model/jps/jps_message_task.rb +178 -0
- data/lib/pindo/module/task/model/{resign → jps}/jps_resign_task.rb +13 -22
- data/lib/pindo/module/task/model/jps/jps_upload_media_task.rb +248 -0
- data/lib/pindo/module/task/model/jps/jps_upload_task.rb +38 -93
- data/lib/pindo/module/task/model/jps_task.rb +43 -0
- data/lib/pindo/module/task/model/resign/ipa_local_resign_task.rb +5 -0
- data/lib/pindo/module/task/model/unity/unity_config_task.rb +0 -4
- data/lib/pindo/module/task/model/unity/unity_export_task.rb +8 -7
- data/lib/pindo/module/task/model/unity/unity_update_task.rb +4 -3
- data/lib/pindo/module/task/model/unity/unity_yoo_asset_task.rb +8 -7
- data/lib/pindo/module/task/model/unity_task.rb +7 -2
- data/lib/pindo/module/task/pindo_task.rb +101 -1
- data/lib/pindo/module/task/task_manager.rb +29 -32
- data/lib/pindo/module/unity/nuget_helper.rb +7 -7
- data/lib/pindo/options/core/global_options_state.rb +96 -26
- data/lib/pindo/options/core/option_configuration.rb +3 -0
- data/lib/pindo/options/core/option_item.rb +36 -0
- data/lib/pindo/options/groups/build_options.rb +23 -6
- data/lib/pindo/options/groups/git_options.rb +115 -0
- data/lib/pindo/options/groups/jps_options.rb +7 -0
- data/lib/pindo/options/groups/option_group.rb +15 -0
- data/lib/pindo/options/groups/unity_options.rb +49 -0
- data/lib/pindo/options/options.rb +2 -0
- data/lib/pindo/version.rb +2 -2
- metadata +15 -11
- data/lib/pindo/base/githelper.rb +0 -686
- data/lib/pindo/base/pindocontext.rb +0 -602
- data/lib/pindo/command/utils/feishu.rb +0 -134
- data/lib/pindo/module/build/version_helper.rb +0 -146
- data/lib/pindo/module/task/model/git_tag_task.rb +0 -80
|
@@ -32,20 +32,23 @@ module Pindo
|
|
|
32
32
|
# PindoTask 基类(简化版,所有任务在主线程中执行)
|
|
33
33
|
class PindoTask
|
|
34
34
|
attr_accessor :id, :name, :status, :error, :result
|
|
35
|
-
attr_reader :type, :priority, :dependencies
|
|
35
|
+
attr_reader :type, :task_key, :priority, :dependencies, :data_dependencies
|
|
36
36
|
attr_accessor :context, :metadata
|
|
37
37
|
attr_reader :created_at, :started_at, :finished_at
|
|
38
38
|
attr_accessor :retry_count # 剩余重试次数
|
|
39
39
|
attr_reader :retry_mode, :retry_delay, :max_retry_count # max_retry_count: 初始最大重试次数
|
|
40
40
|
attr_accessor :callbacks_setup # 标记回调是否已经设置
|
|
41
|
+
attr_accessor :task_manager # TaskManager 实例(依赖注入)
|
|
41
42
|
|
|
42
43
|
def initialize(name, options = {})
|
|
43
44
|
@id = SecureRandom.uuid
|
|
44
45
|
@name = name
|
|
45
46
|
@type = self.class.task_type
|
|
47
|
+
@task_key = self.class.task_key
|
|
46
48
|
@priority = options[:priority] || TaskPriority::HIGH
|
|
47
49
|
@status = TaskStatus::PENDING
|
|
48
50
|
@dependencies = options[:dependencies] || []
|
|
51
|
+
@data_dependencies = options[:data_dependencies] || []
|
|
49
52
|
@context = options[:context] || {}
|
|
50
53
|
@metadata = options[:metadata] || {}
|
|
51
54
|
@error = nil
|
|
@@ -73,6 +76,10 @@ module Pindo
|
|
|
73
76
|
raise NotImplementedError, "Subclass must define task_type"
|
|
74
77
|
end
|
|
75
78
|
|
|
79
|
+
def self.task_key
|
|
80
|
+
raise NotImplementedError, "Subclass must define task_key"
|
|
81
|
+
end
|
|
82
|
+
|
|
76
83
|
# 默认重试配置
|
|
77
84
|
def self.default_retry_mode
|
|
78
85
|
RetryMode::IMMEDIATE
|
|
@@ -135,6 +142,93 @@ module Pindo
|
|
|
135
142
|
@status == TaskStatus::CANCELLED
|
|
136
143
|
end
|
|
137
144
|
|
|
145
|
+
# ========== 依赖任务数据获取 ==========
|
|
146
|
+
|
|
147
|
+
# 获取指定依赖任务
|
|
148
|
+
# @param dep_task_id [String] 依赖任务的 ID
|
|
149
|
+
# @return [PindoTask, nil] 依赖任务对象,如果不存在返回 nil
|
|
150
|
+
def get_dependency_task(dep_task_id)
|
|
151
|
+
return nil unless @task_manager
|
|
152
|
+
@task_manager.find_task(dep_task_id)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# 获取指定依赖任务的结果
|
|
156
|
+
# @param dep_task_id [String] 依赖任务的 ID
|
|
157
|
+
# @return [Hash, nil] 依赖任务的 result,如果任务不存在或未完成返回 nil
|
|
158
|
+
def get_dependency_result(dep_task_id)
|
|
159
|
+
dep_task = get_dependency_task(dep_task_id)
|
|
160
|
+
return nil unless dep_task
|
|
161
|
+
return nil unless dep_task.finished?
|
|
162
|
+
dep_task.result
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# 获取所有依赖任务的结果(按依赖顺序)
|
|
166
|
+
# @return [Hash] key 为任务 ID,value 为任务结果
|
|
167
|
+
def get_all_dependencies_results
|
|
168
|
+
results = {}
|
|
169
|
+
@dependencies.each do |dep_id|
|
|
170
|
+
results[dep_id] = get_dependency_result(dep_id)
|
|
171
|
+
end
|
|
172
|
+
results
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# ========== 任务数据参数传递 ==========
|
|
176
|
+
|
|
177
|
+
# 获取任务的数据参数(用于传递给其他任务)
|
|
178
|
+
# @return [Hash] 包含任务标识和参数的哈希
|
|
179
|
+
def data_param
|
|
180
|
+
{
|
|
181
|
+
task_id: @id,
|
|
182
|
+
task_type: @type,
|
|
183
|
+
task_key: @task_key,
|
|
184
|
+
task_name: @name,
|
|
185
|
+
task_param: build_task_param
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# 获取指定数据依赖任务的数据参数
|
|
190
|
+
# @param task_id [String] 任务 ID
|
|
191
|
+
# @return [Hash, nil] 任务的数据参数,如果任务不存在或未完成返回 nil
|
|
192
|
+
def get_data_param(task_id)
|
|
193
|
+
dep_task = get_dependency_task(task_id)
|
|
194
|
+
return nil unless dep_task
|
|
195
|
+
return nil unless dep_task.finished? && dep_task.status == TaskStatus::SUCCESS
|
|
196
|
+
dep_task.data_param
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# 获取所有数据依赖任务的数据参数
|
|
200
|
+
# @return [Array<Hash>] 数据参数数组
|
|
201
|
+
def get_all_data_params
|
|
202
|
+
@data_dependencies.map { |task_id| get_data_param(task_id) }.compact
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# 根据 task_key 获取数据依赖任务的数据参数
|
|
206
|
+
# @param task_key [Symbol] 任务键
|
|
207
|
+
# @return [Hash, nil] 第一个匹配的任务数据参数
|
|
208
|
+
def get_data_param_by_key(task_key)
|
|
209
|
+
@data_dependencies.each do |task_id|
|
|
210
|
+
param = get_data_param(task_id)
|
|
211
|
+
return param if param && param[:task_key] == task_key
|
|
212
|
+
end
|
|
213
|
+
nil
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# 获取所有指定 task_key 的数据参数
|
|
217
|
+
# @param task_key [Symbol] 任务键
|
|
218
|
+
# @return [Array<Hash>] 匹配的任务数据参数数组
|
|
219
|
+
def get_all_data_params_by_key(task_key)
|
|
220
|
+
get_all_data_params.select { |param| param[:task_key] == task_key }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# 获取主数据参数(第一个数据依赖任务的参数)
|
|
224
|
+
# @return [Hash, nil] 主数据参数
|
|
225
|
+
def primary_data_param
|
|
226
|
+
return nil if @data_dependencies.empty?
|
|
227
|
+
get_data_param(@data_dependencies.first)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# ========== 回调方法 ==========
|
|
231
|
+
|
|
138
232
|
# 添加回调
|
|
139
233
|
def on(event, &block)
|
|
140
234
|
@callbacks[event] << block if @callbacks[event]
|
|
@@ -167,6 +261,12 @@ module Pindo
|
|
|
167
261
|
raise NotImplementedError, "Subclass must implement do_work method"
|
|
168
262
|
end
|
|
169
263
|
|
|
264
|
+
# 构建任务参数(子类重写以提供自定义参数)
|
|
265
|
+
# @return [Hash] 任务参数
|
|
266
|
+
def build_task_param
|
|
267
|
+
{} # 默认返回空哈希
|
|
268
|
+
end
|
|
269
|
+
|
|
170
270
|
private
|
|
171
271
|
|
|
172
272
|
# 内部执行逻辑
|
|
@@ -108,6 +108,15 @@ module Pindo
|
|
|
108
108
|
task&.cancel
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
+
# 查找任务(公共方法,供 PindoTask 获取依赖任务使用)
|
|
112
|
+
# @param task_id [String] 任务 ID
|
|
113
|
+
# @return [PindoTask, nil] 任务对象
|
|
114
|
+
def find_task(task_id)
|
|
115
|
+
all_tasks = @pending_queue + @completed_tasks
|
|
116
|
+
all_tasks << @current_task if @current_task
|
|
117
|
+
all_tasks.find { |t| t.id == task_id }
|
|
118
|
+
end
|
|
119
|
+
|
|
111
120
|
private
|
|
112
121
|
|
|
113
122
|
# 获取下一个可执行的任务
|
|
@@ -168,6 +177,9 @@ module Pindo
|
|
|
168
177
|
def execute_task(task)
|
|
169
178
|
@current_task = task
|
|
170
179
|
|
|
180
|
+
# 注入 TaskManager 实例(依赖注入)
|
|
181
|
+
task.task_manager = self
|
|
182
|
+
|
|
171
183
|
# 设置任务进度回调
|
|
172
184
|
setup_task_callbacks(task)
|
|
173
185
|
|
|
@@ -224,13 +236,6 @@ module Pindo
|
|
|
224
236
|
Funlog.warning("任务 #{task.name} 因#{reason}而被取消")
|
|
225
237
|
end
|
|
226
238
|
|
|
227
|
-
# 查找任务
|
|
228
|
-
def find_task(task_id)
|
|
229
|
-
all_tasks = @pending_queue + @completed_tasks
|
|
230
|
-
all_tasks << @current_task if @current_task
|
|
231
|
-
all_tasks.find { |t| t.id == task_id }
|
|
232
|
-
end
|
|
233
|
-
|
|
234
239
|
# 输出任务执行计划
|
|
235
240
|
def print_execution_plan
|
|
236
241
|
# 按类型分组统计
|
|
@@ -242,37 +247,21 @@ module Pindo
|
|
|
242
247
|
puts "=" * 60
|
|
243
248
|
|
|
244
249
|
tasks_by_type.each do |type, tasks|
|
|
245
|
-
|
|
246
|
-
|
|
250
|
+
# 直接从任务类获取显示名称
|
|
251
|
+
type_name = tasks.first&.class&.task_type_name || type.to_s.capitalize
|
|
252
|
+
puts "\n #{type_name}: #{tasks.count} 个任务"
|
|
253
|
+
|
|
254
|
+
# 显示该类型下的每个任务名称
|
|
255
|
+
tasks.each do |task|
|
|
256
|
+
puts " - #{task.name}"
|
|
257
|
+
end
|
|
247
258
|
end
|
|
248
259
|
|
|
249
|
-
puts " 总计: #{@pending_queue.count} 个任务"
|
|
260
|
+
puts "\n 总计: #{@pending_queue.count} 个任务"
|
|
250
261
|
puts "=" * 60 + "\e[0m"
|
|
251
262
|
puts "\n"
|
|
252
263
|
end
|
|
253
264
|
|
|
254
|
-
# 获取任务类型的显示名称
|
|
255
|
-
def get_type_display_name(type)
|
|
256
|
-
case type
|
|
257
|
-
when :git_tag
|
|
258
|
-
"Git仓库打标签"
|
|
259
|
-
when :unity_config
|
|
260
|
-
"Unity编译模式配置"
|
|
261
|
-
when :unity_update
|
|
262
|
-
"Unity工具库更新"
|
|
263
|
-
when :unity_yoo_asset
|
|
264
|
-
"Unity资源Yoo 打包"
|
|
265
|
-
when :unity_export
|
|
266
|
-
"Unity导出"
|
|
267
|
-
when :build
|
|
268
|
-
"编译构建"
|
|
269
|
-
when :upload
|
|
270
|
-
"上传发布"
|
|
271
|
-
else
|
|
272
|
-
type.to_s.capitalize
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
|
|
276
265
|
# 输出任务执行头部
|
|
277
266
|
def print_task_header(task)
|
|
278
267
|
puts "\n"
|
|
@@ -293,6 +282,14 @@ module Pindo
|
|
|
293
282
|
puts "\n"
|
|
294
283
|
Funlog.error("任务失败: #{task.name}")
|
|
295
284
|
Funlog.error("错误信息: #{error.message}")
|
|
285
|
+
|
|
286
|
+
# 打印堆栈信息用于调试(仅在 PINDO_DEBUG 模式下)
|
|
287
|
+
if ENV['PINDO_DEBUG'] && error.backtrace && !error.backtrace.empty?
|
|
288
|
+
puts "\n堆栈信息:"
|
|
289
|
+
error.backtrace.first(10).each do |line|
|
|
290
|
+
puts " #{line}"
|
|
291
|
+
end
|
|
292
|
+
end
|
|
296
293
|
end
|
|
297
294
|
|
|
298
295
|
# 输出任务底部分隔线
|
|
@@ -2,11 +2,11 @@ require 'fileutils'
|
|
|
2
2
|
require 'json'
|
|
3
3
|
require 'nokogiri'
|
|
4
4
|
require 'open3'
|
|
5
|
+
require 'pindo/base/git_handler'
|
|
5
6
|
|
|
6
7
|
module Pindo
|
|
7
8
|
module Unity
|
|
8
9
|
class NugetHelper
|
|
9
|
-
extend Pindo::Githelper
|
|
10
10
|
|
|
11
11
|
# ============================================
|
|
12
12
|
# ID 格式转换
|
|
@@ -535,11 +535,11 @@ module Pindo
|
|
|
535
535
|
default_message = "feat: 更新版本到 #{nuspec_version}"
|
|
536
536
|
|
|
537
537
|
# 如果不是 Git 仓库,返回默认消息
|
|
538
|
-
unless is_git_directory?(local_repo_dir: package_dir)
|
|
538
|
+
unless Pindo::GitHandler.is_git_directory?(local_repo_dir: package_dir)
|
|
539
539
|
return default_message
|
|
540
540
|
end
|
|
541
541
|
|
|
542
|
-
git_root = git_root_directory(local_repo_dir: package_dir)
|
|
542
|
+
git_root = Pindo::GitHandler.git_root_directory(local_repo_dir: package_dir)
|
|
543
543
|
unless git_root
|
|
544
544
|
return default_message
|
|
545
545
|
end
|
|
@@ -551,7 +551,7 @@ module Pindo
|
|
|
551
551
|
Dir.chdir(git_root)
|
|
552
552
|
|
|
553
553
|
# 获取所有 tags
|
|
554
|
-
all_tags = git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
554
|
+
all_tags = Pindo::GitHandler.git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
555
555
|
|
|
556
556
|
# 查找匹配当前版本的 tag(不区分大小写,支持 v 前缀)
|
|
557
557
|
matching_tag = all_tags.find do |tag|
|
|
@@ -570,7 +570,7 @@ module Pindo
|
|
|
570
570
|
Dir.chdir(git_root)
|
|
571
571
|
|
|
572
572
|
# 获取所有 tags 并按版本号排序
|
|
573
|
-
all_tags = git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
573
|
+
all_tags = Pindo::GitHandler.git!(%W(-C #{git_root} tag -l)).split("\n")
|
|
574
574
|
sorted_tags = all_tags.sort_by do |tag|
|
|
575
575
|
version_str = tag.gsub(/^(v|V|release[\s_-]*)/i, '')
|
|
576
576
|
version_str.split('.').map(&:to_i)
|
|
@@ -598,7 +598,7 @@ module Pindo
|
|
|
598
598
|
# 获取最新 tag
|
|
599
599
|
latest_tag = nil
|
|
600
600
|
["v", "V", "release", ""].each do |prefix|
|
|
601
|
-
latest_tag = get_latest_version_tag(project_dir: git_root, tag_prefix: prefix)
|
|
601
|
+
latest_tag = Pindo::GitHandler.get_latest_version_tag(project_dir: git_root, tag_prefix: prefix)
|
|
602
602
|
break if latest_tag
|
|
603
603
|
end
|
|
604
604
|
|
|
@@ -615,7 +615,7 @@ module Pindo
|
|
|
615
615
|
|
|
616
616
|
# 使用特殊分隔符来区分不同的 commits
|
|
617
617
|
separator = "---COMMIT-SEPARATOR---"
|
|
618
|
-
commits_raw = git!(%W(-C #{git_root} log #{git_range} --pretty=format:%B#{separator}))
|
|
618
|
+
commits_raw = Pindo::GitHandler.git!(%W(-C #{git_root} log #{git_range} --pretty=format:%B#{separator}))
|
|
619
619
|
|
|
620
620
|
# 按分隔符拆分成单个 commits
|
|
621
621
|
commits = commits_raw.split(separator).map(&:strip).reject(&:empty?)
|
|
@@ -11,6 +11,14 @@ module Pindo
|
|
|
11
11
|
class GlobalOptionsState
|
|
12
12
|
include Singleton
|
|
13
13
|
|
|
14
|
+
# 所有已知的 OptionGroup 模块(用于查找参数显示名称)
|
|
15
|
+
OPTION_GROUPS = [
|
|
16
|
+
-> { Pindo::Options::BuildOptions },
|
|
17
|
+
-> { Pindo::Options::JPSOptions },
|
|
18
|
+
-> { Pindo::Options::UnityOptions },
|
|
19
|
+
-> { Pindo::Options::GitOptions }
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
14
22
|
def initialize
|
|
15
23
|
# 运行时状态(内存)
|
|
16
24
|
@current_command = nil # 当前命令名称
|
|
@@ -40,10 +48,23 @@ module Pindo
|
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
# 加载缓存的参数值(带用户确认)
|
|
51
|
+
# 环境变量 PINDO_OPTIONS_CACHE 控制缓存行为:
|
|
52
|
+
# - 1/true/force: 强制使用缓存,不询问用户
|
|
53
|
+
# - 0/false/disable: 禁用缓存,不询问用户
|
|
54
|
+
# - 其他/不设置: 默认行为,询问用户
|
|
43
55
|
# @return [Hash] 缓存的参数值
|
|
44
56
|
def load_cached_values
|
|
45
57
|
return {} unless @current_directory && @current_command
|
|
46
58
|
|
|
59
|
+
# 检查缓存控制环境变量
|
|
60
|
+
cache_mode = ENV['PINDO_OPTIONS_CACHE']&.downcase
|
|
61
|
+
|
|
62
|
+
# 禁用缓存模式: 0, false, disable
|
|
63
|
+
if %w[0 false disable].include?(cache_mode)
|
|
64
|
+
log_verbose("PINDO_OPTIONS_CACHE=#{cache_mode},跳过缓存")
|
|
65
|
+
return {}
|
|
66
|
+
end
|
|
67
|
+
|
|
47
68
|
cached_params = @cache_data.dig(@current_directory.to_sym, @current_command.to_sym)
|
|
48
69
|
|
|
49
70
|
# 没有缓存数据,直接返回空Hash
|
|
@@ -52,17 +73,14 @@ module Pindo
|
|
|
52
73
|
return {}
|
|
53
74
|
end
|
|
54
75
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if force_build && !force_build.empty?
|
|
59
|
-
# 自动使用缓存
|
|
60
|
-
puts "\n检测到 PINDO_FORCE_BUILD 环境变量,自动使用缓存的参数"
|
|
76
|
+
# 强制使用缓存模式: 1, true, force
|
|
77
|
+
if %w[1 true force].include?(cache_mode)
|
|
78
|
+
puts "\n自动使用缓存的参数 (PINDO_OPTIONS_CACHE=#{cache_mode})"
|
|
61
79
|
log_verbose("加载缓存参数: #{cached_params.inspect}")
|
|
62
80
|
return cached_params
|
|
63
81
|
end
|
|
64
82
|
|
|
65
|
-
#
|
|
83
|
+
# 默认模式:显示缓存的参数并询问用户
|
|
66
84
|
display_cached_params(cached_params)
|
|
67
85
|
|
|
68
86
|
# 询问用户是否使用缓存
|
|
@@ -82,6 +100,20 @@ module Pindo
|
|
|
82
100
|
end
|
|
83
101
|
end
|
|
84
102
|
|
|
103
|
+
# 参数显示顺序(优先级从高到低)
|
|
104
|
+
PARAM_DISPLAY_ORDER = [
|
|
105
|
+
# 1. 核心标识参数
|
|
106
|
+
:bundleid, :bundle_id, :bundle_name,
|
|
107
|
+
# 2. 构建配置
|
|
108
|
+
:build_type, :scheme,
|
|
109
|
+
# 3. JPS 相关
|
|
110
|
+
:proj, :upload, :send, :desc,
|
|
111
|
+
# 4. Unity 相关
|
|
112
|
+
:skipconfig, :skiplib, :skipyoo,
|
|
113
|
+
# 5. Git 相关
|
|
114
|
+
:ver_inc, :tag_type, :tag_pre, :release_branch
|
|
115
|
+
].freeze
|
|
116
|
+
|
|
85
117
|
# 显示缓存的参数
|
|
86
118
|
def display_cached_params(cached_params)
|
|
87
119
|
# 根据命令名显示友好的描述
|
|
@@ -99,10 +131,18 @@ module Pindo
|
|
|
99
131
|
puts "\n检测到之前的参数 (#{group_desc}):"
|
|
100
132
|
puts "────────────────────────────────────────"
|
|
101
133
|
|
|
102
|
-
|
|
134
|
+
# 按照预定义顺序排序参数
|
|
135
|
+
sorted_keys = cached_params.keys.sort_by do |key|
|
|
136
|
+
order_index = PARAM_DISPLAY_ORDER.index(key.to_sym)
|
|
137
|
+
order_index || PARAM_DISPLAY_ORDER.size # 未定义的参数排在最后
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
sorted_keys.each do |key|
|
|
141
|
+
value = cached_params[key]
|
|
103
142
|
# 跳过内部字段
|
|
104
143
|
next if key.to_s.start_with?('__')
|
|
105
144
|
next if value.nil?
|
|
145
|
+
next unless is_cacheable?(key) # 跳过不可缓存的参数
|
|
106
146
|
|
|
107
147
|
# 格式化显示参数
|
|
108
148
|
key_name = format_param_name(key)
|
|
@@ -112,24 +152,28 @@ module Pindo
|
|
|
112
152
|
puts "────────────────────────────────────────"
|
|
113
153
|
end
|
|
114
154
|
|
|
115
|
-
#
|
|
155
|
+
# 格式化参数名称(从 OptionItem 定义中查找显示名称)
|
|
156
|
+
# @param key [Symbol, String] 参数键名
|
|
157
|
+
# @return [String] 显示名称
|
|
116
158
|
def format_param_name(key)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
key.to_s
|
|
159
|
+
key_sym = key.to_sym
|
|
160
|
+
|
|
161
|
+
# 遍历所有已知的 OptionGroup,查找匹配的 OptionItem
|
|
162
|
+
OPTION_GROUPS.each do |group_proc|
|
|
163
|
+
begin
|
|
164
|
+
group = group_proc.call
|
|
165
|
+
if group.respond_to?(:all_options)
|
|
166
|
+
option_item = group.all_options[key_sym]
|
|
167
|
+
return option_item.display_name if option_item
|
|
168
|
+
end
|
|
169
|
+
rescue NameError
|
|
170
|
+
# 模块尚未加载,跳过
|
|
171
|
+
next
|
|
172
|
+
end
|
|
132
173
|
end
|
|
174
|
+
|
|
175
|
+
# 如果没有找到匹配的 OptionItem,返回 key 本身
|
|
176
|
+
key.to_s
|
|
133
177
|
end
|
|
134
178
|
|
|
135
179
|
# 清除当前命令的缓存
|
|
@@ -235,10 +279,12 @@ module Pindo
|
|
|
235
279
|
# 确保缓存数据结构存在
|
|
236
280
|
@cache_data[@current_directory.to_sym] ||= {}
|
|
237
281
|
|
|
238
|
-
# 提取当前参数值(排除 nil
|
|
282
|
+
# 提取当前参数值(排除 nil 值和不可缓存的参数)
|
|
239
283
|
current_params = {}
|
|
240
284
|
@current_options.instance_variable_get(:@values).each do |key, value|
|
|
241
|
-
|
|
285
|
+
next if value.nil?
|
|
286
|
+
next unless is_cacheable?(key) # 过滤不可缓存的参数
|
|
287
|
+
current_params[key] = value
|
|
242
288
|
end
|
|
243
289
|
|
|
244
290
|
# 保存到缓存
|
|
@@ -248,6 +294,30 @@ module Pindo
|
|
|
248
294
|
save_cache_to_file_immediate
|
|
249
295
|
end
|
|
250
296
|
|
|
297
|
+
# 判断参数是否可缓存
|
|
298
|
+
# @param key [Symbol, String] 参数键名
|
|
299
|
+
# @return [Boolean] 是否可缓存
|
|
300
|
+
def is_cacheable?(key)
|
|
301
|
+
key_sym = key.to_sym
|
|
302
|
+
|
|
303
|
+
# 遍历所有已知的 OptionGroup,查找匹配的 OptionItem
|
|
304
|
+
OPTION_GROUPS.each do |group_proc|
|
|
305
|
+
begin
|
|
306
|
+
group = group_proc.call
|
|
307
|
+
if group.respond_to?(:all_options)
|
|
308
|
+
option_item = group.all_options[key_sym]
|
|
309
|
+
return option_item.cacheable? if option_item
|
|
310
|
+
end
|
|
311
|
+
rescue NameError
|
|
312
|
+
# 模块尚未加载,跳过
|
|
313
|
+
next
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# 如果没有找到匹配的 OptionItem,默认可缓存
|
|
318
|
+
true
|
|
319
|
+
end
|
|
320
|
+
|
|
251
321
|
# 立即保存缓存数据到文件(内部方法)
|
|
252
322
|
def save_cache_to_file_immediate
|
|
253
323
|
begin
|
|
@@ -133,6 +133,9 @@ module Pindo
|
|
|
133
133
|
# 应用 value_block(交互式获取值)
|
|
134
134
|
# 优先级:命令行参数 > 缓存(已在 raw_values) > value_block > 默认值
|
|
135
135
|
def apply_value_blocks
|
|
136
|
+
# 检测是否是 help 请求,跳过交互式选择
|
|
137
|
+
return if ARGV.include?('--help') || ARGV.include?('-h')
|
|
138
|
+
|
|
136
139
|
@available_options.each do |item|
|
|
137
140
|
# 只有当参数没有值时,才调用 value_block
|
|
138
141
|
next if @values.key?(item.key) && !@values[item.key].nil?
|
|
@@ -5,12 +5,14 @@ module Pindo
|
|
|
5
5
|
|
|
6
6
|
# 核心属性
|
|
7
7
|
attr_accessor :key # Symbol: 参数键名
|
|
8
|
+
attr_accessor :name # String: 显示名称(用于缓存确认等场景)
|
|
8
9
|
attr_accessor :description # String: 参数描述
|
|
9
10
|
attr_accessor :type # Class: 数据类型 (String/Integer/Boolean)
|
|
10
11
|
attr_accessor :env_name # String: 环境变量名
|
|
11
12
|
attr_accessor :aliases # Array<Symbol>: 参数别名
|
|
12
13
|
attr_accessor :default_value # Any: 默认值
|
|
13
14
|
attr_accessor :optional # Boolean: 是否可选(默认为 true)
|
|
15
|
+
attr_accessor :cacheable # Boolean: 是否存入缓存(默认为 true)
|
|
14
16
|
attr_accessor :verify_block # Proc: 自定义验证逻辑
|
|
15
17
|
attr_accessor :value_block # Proc: 获取参数值的 block(交互式输入)
|
|
16
18
|
attr_accessor :example # String: 使用示例
|
|
@@ -25,12 +27,14 @@ module Pindo
|
|
|
25
27
|
raise ArgumentError, "key must be a Symbol" unless key.is_a?(Symbol)
|
|
26
28
|
|
|
27
29
|
@key = key
|
|
30
|
+
@name = options[:name] # 显示名称,如果未设置则使用 key
|
|
28
31
|
@description = options[:description] || options[:desc] || ""
|
|
29
32
|
@type = options[:type] || String
|
|
30
33
|
@env_name = options[:env_name]
|
|
31
34
|
@aliases = options[:aliases] || []
|
|
32
35
|
@default_value = options[:default_value] || options[:default]
|
|
33
36
|
@optional = options.fetch(:optional, true)
|
|
37
|
+
@cacheable = options.fetch(:cacheable, false) # 默认不存入缓存
|
|
34
38
|
@verify_block = options[:verify_block]
|
|
35
39
|
@value_block = options[:value_block]
|
|
36
40
|
@example = options[:example]
|
|
@@ -38,11 +42,21 @@ module Pindo
|
|
|
38
42
|
validate_type!
|
|
39
43
|
end
|
|
40
44
|
|
|
45
|
+
# 获取显示名称(优先使用 name,否则使用 key)
|
|
46
|
+
def display_name
|
|
47
|
+
@name || @key.to_s
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
# 判断是否是 Boolean 类型
|
|
42
51
|
def boolean?
|
|
43
52
|
@type == Boolean || @type == :boolean
|
|
44
53
|
end
|
|
45
54
|
|
|
55
|
+
# 判断是否需要存入缓存
|
|
56
|
+
def cacheable?
|
|
57
|
+
@cacheable
|
|
58
|
+
end
|
|
59
|
+
|
|
46
60
|
# 从环境变量读取值
|
|
47
61
|
# @return [String, nil] 环境变量的值
|
|
48
62
|
def fetch_env_value
|
|
@@ -98,6 +112,28 @@ module Pindo
|
|
|
98
112
|
[option_string, description_text]
|
|
99
113
|
end
|
|
100
114
|
|
|
115
|
+
# 复制当前 OptionItem 并覆盖指定属性
|
|
116
|
+
# @param overrides [Hash] 要覆盖的属性
|
|
117
|
+
# @return [OptionItem] 新的 OptionItem 实例
|
|
118
|
+
# @example
|
|
119
|
+
# UnityOptions.select(:skipconfig).first.with(default_value: true)
|
|
120
|
+
def with(**overrides)
|
|
121
|
+
OptionItem.new(
|
|
122
|
+
key: overrides[:key] || @key,
|
|
123
|
+
name: overrides[:name] || @name,
|
|
124
|
+
description: overrides[:description] || @description,
|
|
125
|
+
type: overrides[:type] || @type,
|
|
126
|
+
env_name: overrides[:env_name] || @env_name,
|
|
127
|
+
aliases: overrides[:aliases] || @aliases,
|
|
128
|
+
default_value: overrides.key?(:default_value) ? overrides[:default_value] : @default_value,
|
|
129
|
+
optional: overrides.key?(:optional) ? overrides[:optional] : @optional,
|
|
130
|
+
cacheable: overrides.key?(:cacheable) ? overrides[:cacheable] : @cacheable,
|
|
131
|
+
verify_block: overrides[:verify_block] || @verify_block,
|
|
132
|
+
value_block: overrides[:value_block] || @value_block,
|
|
133
|
+
example: overrides[:example] || @example
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
101
137
|
private
|
|
102
138
|
|
|
103
139
|
# 验证类型是否合法
|
|
@@ -11,26 +11,42 @@ module Pindo
|
|
|
11
11
|
def self.all_options
|
|
12
12
|
|
|
13
13
|
@all_options ||= {
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
bundleid: OptionItem.new(
|
|
16
16
|
key: :bundleid,
|
|
17
|
-
|
|
17
|
+
name: 'Bundle ID',
|
|
18
|
+
description: '指定 iOS Bundle ID',
|
|
18
19
|
type: String,
|
|
19
20
|
env_name: 'PINDO_BUNDLE_ID',
|
|
20
|
-
aliases: [:bundle_id, :package_name, :bundle_name],
|
|
21
21
|
optional: true,
|
|
22
|
+
cacheable: true, # 存入文件缓存
|
|
22
23
|
verify_block: proc do |value|
|
|
23
|
-
# 支持 iOS 格式 (com.example.app) 和 Android 格式 (com.example.app)
|
|
24
|
-
# iOS 可以包含大写字母、连字符和通配符(*)
|
|
25
24
|
unless value =~ /^[a-zA-Z0-9\-\.\*]+$/
|
|
26
|
-
raise "Bundle ID
|
|
25
|
+
raise "Bundle ID 格式错误: #{value},应该类似 com.example.app"
|
|
27
26
|
end
|
|
28
27
|
end,
|
|
29
28
|
example: 'pindo ios autobuild --bundleid=com.example.app'
|
|
30
29
|
),
|
|
31
30
|
|
|
31
|
+
bundle_name: OptionItem.new(
|
|
32
|
+
key: :bundle_name,
|
|
33
|
+
name: 'Package Name',
|
|
34
|
+
description: '指定 Android Package Name',
|
|
35
|
+
type: String,
|
|
36
|
+
env_name: 'PINDO_BUNDLE_NAME',
|
|
37
|
+
optional: true,
|
|
38
|
+
cacheable: true, # 存入文件缓存
|
|
39
|
+
verify_block: proc do |value|
|
|
40
|
+
unless value =~ /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i
|
|
41
|
+
raise "Package Name 格式错误: #{value},应该类似 com.example.app"
|
|
42
|
+
end
|
|
43
|
+
end,
|
|
44
|
+
example: 'pindo android autobuild --bundle_name=com.example.app'
|
|
45
|
+
),
|
|
46
|
+
|
|
32
47
|
build_type: OptionItem.new(
|
|
33
48
|
key: :build_type,
|
|
49
|
+
name: '构建类型',
|
|
34
50
|
description: '指定构建类型(dev/adhoc/release)',
|
|
35
51
|
type: String,
|
|
36
52
|
env_name: 'PINDO_BUILD_TYPE',
|
|
@@ -47,6 +63,7 @@ module Pindo
|
|
|
47
63
|
|
|
48
64
|
scheme: OptionItem.new(
|
|
49
65
|
key: :scheme,
|
|
66
|
+
name: 'Scheme',
|
|
50
67
|
description: '指定构建 Scheme',
|
|
51
68
|
type: String,
|
|
52
69
|
env_name: 'PINDO_SCHEME',
|