pod-binary-pack 1.0.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.
data/lib/core/cli.rb ADDED
@@ -0,0 +1,442 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'yaml'
5
+ require 'fileutils'
6
+
7
+ module Build
8
+ # 命令行入口:负责参数解析、读取工程配置、加载外部钩子/通知器、装配并启动构建
9
+ class CLI
10
+ CONFIG_TEMPLATE = <<~YAML
11
+ gitee:
12
+ endpoint: ""
13
+ access_token: ""
14
+
15
+ hooks_file: event.rb
16
+ sender_file: send.rb
17
+
18
+ # 例外组件配置(可选,普通组件无需声明),key 为组件名;命令行同名参数优先级更高
19
+ # components:
20
+ # SomeLib:
21
+ # source_url: git@gitee.com:xxx/actual-repo.git # 仓名与组件名不一致/一仓多组件
22
+ # binary_url: git@gitee.com:xxx/actual-binary.git
23
+ # podspec_path: SubDir/SomeLib.podspec # podspec不在仓根目录时的相对路径;不配置则自动递归扫描,歧义时报错
24
+ # tag_prefix: "SomeLib/" # 一仓多组件时源码tag前缀(最终tag SomeLib/1.0.0),二进制仍一组件一仓无需此前缀
25
+ # subspecs: [Core, SQLCipher] # 仅集成指定subspec(短名,可含嵌套),用于互斥subspec组件(如FMDB);不配置默认集成全部
26
+
27
+ profiles:
28
+ default:
29
+ # 版本分支前缀:分支名形如 release_1.2.0 / release-1.2.0 时可缺省 --version 自动解析;
30
+ # 版本号不限段数并支持后缀(如 1.2.2.0 / 1.2.2-fix,含非法字符的不解析);
31
+ # 显式传 --version 时必须与分支版本一致。改为其他前缀(如 ver)在此配置
32
+ release_branch_prefix: release
33
+ repos:
34
+ # 壳工程即打包工程:archive 打包与版本回写都在这个工程上完成
35
+ shell:
36
+ url: git@gitee.com:xxx/请替换为壳工程仓库.git
37
+ branch: master
38
+ workspace: 请替换为壳工程名.xcworkspace
39
+ scheme: 请替换为壳工程scheme名
40
+ # archive 仅为提取组件.framework,默认不签名(无需证书/Apple ID)
41
+ code_signing: false
42
+ development_team: ""
43
+ cocoapods_source:
44
+ url: git@gitee.com:xxx/cocoapods_static_source.git
45
+ branch: master
46
+ cocoapods_binary:
47
+ url: git@gitee.com:xxx/cocoapods_static_binary.git
48
+ branch: master
49
+ source_module:
50
+ # 组件仓地址模板,%{pod} 替换为组件名;命名不符模板的个别组件可用 --source-url 显式指定
51
+ url_template: "git@gitee.com:xxx/%{pod}.git"
52
+ binary_module:
53
+ # 同上,例外组件可用 --binary-url 显式指定
54
+ url_template: "git@gitee.com:xxx/%{pod}.git"
55
+ branch: master
56
+ fork_github:
57
+ mode: fork_github
58
+ repos:
59
+ shell:
60
+ url: git@gitee.com:xxx/请替换为壳工程仓库.git
61
+ branch: master
62
+ workspace: 请替换为壳工程名.xcworkspace
63
+ scheme: 请替换为壳工程scheme名
64
+ code_signing: false
65
+ development_team: ""
66
+ cocoapods_source:
67
+ url: git@gitee.com:xxx/cocoapods_static_source.git
68
+ branch: master
69
+ cocoapods_binary:
70
+ url: git@gitee.com:xxx/cocoapods_static_binary.git
71
+ branch: master
72
+ # fork 模式下源码镜像仓地址(由工具自动创建/同步上游 GitHub 源码);例外组件可用 --source-url/--binary-url 显式指定
73
+ source_module:
74
+ url_template: "git@gitee.com:xxx/%{pod}.git"
75
+ binary_module:
76
+ url_template: "git@gitee.com:xxx/%{pod}.git"
77
+ branch: master
78
+ YAML
79
+
80
+ DEFAULT_CONFIG_PATH = 'config/build.yml'
81
+
82
+ def self.run(argv)
83
+ new.run(argv)
84
+ end
85
+
86
+ def fail(message)
87
+ Build.log.error(message)
88
+ exit(-1)
89
+ end
90
+
91
+ def parse_bool(value, name)
92
+ case value.to_s.strip.downcase
93
+ when 'true' then true
94
+ when 'false' then false
95
+ else fail "参数 --#{name} 只接受 true 或 false,当前值: #{value}"
96
+ end
97
+ end
98
+
99
+ def run(argv)
100
+ options = default_options
101
+ parser = build_parser(options)
102
+
103
+ begin
104
+ parser.parse!(argv)
105
+ rescue OptionParser::ParseError => e
106
+ Build.log.error("参数解析失败: #{e.message}\n\n")
107
+ puts parser
108
+ exit(-1)
109
+ end
110
+
111
+ Build.log.debug_enabled = options[:verbose]
112
+
113
+ if options[:help]
114
+ puts parser
115
+ exit(0)
116
+ end
117
+
118
+ if options[:init]
119
+ write_template(options[:config])
120
+ exit(0)
121
+ end
122
+
123
+ # SNAPSHOT 归一化:fork-github 三方库流程不支持 SNAPSHOT 包,
124
+ # 显式传 true 直接前置报错,未传时固定 false;自研组件未显式指定时默认 true
125
+ if options[:fork_github]
126
+ fail 'fork-github 三方库流程不支持 SNAPSHOT 包,请去掉 --snapshot true 或改为 false' if options[:snapshot_explicit] && options[:snapshot]
127
+ options[:snapshot] = false
128
+ elsif options[:snapshot].nil?
129
+ options[:snapshot] = true
130
+ end
131
+
132
+ # --merge-master 未显式指定时,按是否SNAPSHOT推导: SNAPSHOT包不合并,正式包合并
133
+ options[:merge_master] = !options[:snapshot] if options[:merge_master].nil?
134
+
135
+ options[:config] = resolve_config_path(options[:config])
136
+ yaml = load_yaml(options[:config])
137
+ config_dir = File.dirname(File.expand_path(options[:config]))
138
+ setup_token_env(yaml['gitee'])
139
+ load_extensions(yaml, config_dir)
140
+ profile = select_profile(yaml, options)
141
+ apply_component_overrides(options, yaml)
142
+
143
+ # 版本号解析依赖profile中的前缀配置,须在profile选定后进行
144
+ resolve_version(options, profile)
145
+ validate_options(options)
146
+
147
+ start_build(options, profile)
148
+ end
149
+
150
+ # 版本号解析与版本分支一致性校验:
151
+ # - 显式传 --version 且分支符合 <前缀>_<版本>/<前缀>-<版本>:二者必须一致,否则报错停止
152
+ # - 缺省 --version 且分支符合规则:从分支解析出版本号
153
+ # - 其余情况(如master/普通特性分支):仍要求显式传 --version
154
+ def resolve_version(options, profile)
155
+ prefix = profile.is_a?(Array) ? profile[1] : profile
156
+ prefix_value = prefix.is_a?(Hash) ? prefix['release_branch_prefix'] : nil
157
+ branch_version = Build::RepoModule.parse_version_from_branch(options[:branch], prefix_value)
158
+
159
+ if options[:version].to_s.strip.empty?
160
+ unless branch_version
161
+ fail "缺少 --version,且分支 '#{options[:branch]}' 不符合版本分支规则(形如 #{prefix_value || 'release'}_1.2.0 或 #{prefix_value || 'release'}-1.2.0;版本号不限段数并支持后缀,如 1.2.2.0、1.2.2-fix),无法自动解析版本号"
162
+ end
163
+ options[:version] = branch_version
164
+ Build.log.detail("未传 --version,从版本分支 #{options[:branch]} 解析出版本号 #{branch_version}")
165
+ elsif branch_version && branch_version != options[:version].strip
166
+ fail "分支 #{options[:branch]} 解析出版本号 #{branch_version},与传入的 --version #{options[:version]} 不一致,请保持相同或缺省 --version"
167
+ end
168
+ end
169
+
170
+ # build.yml 的 components 按组件名声明例外配置(仓名不符/podspec在子目录/一仓多组件/互斥subspec集成集合),
171
+ # 命令行显式参数优先级高于配置文件
172
+ def apply_component_overrides(options, yaml)
173
+ cfg = (yaml['components'] || {})[options[:pod].to_s]
174
+ if cfg.is_a?(Hash)
175
+ { source_url: 'source_url', binary_url: 'binary_url',
176
+ podspec_path: 'podspec_path', tag_prefix: 'tag_prefix' }.each do |opt_key, cfg_key|
177
+ cfg_value = cfg[cfg_key].to_s.strip
178
+ options[opt_key] = cfg_value if options[opt_key].to_s.strip.empty? && !cfg_value.empty?
179
+ end
180
+ # subspecs:CLI 数组优先,否则取配置(必须是非空字符串数组);规范化在 RepoModule 初始化统一处理
181
+ if options[:subspecs].nil? && cfg['subspecs'].is_a?(Array)
182
+ cfg_subspecs = cfg['subspecs'].map { |s| s.to_s.strip }.reject(&:empty?)
183
+ options[:subspecs] = cfg_subspecs unless cfg_subspecs.empty?
184
+ end
185
+ Build.log.detail("组件 #{options[:pod]} 命中 build.yml components 例外配置: #{cfg.keys.join('、')}") unless cfg.empty?
186
+ end
187
+
188
+ # tag前缀规范化:非空时统一补尾部'/',最终源码tag形如 Foo/1.0.0
189
+ prefix = options[:tag_prefix].to_s.strip
190
+ options[:tag_prefix] = prefix.empty? || prefix.end_with?('/') ? (prefix.empty? ? nil : prefix) : "#{prefix}/"
191
+ end
192
+
193
+ def default_options
194
+ {
195
+ config: DEFAULT_CONFIG_PATH,
196
+ branch: 'master',
197
+ # nil 表示用户未显式传 --snapshot,归一化阶段再按组件类型决定默认值
198
+ # (自研组件默认 true,三方库强制 false)
199
+ snapshot: nil,
200
+ snapshot_explicit: false,
201
+ device: true,
202
+ simulator: true,
203
+ # nil 表示用户未显式指定,解析后按 snapshot 推导(SNAPSHOT不合并/正式包合并)
204
+ merge_master: nil,
205
+ force_cover: false,
206
+ publish_shell: false,
207
+ fork_github: false,
208
+ # 显式指定组件源码/二进制仓地址,非空时跳过 url_template 的 %{pod} 拼接,
209
+ # 用于仓库命名不符合模板约定(拼接路径不正确)的个别组件
210
+ source_url: nil,
211
+ binary_url: nil,
212
+ # podspec相对源码仓根目录的路径(podspec不在根目录时使用);一仓多组件时源码tag前缀(如 Foo/)
213
+ podspec_path: nil,
214
+ tag_prefix: nil,
215
+ # 仅集成指定subspec(数组短名);nil=默认集成全部,用于互斥subspec组件(如FMDB)
216
+ subspecs: nil,
217
+ build_url: '',
218
+ profile: nil,
219
+ verbose: false
220
+ }
221
+ end
222
+
223
+ def build_parser(options)
224
+ OptionParser.new do |opts|
225
+ opts.banner = '用法: ruby setup.rb --pod 组件名 --version 版本号 [选项]'
226
+ opts.on('--pod NAME', '必填,组件名') { |v| options[:pod] = v }
227
+ opts.on('--version VER', '组件版本号(不带SNAPSHOT);使用版本分支(如release_1.2.0)时可缺省,自动从分支解析') { |v| options[:version] = v }
228
+ opts.on('--branch BRANCH', '源码仓库分支/tag,默认 master') { |v| options[:branch] = v }
229
+ opts.on('--snapshot true|false', '是否SNAPSHOT包,自研组件默认 true;fork-github 三方库流程固定 false,显式传 true 会报错') do |v|
230
+ options[:snapshot] = parse_bool(v, 'snapshot')
231
+ options[:snapshot_explicit] = true
232
+ end
233
+ opts.on('--device true|false', '是否构建真机包,默认 true') { |v| options[:device] = parse_bool(v, 'device') }
234
+ opts.on('--simulator true|false', '是否构建模拟器包,默认 true') { |v| options[:simulator] = parse_bool(v, 'simulator') }
235
+ opts.on('--merge-master true|false', '是否合并到master,默认跟随SNAPSHOT(SNAPSHOT包不合并,正式包合并)') { |v| options[:merge_master] = parse_bool(v, 'merge-master') }
236
+ opts.on('--force-cover true|false', '是否强制覆盖发布,默认 false') { |v| options[:force_cover] = parse_bool(v, 'force-cover') }
237
+ opts.on('--publish-shell true|false', '是否发布到壳工程,默认 false') { |v| options[:publish_shell] = parse_bool(v, 'publish-shell') }
238
+ opts.on('--fork-github', '三方库GitHub fork模式:自动解析podspec并fork上游源码') { options[:fork_github] = true }
239
+ opts.on('--source-url URL', '显式指定组件源码仓地址,覆盖 source_module.url_template 拼接(用于命名不符合模板的组件/一仓多组件)') { |v| options[:source_url] = v }
240
+ opts.on('--binary-url URL', '显式指定组件二进制仓地址,覆盖 binary_module.url_template 拼接(用于命名不符合模板的组件)') { |v| options[:binary_url] = v }
241
+ opts.on('--podspec-path PATH', 'podspec相对源码仓根目录的路径(如 SubDir/Foo.podspec);不传时根目录约定优先,找不到自动递归扫描,歧义时报错') { |v| options[:podspec_path] = v }
242
+ opts.on('--tag-prefix PREFIX', '一仓多组件时的源码tag前缀(如 Foo/,最终tag为 Foo/1.0.0),默认无前缀') { |v| options[:tag_prefix] = v }
243
+ opts.on('--subspecs A,B', '仅集成指定subspec(逗号分隔短名,如 Core,SQLCipher),用于互斥subspec组件;默认集成全部') do |v|
244
+ subspecs = v.to_s.split(/[,.,、]/).map { |s| s.strip }.reject(&:empty?)
245
+ options[:subspecs] = subspecs unless subspecs.empty?
246
+ end
247
+ opts.on('--profile NAME', '使用配置文件中的指定profile,默认按模式自动选择') { |v| options[:profile] = v }
248
+ opts.on('--build-url URL', '构建地址(用于通知文案跳转,Jenkins作业/web平台触发时各自传入)', ) { |v| options[:build_url] = v }
249
+ opts.on('--config PATH', "环境配置文件,默认 #{DEFAULT_CONFIG_PATH}") { |v| options[:config] = v }
250
+ opts.on('--verbose', '输出调试细节(git命令、网络响应等),也可用环境变量 POD_BINARY_PACK_VERBOSE=1') { options[:verbose] = true }
251
+ opts.on('--init', '在 --config 指定位置生成配置模板并退出') { options[:init] = true }
252
+ opts.on('-h', '--help', '查看帮助') { options[:help] = true }
253
+ end
254
+ end
255
+
256
+ def validate_options(options)
257
+ missing = []
258
+ missing << '--pod' if options[:pod].to_s.empty?
259
+ missing << '--version' if options[:version].to_s.empty?
260
+ if !missing.empty?
261
+ Build.log.error("缺少必填参数: #{missing.join('、')}\n\n")
262
+ puts build_parser(options)
263
+ exit(-1)
264
+ end
265
+
266
+ fail 'SNAPSHOT包发布不能同时选择合并到master分支(--snapshot=true 时 --merge-master 必须为 false)' if options[:snapshot] && options[:merge_master]
267
+ fail '版本号里不能带SNAPSHOT,SNAPSHOT请通过 --snapshot true 指定' if options[:version].include?('SNAPSHOT')
268
+ end
269
+
270
+ def write_template(path)
271
+ fail "配置文件已存在,为避免覆盖请先手动处理: #{path}" if File.exist?(path)
272
+ FileUtils.mkdir_p(File.dirname(File.expand_path(path)))
273
+ File.write(path, CONFIG_TEMPLATE)
274
+ Build.log.success("已生成配置模板: #{File.expand_path(path)}")
275
+ Build.log.info(' 请填写各仓库地址(尤其是 shell.url)后重新执行')
276
+ end
277
+
278
+ def resolve_config_path(path)
279
+ # 使用默认配置且当前目录找不到时,兼容在上一级目录(如仓库内的 test/ 子目录)运行
280
+ if path == DEFAULT_CONFIG_PATH && !File.exist?(path) && File.exist?(File.join('..', DEFAULT_CONFIG_PATH))
281
+ File.join('..', DEFAULT_CONFIG_PATH)
282
+ else
283
+ path
284
+ end
285
+ end
286
+
287
+ def load_yaml(path)
288
+ fail "环境配置文件不存在: #{path},可先执行 ruby setup.rb --init 生成模板" unless File.exist?(path)
289
+ YAML.load_file(path) || {}
290
+ end
291
+
292
+ def setup_token_env(gitee)
293
+ return unless gitee.is_a?(Hash)
294
+ ENV['GITEE_ACCESS_TOKEN'] = gitee['access_token'].to_s unless gitee['access_token'].to_s.strip.empty?
295
+ ENV['GITEE_API_URL'] = gitee['endpoint'].to_s unless gitee['endpoint'].to_s.strip.empty?
296
+ end
297
+
298
+ def load_extensions(yaml, config_dir)
299
+ hooks_file = yaml['hooks_file'].to_s
300
+ unless hooks_file.empty?
301
+ require File.expand_path(hooks_file, config_dir)
302
+ Build.log.detail("已加载钩子文件: #{hooks_file}")
303
+ end
304
+
305
+ @sender = nil
306
+ sender_file = yaml['sender_file'].to_s
307
+ unless sender_file.empty?
308
+ require File.expand_path(sender_file, config_dir)
309
+ @sender = Object.const_get('Send').new
310
+ Build.log.detail("已加载通知器: #{sender_file}")
311
+ end
312
+ end
313
+
314
+ def select_profile(yaml, options)
315
+ name = options[:profile] || (options[:fork_github] ? 'fork_github' : 'default')
316
+ profiles = yaml['profiles'] || {}
317
+ profile = profiles[name]
318
+ fail "配置文件中缺少 profile: #{name}" unless profile.is_a?(Hash)
319
+ [name, profile]
320
+ end
321
+
322
+ def repo_url(profile, key)
323
+ repo = (profile['repos'] || {})[key]
324
+ fail "配置文件 profile(#{profile_name_current}) 缺少 repos.#{key}" unless repo.is_a?(Hash)
325
+ url = repo['url'].to_s
326
+ fail "请先在配置文件中填写 repos.#{key}.url" if url.strip.empty?
327
+ repo
328
+ end
329
+
330
+ def repo_template(profile, key)
331
+ repo = (profile['repos'] || {})[key]
332
+ fail "配置文件 profile(#{profile_name_current}) 缺少 repos.#{key}" unless repo.is_a?(Hash)
333
+ template = repo['url_template'].to_s
334
+ fail "请先在配置文件中填写 repos.#{key}.url_template" if template.strip.empty?
335
+ repo
336
+ end
337
+
338
+ def profile_name_current
339
+ @current_profile_name || 'default'
340
+ end
341
+
342
+ # 解析组件仓地址:命令行显式地址优先(用于命名不符合 url_template 约定的组件),
343
+ # 未传时按模板用组件名拼接
344
+ def resolve_module_url(explicit_url, template, pod_name, option_name)
345
+ url = explicit_url.to_s.strip
346
+ if url.empty?
347
+ fail "请在配置文件填写 url_template,或通过 --#{option_name} 显式指定仓库地址" if template.to_s.strip.empty?
348
+ format(template, pod: pod_name)
349
+ else
350
+ Build.log.detail("参数 --#{option_name} 显式指定仓库地址,跳过 url_template 拼接: #{url}")
351
+ url
352
+ end
353
+ end
354
+
355
+ def start_build(options, profile_tuple)
356
+ @current_profile_name, profile = profile_tuple
357
+ fork_mode = options[:fork_github] || profile['mode'] == 'fork_github'
358
+
359
+ cocoapods_source_cfg = repo_url(profile, 'cocoapods_source')
360
+ cocoapods_binary_cfg = repo_url(profile, 'cocoapods_binary')
361
+ shell_cfg = repo_url(profile, 'shell')
362
+ source_cfg = repo_template(profile, 'source_module')
363
+ binary_cfg = repo_template(profile, 'binary_module')
364
+
365
+ shell_workspace = shell_cfg['workspace'].to_s.strip
366
+ shell_scheme = shell_cfg['scheme'].to_s.strip
367
+ fail '请在配置文件中填写 repos.shell.workspace(壳工程 xcworkspace 名称)' if shell_workspace.empty?
368
+ fail '请在配置文件中填写 repos.shell.scheme(壳工程打包 scheme 名称)' if shell_scheme.empty?
369
+
370
+ config = Build::Config.instance
371
+ config.build_url = options[:build_url]
372
+
373
+ repo_cocoapods_source = Build::RepoCocoapods.new(
374
+ config.cocoapods_source_path,
375
+ cocoapods_source_cfg['url'],
376
+ cocoapods_source_cfg['branch'] || 'master'
377
+ )
378
+ repo_cocoapods_binary = Build::RepoCocoapods.new(
379
+ config.cocoapods_binary_path,
380
+ cocoapods_binary_cfg['url'],
381
+ cocoapods_binary_cfg['branch'] || 'master'
382
+ )
383
+ auto_build = Build::AutoBuild.new(config, repo_cocoapods_source, repo_cocoapods_binary)
384
+
385
+ pod_name = options[:pod]
386
+ pod_version = options[:version]
387
+
388
+ if fork_mode
389
+ github_helper = Build::GithubHelper.instance
390
+ github_helper.request_podspec(config.podspec_github_path, pod_name, pod_version)
391
+ source_repo_url = resolve_module_url(options[:source_url], source_cfg['url_template'], pod_name, 'source-url')
392
+ source_repo_branch = github_helper.github_tag
393
+ # 内部"三方库处理"标记(不锁分支/不改版本tag等),仅 fork-github 流程启用
394
+ third = 'true'
395
+ fork = true
396
+ snapshot = false
397
+ merge_master = false
398
+ Build.log.detail("三方库fork模式:源码 #{source_repo_url} 分支/tag #{source_repo_branch}")
399
+ else
400
+ source_repo_url = resolve_module_url(options[:source_url], source_cfg['url_template'], pod_name, 'source-url')
401
+ source_repo_branch = options[:branch]
402
+ third = 'false'
403
+ fork = false
404
+ snapshot = options[:snapshot]
405
+ merge_master = options[:merge_master]
406
+ end
407
+
408
+ binary_repo_url = resolve_module_url(options[:binary_url], binary_cfg['url_template'], pod_name, 'binary-url')
409
+ binary_repo_branch = binary_cfg['branch'] || 'master'
410
+
411
+ repo_shell = Build::RepoXCodeShell.new(
412
+ config.xcode_shell_path, shell_cfg['url'], shell_cfg['branch'] || 'master',
413
+ shell_workspace, shell_scheme,
414
+ code_signing: shell_cfg['code_signing'] == true,
415
+ development_team: shell_cfg['development_team'].to_s.strip.empty? ? nil : shell_cfg['development_team'].to_s.strip
416
+ )
417
+ build_item = Build::BuildItem.new(
418
+ snapshot,
419
+ options[:device],
420
+ options[:simulator],
421
+ merge_master,
422
+ options[:force_cover],
423
+ options[:publish_shell],
424
+ fork
425
+ )
426
+ release_prefix = profile.is_a?(Hash) ? profile['release_branch_prefix'] : nil
427
+ repo_module_source = Build::RepoModuleSource.new(
428
+ config.module_source_path, source_repo_url, source_repo_branch,
429
+ pod_name, pod_version, third, build_item,
430
+ podspec_rel_path: options[:podspec_path], tag_prefix: options[:tag_prefix],
431
+ release_branch_prefix: release_prefix,
432
+ build_subspecs: options[:subspecs]
433
+ )
434
+ repo_module_binary = Build::RepoModuleBinary.new(
435
+ config.module_binary_path, binary_repo_url, binary_repo_branch,
436
+ pod_name, pod_version, third, build_item
437
+ )
438
+
439
+ auto_build.run(repo_shell, repo_module_source, repo_module_binary, build_item, @sender)
440
+ end
441
+ end
442
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ module Build
3
+ class Commit
4
+ # @return [String] 最新的提交ID
5
+ attr_accessor :commit_id
6
+
7
+ # @return [String] 最新的提交者
8
+ attr_accessor :committer
9
+
10
+ # @return [String] 最新的提交者邮箱
11
+ attr_accessor :commit_mail
12
+
13
+ # @return [String] 最新的提交信息
14
+ attr_accessor :commit_msg
15
+
16
+ # @return [String] 最新的提交时间
17
+ attr_accessor :commit_time
18
+ end
19
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ require 'singleton'
3
+
4
+ module Build
5
+ class Config
6
+ include Singleton
7
+
8
+ # @return [String] 构建地址(通知文案跳转用,由触发方传入:Jenkins作业/web平台构建详情页)
9
+ attr_accessor :build_url
10
+
11
+ def initialize
12
+ super
13
+ init_workspace
14
+ end
15
+
16
+ def work_dir
17
+ FileUtils.pwd + "/BuildWorkspace"
18
+ end
19
+
20
+ # 壳工程(同时承担打包,全流程只 clone 一次)
21
+ def xcode_shell_path
22
+ "#{work_dir}/XcodeShell"
23
+ end
24
+
25
+ def cocoapods_source_path
26
+ "#{work_dir}/CocoapodsSource"
27
+ end
28
+
29
+ def cocoapods_binary_path
30
+ "#{work_dir}/CocoapodsBinary"
31
+ end
32
+
33
+ def module_source_path
34
+ "#{work_dir}/ModuleSource"
35
+ end
36
+
37
+ def module_binary_path
38
+ "#{work_dir}/ModuleBinary"
39
+ end
40
+
41
+ def podspec_github_path
42
+ "#{work_dir}/PodspecGithub"
43
+ end
44
+
45
+ def module_github_path
46
+ "#{work_dir}/ModuleGithub"
47
+ end
48
+
49
+ def init_workspace
50
+ Build.log.step("准备工作目录:#{work_dir}")
51
+ FileUtils.rm_rf(work_dir)
52
+ Dir.mkdir(work_dir)
53
+ end
54
+
55
+ end
56
+ end
data/lib/core/cost.rb ADDED
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+ module Build
3
+
4
+ class Cost
5
+ # @return [Integer] 耗时 单位秒
6
+ attr_accessor :duration
7
+
8
+ # @return [String] 描述
9
+ attr_accessor :description
10
+
11
+ def initialize(description, duration)
12
+ @description = description
13
+ @duration= duration
14
+ end
15
+
16
+ end
17
+
18
+ class Log
19
+
20
+ attr_accessor :cost_array
21
+
22
+ attr_accessor :last_time
23
+
24
+ attr_accessor :start_time
25
+
26
+ # @return [Integer] 总体耗费时间
27
+ attr_accessor :cost_time
28
+
29
+ def initialize
30
+ @cost_array = Array.new
31
+ @start_time = Time.now.getutc.to_i
32
+ @last_time = Time.now.getutc.to_i
33
+ end
34
+
35
+ # 添加一条耗时
36
+ def record_time(description)
37
+
38
+ current_time = Time.now.getutc.to_i
39
+ duration = current_time - last_time
40
+
41
+ cost = Cost.new(description, duration)
42
+ cost_array << cost
43
+
44
+ @last_time = current_time
45
+
46
+ end
47
+
48
+ def end_record_time(description)
49
+ current_time = Time.now.getutc.to_i
50
+ duration = current_time - start_time
51
+ @cost_time = duration
52
+
53
+ cost = Cost.new(description, duration)
54
+ cost_array << cost
55
+
56
+ @last_time = current_time
57
+ end
58
+
59
+ # 打印耗时
60
+ def print_cost
61
+ Build.log.step("打印各阶段耗时:")
62
+ cost_array.each do |cost|
63
+ Build.log.info("#{cost.description}耗时: #{cost.duration}秒")
64
+ end
65
+ end
66
+
67
+ end
68
+ end