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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +147 -0
- data/exe/pod-binary-pack +6 -0
- data/lib/core/auto_build.rb +299 -0
- data/lib/core/build_item.rb +69 -0
- data/lib/core/cli.rb +442 -0
- data/lib/core/commit.rb +19 -0
- data/lib/core/config.rb +56 -0
- data/lib/core/cost.rb +68 -0
- data/lib/core/git_helper.rb +222 -0
- data/lib/core/github_fork.rb +425 -0
- data/lib/core/github_helper.rb +334 -0
- data/lib/core/hook_manager.rb +32 -0
- data/lib/core/hosting/gitee_client.rb +114 -0
- data/lib/core/hosting/gitee_provider.rb +312 -0
- data/lib/core/hosting/gitlab_provider.rb +342 -0
- data/lib/core/hosting/provider.rb +96 -0
- data/lib/core/hosting.rb +43 -0
- data/lib/core/logger.rb +66 -0
- data/lib/core/podspec.rb +126 -0
- data/lib/core/push.rb +80 -0
- data/lib/core/repo.rb +284 -0
- data/lib/core/repo_cocoapods.rb +64 -0
- data/lib/core/repo_module.rb +281 -0
- data/lib/core/repo_module_binary.rb +439 -0
- data/lib/core/repo_module_source.rb +506 -0
- data/lib/core/repo_xcode.rb +41 -0
- data/lib/core/repo_xcode_package.rb +713 -0
- data/lib/core/repo_xcode_shell.rb +170 -0
- data/lib/core/util/file_helper.rb +100 -0
- data/lib/core/util/string.rb +35 -0
- data/lib/pod/binary_pack/version.rb +7 -0
- data/lib/pod/binary_pack.rb +9 -0
- metadata +144 -0
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
|
data/lib/core/commit.rb
ADDED
|
@@ -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
|
data/lib/core/config.rb
ADDED
|
@@ -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
|