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
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'shellwords'
|
|
5
|
+
require 'pathname'
|
|
6
|
+
require_relative 'podspec'
|
|
7
|
+
require_relative 'repo_module'
|
|
8
|
+
require_relative 'github_helper'
|
|
9
|
+
require_relative 'util/string'
|
|
10
|
+
require_relative 'util/file_helper'
|
|
11
|
+
|
|
12
|
+
module Build
|
|
13
|
+
class RepoModuleSource < RepoModule
|
|
14
|
+
# podspec未声明Swift版本时补全的默认版本:部分含Swift的三方库(如LookinServer)root未声明
|
|
15
|
+
# swift_version,纯OC壳工程target又未设SWIFT_VERSION,不补会导致 pod install 报
|
|
16
|
+
# "Unable to determine Swift version";Swift 5 语法在 Xcode 16/27 均可编译
|
|
17
|
+
DEFAULT_SWIFT_VERSION = '5.0'.freeze
|
|
18
|
+
|
|
19
|
+
# 最低支持版本
|
|
20
|
+
attr_accessor :ios_version
|
|
21
|
+
|
|
22
|
+
# 上次发布该版本时podspec.json中记录的commitID(无历史版本时为nil)
|
|
23
|
+
attr_accessor :last_released_commit_id
|
|
24
|
+
|
|
25
|
+
# 源码仓库的校验逻辑
|
|
26
|
+
def validate
|
|
27
|
+
super
|
|
28
|
+
|
|
29
|
+
branch_version = self.class.parse_version_from_branch(branch, release_branch_prefix)
|
|
30
|
+
|
|
31
|
+
# 分支是版本分支(前缀可配置):分支里的版本号必须与打包版本号一致
|
|
32
|
+
if branch_version
|
|
33
|
+
if branch_version != pod_version
|
|
34
|
+
error = "版本分支#{branch}解析出版本号#{branch_version},与传入的打包版本号#{pod_version}不一致,请保持二者相同(或直接缺省--version由分支解析)"
|
|
35
|
+
pusher.push("❌ 打包失败", error)
|
|
36
|
+
Process.exit(-1)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 标准发布版本号(纯数字二/三/四段式,如 1.2 / 1.2.0 / 1.2.2.0)只能在
|
|
41
|
+
# master 分支或版本分支(<定制前缀>_x.y.z)上打包;
|
|
42
|
+
# 非 master、非版本前缀的普通特性分支不允许打标准版本号。
|
|
43
|
+
# 单段、五段及以上、带后缀的版本(如 1.2.2-fix / 1.2.0-beta.1)不属于标准发布版本,不做此约束
|
|
44
|
+
if self.branch != "master" and self.branch != self.pod_version and not self.third #特殊处理
|
|
45
|
+
if standard_release_version?(pod_version) and branch_version.nil?
|
|
46
|
+
error = "标准版本号(#{pod_version},二段/三段/四段式)只能在 master 分支或版本分支上打包," \
|
|
47
|
+
"请将分支命名为 #{release_branch_prefix}_#{pod_version} 或 #{release_branch_prefix}-#{pod_version}(前缀可在 build.yml 配置)," \
|
|
48
|
+
"当前分支 #{self.branch} 不符合规则;带后缀的版本号(如 1.2.0-fix)不受此限制"
|
|
49
|
+
pusher.push("❌ 打包失败", error)
|
|
50
|
+
Process.exit(-1)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 是否为标准发布版本号:纯数字且恰为 2~4 段(1.2 / 1.2.3 / 1.2.3.4)
|
|
57
|
+
def standard_release_version?(version)
|
|
58
|
+
version.to_s.match?(/\A\d+\.\d+(?:\.\d+){0,2}\z/)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @api 锁死分支
|
|
62
|
+
def lock_branch_if_need
|
|
63
|
+
if build_item.snapshot
|
|
64
|
+
Build.log.info("当前源码仓库为SNAPSHOT包,无需锁死分支")
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if third
|
|
69
|
+
Build.log.info("当前打包为三方库,无需锁死分支")
|
|
70
|
+
return
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
unless hosting.available?
|
|
74
|
+
Build.log.info("当前组件仓库非受支持的代码托管平台, 跳过锁死分支, url = #{ssh_url}")
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless repo_info
|
|
79
|
+
Build.log.info("未查询到仓库信息,跳过锁死分支 #{branch}")
|
|
80
|
+
return
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Build.log.info("当前源码仓库(#{repo_info.full_name})打包模式为正式包,需锁死分支 #{branch}")
|
|
84
|
+
setting_url = hosting.lock_branch(repo_info, branch, page_url)
|
|
85
|
+
|
|
86
|
+
# 推送
|
|
87
|
+
pusher.push("【✅组件分支已锁死无法提交】", "配置地址 #{setting_url}")
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
# @api force_cover这个字段的检测
|
|
91
|
+
def force_cover_validate(repo_cocoapods_source)
|
|
92
|
+
|
|
93
|
+
# http归档git化组件:每次构建基于官方归档重新生成单提交历史,commit不延续,
|
|
94
|
+
# 上次发布的commit不可能包含在本次历史中,包含校验对这类组件无意义
|
|
95
|
+
if Build::GithubHelper.instance.http_source
|
|
96
|
+
Build.log.info("不校验commit信息是否包含上次,原因:http归档组件每次按官方归档重建,commit不延续历史")
|
|
97
|
+
return
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# 强制覆盖的话 则不需要commit校验
|
|
101
|
+
if build_item.force_cover
|
|
102
|
+
Build.log.info("不校验commit信息是否包含上次,原因:force_cover = false")
|
|
103
|
+
return
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# 非受支持平台地址 不校验
|
|
107
|
+
unless hosting.available?
|
|
108
|
+
Build.log.info("不校验commit信息是否包含上次,原因:当前组件仓库非受支持的代码托管平台, url = #{ssh_url}")
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# 未查询到仓库信息 不校验
|
|
113
|
+
unless repo_info
|
|
114
|
+
Build.log.info("不校验commit信息是否包含上次,原因:未查询到当前组件的仓库信息")
|
|
115
|
+
return
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# 判断cocoapods_source里文件夹是否存在
|
|
119
|
+
pod_name_path = "#{repo_cocoapods_source.path}/#{pod_name}"
|
|
120
|
+
unless File.directory?(pod_name_path)
|
|
121
|
+
Build.log.info("不校验commit信息是否包含上次,原因:repo_cocoapods_source没有当前组件文件夹")
|
|
122
|
+
return
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# 判断json文件是否存在
|
|
126
|
+
pod_version_path = "#{pod_name_path}/#{pod_version_final}"
|
|
127
|
+
unless File.directory?(pod_version_path)
|
|
128
|
+
Build.log.info("不校验commit信息是否包含上次,原因:repo_cocoapods_source组件文件夹没有当前版本文件夹")
|
|
129
|
+
return
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
podspec_json_path = "#{pod_version_path}/#{pod_name}.podspec.json"
|
|
133
|
+
# 文件不存在 不校验了
|
|
134
|
+
unless File.exist?(podspec_json_path)
|
|
135
|
+
Build.log.info("不校验commit信息是否包含上次,原因:repo_cocoapods_source组件文件夹没有podspec.json文件")
|
|
136
|
+
return
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
json = File.read(podspec_json_path)
|
|
140
|
+
podspec_content = JSON.parse(json)
|
|
141
|
+
# 扩展信息统一读取 build_metadata 嵌套字段
|
|
142
|
+
metadata = podspec_content[PODSPEC_METADATA_KEY] || {}
|
|
143
|
+
commit_id = metadata["commit_id"]
|
|
144
|
+
committer = metadata["committer"]
|
|
145
|
+
commit_msg = metadata["commit_message"]
|
|
146
|
+
commit_time = metadata["commit_time"]
|
|
147
|
+
unless commit_id
|
|
148
|
+
Build.log.info("不校验commit信息是否包含上次,原因:repo_cocoapods_source的podspec.json build_metadata.commit_id为空")
|
|
149
|
+
return
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# 记录上次发布的commit,供后续判断"本次是否有新提交"
|
|
153
|
+
@last_released_commit_id = commit_id
|
|
154
|
+
|
|
155
|
+
# 校验
|
|
156
|
+
contain = self.hosting.branch_contain_commit?(repo_info, branch, commit_id)
|
|
157
|
+
Build.log.step("(#{File.basename(self.path)})上次发布的commit信息:")
|
|
158
|
+
Build.log.detail("#{commit_id} #{commit_msg} #{commit_time}")
|
|
159
|
+
if contain
|
|
160
|
+
Build.log.detail("校验上次发布 commit 包含关系:通过")
|
|
161
|
+
else
|
|
162
|
+
error = "当前发布分支#{branch}不包含上次发布的#{committer}提交, 请先合并分支再打包或手动选择强制覆盖"
|
|
163
|
+
pusher.push("❌ 打包失败", error)
|
|
164
|
+
Process.exit(-1)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# 本次打包的源码是否与上次发布完全一致(当前HEAD就是上次发布的commit)。
|
|
169
|
+
# 一致时源码仓/二进制仓的版本tag无需删除后重新推送(tag仍指向同一个commit)。
|
|
170
|
+
# 三方库(走上游版本tag)、强制覆盖、无历史版本记录时不做该判定。
|
|
171
|
+
def no_new_commit_since_last_release?
|
|
172
|
+
return false if third
|
|
173
|
+
return false if build_item.force_cover
|
|
174
|
+
return false if last_released_commit_id.to_s.empty?
|
|
175
|
+
|
|
176
|
+
same_commit?(last_released_commit_id, last_commit.commit_id)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# 比较两个commit标识是否指向同一提交(兼容完整SHA与短SHA互相比较)
|
|
180
|
+
def same_commit?(sha_a, sha_b)
|
|
181
|
+
a = sha_a.to_s.strip.downcase
|
|
182
|
+
b = sha_b.to_s.strip.downcase
|
|
183
|
+
return false if a.empty? || b.empty? || a.length < 7 || b.length < 7
|
|
184
|
+
|
|
185
|
+
a == b || a.start_with?(b) || b.start_with?(a)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# podspec所在目录:子目录podspec时以该目录为文件匹配根(与CocoaPods :podspec 语义一致),
|
|
189
|
+
# 默认仍是仓库根目录。必须在 podspec 定位完成(update_podspec)后才有值
|
|
190
|
+
def component_root_dir
|
|
191
|
+
@source_podspec_entry ? File.dirname(@source_podspec_entry) : path
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# podspec是否位于仓库根目录:false时打包生成的LocalDependencies需用podspec:指向具体文件
|
|
195
|
+
def podspec_at_repo_root?
|
|
196
|
+
source_podspec_entry
|
|
197
|
+
File.dirname(@source_podspec_entry) == File.expand_path(path)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# podspec中全部subspec的短名(去掉root前缀),含嵌套subspec;无subspec时返回空数组。
|
|
201
|
+
# json中的name形如 "BSDebugPlugins/DLAM" / "Root/A/B",统一取最后一段
|
|
202
|
+
def podspec_subspec_names
|
|
203
|
+
collect_subspec_names(podspec_content && podspec_content["subspecs"])
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def collect_subspec_names(subspecs)
|
|
207
|
+
Array(subspecs).flat_map do |subspec|
|
|
208
|
+
name = subspec["name"].to_s.split("/").last
|
|
209
|
+
children = collect_subspec_names(subspec["subspecs"])
|
|
210
|
+
name.empty? ? children : [name] + children
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# 本次打包实际集成的subspec短名列表(须在podspec定位完成后调用):
|
|
215
|
+
# - 显式指定(build.yml components.<组件>.subspecs 或 --subspecs)时校验存在性后按指定值,
|
|
216
|
+
# 用于互斥subspec组件(如FMDB),全量合并编译必然冲突的场景
|
|
217
|
+
# - 未指定时默认集成podspec全部subspec
|
|
218
|
+
def resolved_build_subspecs
|
|
219
|
+
all = podspec_subspec_names
|
|
220
|
+
return all if all.empty? || build_subspecs.nil?
|
|
221
|
+
|
|
222
|
+
unknown = build_subspecs - all
|
|
223
|
+
unless unknown.empty?
|
|
224
|
+
error = "subspec配置指定了podspec中不存在的名称: #{unknown.join('、')};" \
|
|
225
|
+
"组件#{pod_name}可用的subspec: #{all.join('、')}"
|
|
226
|
+
pusher.push("❌ 打包失败", error)
|
|
227
|
+
Process.exit(-1)
|
|
228
|
+
end
|
|
229
|
+
build_subspecs
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# root及所有(含嵌套)subspec的扁平化列表
|
|
233
|
+
def all_podspec_specs
|
|
234
|
+
collect_specs(podspec_content)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def collect_specs(spec)
|
|
238
|
+
return [] unless spec.is_a?(Hash)
|
|
239
|
+
[spec] + Array(spec["subspecs"]).flat_map { |child| collect_specs(child) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# 全部resource_bundles的bundle名(root+subspecs,去重)
|
|
243
|
+
def all_resource_bundle_names
|
|
244
|
+
all_podspec_specs.flat_map do |spec|
|
|
245
|
+
bundles = spec["resource_bundles"] || spec["resource_bundle"]
|
|
246
|
+
bundles.is_a?(Hash) ? bundles.keys : []
|
|
247
|
+
end.uniq
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# 全部resources/resource声明的glob(root+subspecs,去重)
|
|
251
|
+
def all_resource_globs
|
|
252
|
+
all_podspec_specs.flat_map do |spec|
|
|
253
|
+
resources = spec["resources"] || spec["resource"]
|
|
254
|
+
case resources
|
|
255
|
+
when Array then resources
|
|
256
|
+
when nil then []
|
|
257
|
+
else [resources]
|
|
258
|
+
end
|
|
259
|
+
end.uniq
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# 定位源码仓内的podspec文件(.podspec或.podspec.json的绝对路径)
|
|
263
|
+
# 优先级: 显式配置(--podspec-path/build.yml) > 根目录<组件名>.podspec约定 > 递归自动扫描(唯一命中)
|
|
264
|
+
def source_podspec_entry
|
|
265
|
+
return @source_podspec_entry if @source_podspec_entry
|
|
266
|
+
|
|
267
|
+
root_podspec = File.join(path, "#{pod_name}.podspec")
|
|
268
|
+
if podspec_rel_path
|
|
269
|
+
explicit = File.expand_path(podspec_rel_path, path)
|
|
270
|
+
unless File.exist?(explicit)
|
|
271
|
+
error = "配置的podspec路径不存在: #{podspec_rel_path}(相对源码仓根目录),仓库: #{ssh_url}"
|
|
272
|
+
pusher.push("❌ 打包失败", error)
|
|
273
|
+
Process.exit(-1)
|
|
274
|
+
end
|
|
275
|
+
@source_podspec_entry = explicit
|
|
276
|
+
Build.log.detail("使用显式配置的podspec: #{podspec_rel_path}")
|
|
277
|
+
return @source_podspec_entry
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
if File.exist?(root_podspec)
|
|
281
|
+
@source_podspec_entry = root_podspec
|
|
282
|
+
return @source_podspec_entry
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# 自动扫描:兼容podspec不在根目录/一仓多组件,排除.git与Pods目录
|
|
286
|
+
candidates = Dir.glob(File.join(path, '**', "#{pod_name}.podspec{,.json}"))
|
|
287
|
+
.reject { |p| p.include?('/.git/') || p.include?('/Pods/') }
|
|
288
|
+
.sort_by { |p| p.end_with?('.podspec.json') ? 1 : 0 }
|
|
289
|
+
if candidates.size == 1
|
|
290
|
+
@source_podspec_entry = candidates.first
|
|
291
|
+
rel = Pathname.new(@source_podspec_entry).relative_path_from(Pathname.new(File.expand_path(path))).to_s
|
|
292
|
+
Build.log.detail("根目录未找到podspec,自动扫描命中: #{rel}")
|
|
293
|
+
return @source_podspec_entry
|
|
294
|
+
elsif candidates.size > 1
|
|
295
|
+
rel_list = candidates.map { |p| Pathname.new(p).relative_path_from(Pathname.new(File.expand_path(path))).to_s }
|
|
296
|
+
error = "扫描到多个#{pod_name}的podspec(#{rel_list.join('、')}),无法自动确定,请通过 --podspec-path 或 build.yml components.#{pod_name}.podspec_path 显式指定"
|
|
297
|
+
pusher.push("❌ 打包失败", error)
|
|
298
|
+
Process.exit(-1)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
error = "podspec文件不存在:源码仓根目录及子目录均未找到 #{pod_name}.podspec(.json),如podspec命名特殊请通过 --podspec-path 指定"
|
|
302
|
+
pusher.push("❌ 打包失败", error)
|
|
303
|
+
Process.exit(-1)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# @api podspec文件更新
|
|
307
|
+
def update_podspec
|
|
308
|
+
|
|
309
|
+
entry = source_podspec_entry
|
|
310
|
+
|
|
311
|
+
if entry.end_with?('.podspec.json')
|
|
312
|
+
# 仓库内本就是json podspec,直接使用不做转换
|
|
313
|
+
@podspec_json_path = entry
|
|
314
|
+
else
|
|
315
|
+
# .podspec 生成对应的json(生成后从工作副本删除.podspec,与既有行为一致)
|
|
316
|
+
@podspec_json_path = "#{entry}.json"
|
|
317
|
+
FileUtils.rm_f(@podspec_json_path)
|
|
318
|
+
command = "pod ipc spec #{Shellwords.escape(entry)} >> #{Shellwords.escape(podspec_json_path)}"
|
|
319
|
+
res = system command
|
|
320
|
+
unless res == true
|
|
321
|
+
error = "命令执行失败: #{command}"
|
|
322
|
+
pusher.push("❌ 打包失败", error)
|
|
323
|
+
Process.exit(-1)
|
|
324
|
+
end
|
|
325
|
+
FileUtils.rm_rf(entry)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
unless File.exist?(podspec_json_path)
|
|
329
|
+
error = "podspec文件不存在"
|
|
330
|
+
pusher.push("❌ 打包失败", error)
|
|
331
|
+
Process.exit(-1)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# 处理json文件
|
|
335
|
+
podspec = Podspec.new(podspec_json_path)
|
|
336
|
+
podspec_content = podspec.podspec_content
|
|
337
|
+
|
|
338
|
+
# 显式指定集成subspec(build.yml components.<组件>.subspecs 或 --subspecs)时,
|
|
339
|
+
# 把本地podspec同步剪裁为该集合后再写回:
|
|
340
|
+
# - 互斥subspec组件(如FMDB)即使声明:subspecs,CocoaPods仍会激活default_subspecs
|
|
341
|
+
# 指向的subspec(如standard的libraries: sqlite3),把系统libsqlite3混入
|
|
342
|
+
# SQLCipher加密版产物的链接,必须从spec源头剪掉
|
|
343
|
+
# - default_subspecs指向被剪subspec时一并移除,默认=保留的全部subspec
|
|
344
|
+
removed_subspec_names = []
|
|
345
|
+
if build_subspecs.is_a?(Array) && podspec_content["subspecs"].is_a?(Array)
|
|
346
|
+
all_names = collect_subspec_names(podspec_content["subspecs"])
|
|
347
|
+
unknown = build_subspecs - all_names
|
|
348
|
+
unless unknown.empty?
|
|
349
|
+
error = "subspec配置指定了podspec中不存在的名称: #{unknown.join('、')};" \
|
|
350
|
+
"组件#{pod_name}可用的subspec: #{all_names.join('、')}"
|
|
351
|
+
pusher.push("❌ 打包失败", error)
|
|
352
|
+
Process.exit(-1)
|
|
353
|
+
end
|
|
354
|
+
kept = podspec_content["subspecs"].select do |subspec|
|
|
355
|
+
names = [subspec["name"].to_s.split("/").last] + collect_subspec_names(subspec["subspecs"])
|
|
356
|
+
(names & build_subspecs).any?
|
|
357
|
+
end
|
|
358
|
+
if kept.size < podspec_content["subspecs"].size
|
|
359
|
+
removed_subspec_names = (podspec_content["subspecs"] - kept).map { |s| s["name"] }
|
|
360
|
+
Build.log.detail("本地podspec按配置剪掉未集成的subspec: #{removed_subspec_names.join('、')}")
|
|
361
|
+
podspec_content["subspecs"] = kept
|
|
362
|
+
defaults = case podspec_content["default_subspecs"]
|
|
363
|
+
when String then podspec_content["default_subspecs"].split(",").map { |d| d.to_s.split("/").last.strip }
|
|
364
|
+
when Array then podspec_content["default_subspecs"].map { |d| d.to_s.split("/").last.strip }
|
|
365
|
+
else []
|
|
366
|
+
end.reject(&:empty?)
|
|
367
|
+
podspec_content.delete("default_subspecs") unless defaults.empty? || (defaults - build_subspecs).empty?
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
podspec_content.delete("license")
|
|
372
|
+
|
|
373
|
+
# source改写:
|
|
374
|
+
# - http归档git化组件(克隆内podspec仍带source.http):整体替换为镜像仓git+tag引用,
|
|
375
|
+
# 避免http与git两种来源并存导致CocoaPods解析冲突;tag即git化时在镜像仓打的版本tag
|
|
376
|
+
# - git源组件:替换git地址;非三方库再补version/tag(三方库沿用上游tag)
|
|
377
|
+
if podspec_content["source"].is_a?(Hash) && podspec_content["source"]["http"]
|
|
378
|
+
podspec_content["source"] = {"git" => ssh_url, "tag" => version_tag}
|
|
379
|
+
Build.log.detail("http归档组件source已改写为镜像仓引用: #{ssh_url} (tag: #{version_tag})")
|
|
380
|
+
else
|
|
381
|
+
podspec_content["source"]["git"] = ssh_url
|
|
382
|
+
|
|
383
|
+
unless self.third
|
|
384
|
+
podspec_content["version"] = pod_version_final
|
|
385
|
+
# 一仓多组件时tag带组件名前缀(Foo/1.0.0),默认无前缀
|
|
386
|
+
podspec_content["source"]["tag"] = version_tag
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# 含Swift源码但未声明Swift版本的podspec补全默认版本,避免纯OC壳工程 pod install 失败
|
|
391
|
+
ensure_swift_version(podspec_content)
|
|
392
|
+
|
|
393
|
+
# 处理有些库因为缺少配置导致的编译问题
|
|
394
|
+
if pod_name == "CocoaHTTPServer"
|
|
395
|
+
podspec_content["pod_target_xcconfig"] = {'GCC_PREPROCESSOR_DEFINITIONS' => ['$(inherited)', 'DD_LEGACY_MACROS=1']}
|
|
396
|
+
elsif pod_name == "Protobuf"
|
|
397
|
+
podspec_content["pod_target_xcconfig"] = {'GCC_PREPROCESSOR_DEFINITIONS' => ['$(inherited)', 'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1']}
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# 处理MLeaksFinder公开头文件
|
|
401
|
+
if pod_name == "MLeaksFinder"
|
|
402
|
+
podspec_content["public_header_files"] << "MLeaksFinder/MLeaksMessenger.h"
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# 工具生成的扩展信息统一收敛到 build_metadata 字段,
|
|
406
|
+
# 嵌套对象会被 CocoaPods 整体忽略,避免自定义key污染/冲突官方podspec属性
|
|
407
|
+
metadata = {
|
|
408
|
+
"address" => http_url,
|
|
409
|
+
"committer" => last_commit.committer,
|
|
410
|
+
"commit_id" => last_commit.commit_id,
|
|
411
|
+
"commit_message" => last_commit.commit_msg,
|
|
412
|
+
"commit_time" => last_commit.commit_time,
|
|
413
|
+
"commit_email" => last_commit.commit_mail,
|
|
414
|
+
"tool_version" => Pod::BinaryPack::VERSION,
|
|
415
|
+
"note" => "这是一份自动生成的文件, 请勿手动修改"
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
# http归档git化组件补充归档溯源信息(原始归档地址与实际计算的sha256)
|
|
419
|
+
github_helper = Build::GithubHelper.instance
|
|
420
|
+
if github_helper.http_source
|
|
421
|
+
metadata["archive_url"] = github_helper.archive_url
|
|
422
|
+
metadata["archive_sha256"] = github_helper.archive_sha_actual if github_helper.archive_sha_actual
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# 与官方原始podspec存在subspec集合差异(互斥subspec按配置剪裁)时显式说明,
|
|
426
|
+
# 该字段会随podspec_content一并带入二进制spec的build_metadata
|
|
427
|
+
if removed_subspec_names.any?
|
|
428
|
+
metadata["subspec_note"] = "该组件subspec存在互斥关系,本spec与官方原始podspec存在差异:" \
|
|
429
|
+
"按配置仅集成 #{build_subspecs.join('、')},剪掉 #{removed_subspec_names.join('、')}," \
|
|
430
|
+
"default_subspecs 已同步调整;如需其他subspec组合请调整 build.yml components.subspecs 后重新打包"
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
podspec_content[PODSPEC_METADATA_KEY] = metadata
|
|
434
|
+
|
|
435
|
+
# 更新属性
|
|
436
|
+
@podspec_content = podspec_content
|
|
437
|
+
__update_framework_name(podspec_content)
|
|
438
|
+
|
|
439
|
+
# 更新最低版本号
|
|
440
|
+
platforms = podspec_content["platforms"]
|
|
441
|
+
if platforms and platforms.class == Hash and platforms["ios"]
|
|
442
|
+
@ios_version = platforms["ios"]
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
## 重写文件
|
|
446
|
+
new_file = File.new(podspec_json_path, "w+")
|
|
447
|
+
if new_file
|
|
448
|
+
new_file.syswrite(JSON.pretty_generate(podspec_content))
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
private
|
|
453
|
+
|
|
454
|
+
# podspec本身未声明 swift_version/swift_versions,且root或任一(含嵌套)subspec确实包含
|
|
455
|
+
# .swift源文件时,补全默认Swift版本。纯OC组件/已声明版本的组件不改动
|
|
456
|
+
def ensure_swift_version(spec)
|
|
457
|
+
return if spec['swift_version'] || spec['swift_versions']
|
|
458
|
+
return unless contains_swift_sources?(spec)
|
|
459
|
+
|
|
460
|
+
spec['swift_version'] = DEFAULT_SWIFT_VERSION
|
|
461
|
+
Build.log.detail("podspec未声明swift_version/swift_versions,但root或subspec中检测到Swift源文件," \
|
|
462
|
+
"已自动补全 swift_version=#{DEFAULT_SWIFT_VERSION}(否则纯OC壳工程集成时pod install无法确定Swift版本)")
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# root及全部subspec的source_files中是否存在.swift文件
|
|
466
|
+
def contains_swift_sources?(spec)
|
|
467
|
+
collect_specs(spec).any? do |sub_spec|
|
|
468
|
+
Array(sub_spec['source_files']).any? do |pattern|
|
|
469
|
+
FileHelper.source_files_under(File.join(component_root_dir, pattern))
|
|
470
|
+
.any? { |file_path| File.extname(file_path).downcase == '.swift' }
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
def __update_framework_name(podspec_content)
|
|
476
|
+
# 如果指定了module_name 就取这个名字,否则就取pod_name作为framework_name
|
|
477
|
+
framework_name = podspec_content["module_name"]
|
|
478
|
+
header_dir = podspec_content["header_dir"]
|
|
479
|
+
if framework_name.nil?
|
|
480
|
+
# 如果没有,就取一下head_dir试试
|
|
481
|
+
framework_name = header_dir ? header_dir : pod_name
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# 解决pod库名字中+ 和- 被转成_的问题
|
|
485
|
+
framework_name = framework_name.gsub("-", "_")
|
|
486
|
+
framework_name = framework_name.gsub("+", "_")
|
|
487
|
+
|
|
488
|
+
@framework_name = framework_name
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
public def git_tag
|
|
492
|
+
# http归档组件:版本tag在fork阶段就地git化时已处理(镜像仓同名tag默认跳过/force_cover强制覆盖),
|
|
493
|
+
# 且克隆处于tag检出的detached状态,无法走"拉取+打tag"常规流程,直接跳过
|
|
494
|
+
return if Build::GithubHelper.instance.http_source
|
|
495
|
+
|
|
496
|
+
unless self.third
|
|
497
|
+
super
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
if self.third and build_item.force_cover
|
|
501
|
+
super
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative 'repo'
|
|
3
|
+
require 'xcodeproj'
|
|
4
|
+
|
|
5
|
+
module Build
|
|
6
|
+
class RepoXCode < Repo
|
|
7
|
+
attr_reader :xcworkspace
|
|
8
|
+
attr_reader :scheme
|
|
9
|
+
# @return [Boolean] archive 时是否启用代码签名
|
|
10
|
+
attr_reader :code_signing
|
|
11
|
+
# @return [String, nil] 签名团队ID(仅 code_signing=true 时生效)
|
|
12
|
+
attr_reader :development_team
|
|
13
|
+
|
|
14
|
+
def initialize(path, ssh_url, branch, xcworkspace = nil, scheme = nil,
|
|
15
|
+
code_signing: false, development_team: nil)
|
|
16
|
+
super(path, ssh_url, branch)
|
|
17
|
+
@xcworkspace = xcworkspace
|
|
18
|
+
@scheme = scheme
|
|
19
|
+
@code_signing = code_signing
|
|
20
|
+
@development_team = development_team
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def pod_update
|
|
24
|
+
current = FileUtils.pwd
|
|
25
|
+
Dir.chdir(self.path)
|
|
26
|
+
dir_name = File.basename(self.path)
|
|
27
|
+
|
|
28
|
+
command = "pod update"
|
|
29
|
+
Build.log.step("(#{dir_name})执行pod命令")
|
|
30
|
+
Build.log.debug(command)
|
|
31
|
+
res = system command
|
|
32
|
+
unless res == true
|
|
33
|
+
error = "pod命令执行失败: #{command}"
|
|
34
|
+
pusher.push("❌ 打包失败", error)
|
|
35
|
+
Process.exit(-1)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Dir.chdir(current)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|