fastlane-plugin-fastci 0.0.1
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/LICENSE +21 -0
- data/README.md +23 -0
- data/lib/fastlane/plugin/fastci/actions/analyze_swiftlint_action.rb +116 -0
- data/lib/fastlane/plugin/fastci/actions/detect_duplicity_code_action.rb +105 -0
- data/lib/fastlane/plugin/fastci/actions/detect_unused_code_action.rb +102 -0
- data/lib/fastlane/plugin/fastci/actions/detect_unused_image_action.rb +54 -0
- data/lib/fastlane/plugin/fastci/actions/install_certificate_action.rb +40 -0
- data/lib/fastlane/plugin/fastci/actions/install_profile_action.rb +37 -0
- data/lib/fastlane/plugin/fastci/actions/noti_dingding_action.rb +51 -0
- data/lib/fastlane/plugin/fastci/actions/package_action.rb +262 -0
- data/lib/fastlane/plugin/fastci/actions/update_build_number_action.rb +65 -0
- data/lib/fastlane/plugin/fastci/actions/upload_pgy_action.rb +47 -0
- data/lib/fastlane/plugin/fastci/actions/upload_store_action.rb +50 -0
- data/lib/fastlane/plugin/fastci/helper/common_helper.rb +108 -0
- data/lib/fastlane/plugin/fastci/helper/constants.rb +48 -0
- data/lib/fastlane/plugin/fastci/helper/environment.rb +86 -0
- data/lib/fastlane/plugin/fastci/version.rb +5 -0
- data/lib/fastlane/plugin/fastci.rb +16 -0
- data/lib/fastlane/plugin/python/generate_duplicity_code_html.py +270 -0
- data/lib/fastlane/plugin/python/generate_lint_html.py +281 -0
- data/lib/fastlane/plugin/python/generate_unused_code_html.py +328 -0
- data/lib/fastlane/plugin/python/generate_unused_image_html.py +308 -0
- metadata +89 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
require 'gym'
|
|
2
|
+
require 'fastlane_core'
|
|
3
|
+
require 'fastlane/action'
|
|
4
|
+
include Fastlane::Helper
|
|
5
|
+
|
|
6
|
+
module Fastlane
|
|
7
|
+
module Actions
|
|
8
|
+
# 打包
|
|
9
|
+
class PackageAction < Action
|
|
10
|
+
def self.run(params)
|
|
11
|
+
|
|
12
|
+
# 入参配置
|
|
13
|
+
configuration = params[:configuration] || "Debug"
|
|
14
|
+
export_method = params[:export_method] || "development"
|
|
15
|
+
build = params[:build] || nil
|
|
16
|
+
is_analyze_swiftlint = params[:is_analyze_swiftlint] || false
|
|
17
|
+
is_detect_duplicity_code = params[:is_detect_duplicity_code] || false
|
|
18
|
+
is_detect_unused_code = params[:is_detect_unused_code] || false
|
|
19
|
+
is_detect_unused_image = params[:is_detect_unused_image] || false
|
|
20
|
+
if export_method == "app-store"
|
|
21
|
+
configuration = "Release"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# 清理上一次的打包缓存
|
|
25
|
+
FileUtils.rm_rf(Dir.glob("#{Constants.BUILD_LOG_DIR}/*"))
|
|
26
|
+
FileUtils.rm_rf(Dir.glob("#{Constants.IPA_OUTPUT_DIR}/*"))
|
|
27
|
+
|
|
28
|
+
# 安装证书
|
|
29
|
+
InstallCertificateAction.run({})
|
|
30
|
+
# 安装 provisioningProfile
|
|
31
|
+
InstallProfileAction.run({})
|
|
32
|
+
|
|
33
|
+
scheme = Environment.scheme
|
|
34
|
+
# 更改项目build号
|
|
35
|
+
UpdateBuildNumberAction.run(
|
|
36
|
+
build: build
|
|
37
|
+
)
|
|
38
|
+
time = Time.new.strftime("%Y%m%d%H%M")
|
|
39
|
+
version = Actions::GetVersionNumberAction.run(target: "#{Environment.target}")
|
|
40
|
+
build = Actions::GetBuildNumberAction.run({})
|
|
41
|
+
# 生成ipa包的名字格式
|
|
42
|
+
ipaName = "#{Environment.scheme}_#{export_method}_#{version}_#{build}.ipa"
|
|
43
|
+
|
|
44
|
+
# 获取 Extension 的 Bundle ID(可能有多个,用逗号分隔)
|
|
45
|
+
extension_bundle_ids = Environment.extension_bundle_ids
|
|
46
|
+
extension_profile_names = []
|
|
47
|
+
# profile 名字
|
|
48
|
+
profile_name = ""
|
|
49
|
+
|
|
50
|
+
case export_method
|
|
51
|
+
when "development"
|
|
52
|
+
profile_name = Environment.provisioningProfiles_development
|
|
53
|
+
extension_profile_names = Environment.extension_profiles_development
|
|
54
|
+
when "ad-hoc"
|
|
55
|
+
profile_name = Environment.provisioningProfiles_adhoc
|
|
56
|
+
extension_profile_names = Environment.extension_profiles_adhoc
|
|
57
|
+
when "app-store"
|
|
58
|
+
profile_name = Environment.provisioningProfiles_appstore
|
|
59
|
+
extension_profile_names = Environment.extension_profiles_appstore
|
|
60
|
+
else
|
|
61
|
+
raise "Unsupported export method: #{export_method}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 组装 provisioningProfiles
|
|
65
|
+
provisioningProfiles_map = {
|
|
66
|
+
"#{Environment.bundleID}" => "#{profile_name}"
|
|
67
|
+
}
|
|
68
|
+
extension_bundle_ids.each_with_index do |ext_bundle_id, idx|
|
|
69
|
+
provisioningProfiles_map[ext_bundle_id.strip] = extension_profile_names[idx]&.strip
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
UI.message("*************| 开始打包 |*************")
|
|
73
|
+
|
|
74
|
+
options = {
|
|
75
|
+
clean: true,
|
|
76
|
+
silent: true,
|
|
77
|
+
workspace: Environment.workspace,
|
|
78
|
+
scheme: scheme,
|
|
79
|
+
configuration: configuration,
|
|
80
|
+
buildlog_path: Constants.BUILD_LOG_DIR,
|
|
81
|
+
output_name: ipaName,
|
|
82
|
+
output_directory: Constants.IPA_OUTPUT_DIR,
|
|
83
|
+
export_options: {
|
|
84
|
+
method: export_method,
|
|
85
|
+
provisioningProfiles: provisioningProfiles_map
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
config = FastlaneCore::Configuration.create(Gym::Options.available_options, options)
|
|
89
|
+
Gym::Manager.new.work(config)
|
|
90
|
+
|
|
91
|
+
UI.message("*************| 打包完成 |*************")
|
|
92
|
+
|
|
93
|
+
UI.message("*************| 复制打包产物 |*************")
|
|
94
|
+
# 定义桌面路径
|
|
95
|
+
desktop_path = File.expand_path("~/Desktop")
|
|
96
|
+
output_path = File.join(desktop_path, "BuildOutput_#{scheme}")
|
|
97
|
+
target_path = File.join(output_path, "#{build}")
|
|
98
|
+
FileUtils.mkdir_p(target_path)
|
|
99
|
+
# 构建复制到桌面
|
|
100
|
+
Dir.glob("#{Constants.IPA_OUTPUT_DIR}/*").each do |file|
|
|
101
|
+
UI.message("准备复制文件:#{file} 到 #{target_path}")
|
|
102
|
+
FileUtils.cp_r(file, target_path)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# UI.message("*************| 重置 Git 仓库 |*************")
|
|
106
|
+
# 重置 Git 仓库
|
|
107
|
+
# system("git reset --hard")
|
|
108
|
+
|
|
109
|
+
ipa_path = "#{Constants.IPA_OUTPUT_DIR}/#{ipaName}"
|
|
110
|
+
|
|
111
|
+
if export_method == "app-store"
|
|
112
|
+
notiText = "🚀🚀🚀🚀🚀🚀\n\n#{scheme}-iOS-打包完成\n\n#{version}_#{build}_#{export_method}\n\n🚀🚀🚀🚀🚀🚀"
|
|
113
|
+
NotiDingdingAction.run(notiText: notiText)
|
|
114
|
+
|
|
115
|
+
if CommonHelper.is_validate_string(Environment.connect_key_id) && CommonHelper.is_validate_string(Environment.connect_issuer_id)
|
|
116
|
+
|
|
117
|
+
UploadStoreAction.run({})
|
|
118
|
+
notiText = "🚀🚀🚀🚀🚀🚀\n\n#{scheme}-iOS-上传完成\n\n#{version}_#{build}_#{export_method}\n\n🚀🚀🚀🚀🚀🚀"
|
|
119
|
+
NotiDingdingAction.run(notiText: notiText)
|
|
120
|
+
end
|
|
121
|
+
else
|
|
122
|
+
# 上传蒲公英
|
|
123
|
+
pgy_upload_info = UploadPgyAction.run(
|
|
124
|
+
"ipa_path": ipa_path
|
|
125
|
+
)
|
|
126
|
+
qrCode = pgy_upload_info["buildQRCodeURL"]
|
|
127
|
+
|
|
128
|
+
# 钉钉通知
|
|
129
|
+
notiText = "🚀🚀🚀🚀🚀🚀\n\n#{scheme}-iOS-打包完成\n\n#{version}_#{build}_#{export_method}\n\n🚀🚀🚀🚀🚀🚀"
|
|
130
|
+
if CommonHelper.is_validate_string(qrCode)
|
|
131
|
+
notiText << "\n\n⬇️⬇️⬇️ 扫码安装 ⬇️⬇️⬇️\n\n"
|
|
132
|
+
end
|
|
133
|
+
NotiDingdingAction.run(notiText: notiText)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# 代码分析
|
|
137
|
+
if is_analyze_swiftlint && export_method != "app-store"
|
|
138
|
+
analyze_swiftlint(is_from_package: true, configuration: configuration)
|
|
139
|
+
# 结果复制到桌面
|
|
140
|
+
FileUtils.cp(SWIFTLINT_HTML_FILE, target_path)
|
|
141
|
+
FileUtils.cp(SWIFTLINT_ANALYZE_HTML_FILE, target_path)
|
|
142
|
+
UI.message("*************| 代码分析完成 |*************")
|
|
143
|
+
else
|
|
144
|
+
UI.message("*************| 跳过代码分析 |*************")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# 重复代码检查
|
|
148
|
+
if is_detect_duplicity_code && export_method != "app-store"
|
|
149
|
+
detect_code_duplicity(is_all: true)
|
|
150
|
+
# 结果复制到桌面
|
|
151
|
+
FileUtils.cp(DUPLICITY_CODE_HTML_FILE, target_path)
|
|
152
|
+
UI.message("*************| 重复代码检查完成 |*************")
|
|
153
|
+
else
|
|
154
|
+
UI.message("*************| 跳过重复代码检查 |*************")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# 无用代码检查
|
|
158
|
+
if is_detect_unused_code && export_method != "app-store"
|
|
159
|
+
DetectUnusedCodeAction.run(
|
|
160
|
+
is_from_package: true,
|
|
161
|
+
configuration: configuration
|
|
162
|
+
)
|
|
163
|
+
# 结果复制到桌面
|
|
164
|
+
FileUtils.cp(Constants.UNUSED_CODE_HTML_FILE, target_path)
|
|
165
|
+
UI.message("*************| 无用代码检查完成 |*************")
|
|
166
|
+
else
|
|
167
|
+
UI.message("*************| 跳过无用代码检查 |*************")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# 无用图片检查
|
|
171
|
+
if is_detect_unused_image && export_method != "app-store"
|
|
172
|
+
DetectUnusedImageAction.run({})
|
|
173
|
+
# 结果复制到桌面
|
|
174
|
+
FileUtils.cp(Constants.UNUSED_IMAGE_HTML_FILE, target_path)
|
|
175
|
+
UI.message("*************| 无用图片检查完成 |*************")
|
|
176
|
+
else
|
|
177
|
+
UI.message("*************| 跳过未使用图片检查 |*************")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
if is_swiftlint ||
|
|
181
|
+
is_detect_duplicity_code ||
|
|
182
|
+
is_detect_unused_code ||
|
|
183
|
+
is_detect_unused_image
|
|
184
|
+
# 钉钉通知
|
|
185
|
+
notiText = "🚀🚀🚀🚀🚀🚀\n\n#{scheme}-iOS-代码检查完成\n\n#{version}_#{build}_#{export_method}\n\n🚀🚀🚀🚀🚀🚀"
|
|
186
|
+
NotiDingdingAction.run(notiText: notiText)
|
|
187
|
+
else
|
|
188
|
+
UI.message("*************| 跳过代码检查 |*************")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
UI.message("*************| 脚本完成 |*************")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.description
|
|
195
|
+
"打包"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def self.available_options
|
|
199
|
+
[
|
|
200
|
+
FastlaneCore::ConfigItem.new(
|
|
201
|
+
key: :configuration,
|
|
202
|
+
description: "编译环境 Release or Debug",
|
|
203
|
+
optional: true,
|
|
204
|
+
default_value: "Release",
|
|
205
|
+
type: String
|
|
206
|
+
),
|
|
207
|
+
FastlaneCore::ConfigItem.new(
|
|
208
|
+
key: :export_method,
|
|
209
|
+
description: "打包方式 ad-hoc, enterprise, app-store, development",
|
|
210
|
+
optional: true,
|
|
211
|
+
default_value: "development",
|
|
212
|
+
type: String
|
|
213
|
+
),
|
|
214
|
+
FastlaneCore::ConfigItem.new(
|
|
215
|
+
key: :build,
|
|
216
|
+
description: "不采取自动更新,自定义 build 号",
|
|
217
|
+
optional: true,
|
|
218
|
+
default_value: nil,
|
|
219
|
+
type: String
|
|
220
|
+
),
|
|
221
|
+
FastlaneCore::ConfigItem.new(
|
|
222
|
+
key: :is_analyze_code,
|
|
223
|
+
description: "是否代码分析",
|
|
224
|
+
optional: true,
|
|
225
|
+
default_value: false,
|
|
226
|
+
type: Boolean
|
|
227
|
+
),
|
|
228
|
+
FastlaneCore::ConfigItem.new(
|
|
229
|
+
key: :is_detect_code_duplicity,
|
|
230
|
+
description: "是否检查重复代码",
|
|
231
|
+
optional: true,
|
|
232
|
+
default_value: false,
|
|
233
|
+
type: Boolean
|
|
234
|
+
),
|
|
235
|
+
FastlaneCore::ConfigItem.new(
|
|
236
|
+
key: :is_detect_unused_code,
|
|
237
|
+
description: "是否检查无用代码",
|
|
238
|
+
optional: true,
|
|
239
|
+
default_value: false,
|
|
240
|
+
type: Boolean
|
|
241
|
+
),
|
|
242
|
+
FastlaneCore::ConfigItem.new(
|
|
243
|
+
key: :is_detect_unused_image,
|
|
244
|
+
description: "是否检查无用图片",
|
|
245
|
+
optional: true,
|
|
246
|
+
default_value: false,
|
|
247
|
+
type: Boolean
|
|
248
|
+
),
|
|
249
|
+
]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.is_supported?(platform)
|
|
253
|
+
platform == :ios
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def self.category
|
|
257
|
+
:building
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
include Fastlane::Helper
|
|
3
|
+
|
|
4
|
+
module Fastlane
|
|
5
|
+
module Actions
|
|
6
|
+
# 更新 build 号
|
|
7
|
+
class UpdateBuildNumberAction < Action
|
|
8
|
+
def self.run(params)
|
|
9
|
+
|
|
10
|
+
build = params[:build] || nil
|
|
11
|
+
unless CommonHelper.is_validate_string(build)
|
|
12
|
+
currentTime = Time.new.strftime("%Y%m%d")
|
|
13
|
+
build = CommonHelper.get_cached_build_number
|
|
14
|
+
|
|
15
|
+
if build.include?("#{currentTime}.")
|
|
16
|
+
# 当天版本 计算迭代版本号
|
|
17
|
+
lastStr = build.split('.').last
|
|
18
|
+
lastNum = lastStr.to_i
|
|
19
|
+
lastNum += 1
|
|
20
|
+
lastStr = lastNum.to_s.rjust(2, '0')
|
|
21
|
+
build = "#{currentTime}.#{lastStr}"
|
|
22
|
+
else
|
|
23
|
+
# 非当天版本 build 重置
|
|
24
|
+
build = "#{currentTime}.01"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 缓存新的 build 号
|
|
28
|
+
CommonHelper.write_cached_txt(Constants.BUILD_NUMBER_FILE, build)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
UI.message("*************| 更新 build #{build} |*************")
|
|
32
|
+
# 更改项目 build 号
|
|
33
|
+
Actions::IncrementBuildNumberAction.run(
|
|
34
|
+
build_number: build
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.description
|
|
40
|
+
"更新 build 号"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.available_options
|
|
44
|
+
[
|
|
45
|
+
FastlaneCore::ConfigItem.new(
|
|
46
|
+
key: :build,
|
|
47
|
+
description: "不采取自动更新,自定义 build 号",
|
|
48
|
+
optional: true,
|
|
49
|
+
default_value: nil,
|
|
50
|
+
type: String
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.is_supported?(platform)
|
|
56
|
+
platform == :ios
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.category
|
|
60
|
+
:building
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
require 'fastlane/plugin/pgyer'
|
|
3
|
+
include Fastlane::Helper
|
|
4
|
+
|
|
5
|
+
module Fastlane
|
|
6
|
+
module Actions
|
|
7
|
+
# 上传蒲公英
|
|
8
|
+
class UploadPgyAction < Action
|
|
9
|
+
def self.run(params)
|
|
10
|
+
UI.message("*************| 开始上传蒲公英 |*************")
|
|
11
|
+
|
|
12
|
+
unless CommonHelper.is_validate_string(Environment.pgy_api_key)
|
|
13
|
+
UI.message("*************| 没有配置 pgy_api_key |*************")
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ipa_path = params[:ipa_path] || ""
|
|
18
|
+
|
|
19
|
+
pgyinfo = PgyerAction.run(
|
|
20
|
+
ipa: ipa_path,
|
|
21
|
+
api_key: Environment.pgy_api_key,
|
|
22
|
+
password: Environment.pgy_password,
|
|
23
|
+
install_type: "2"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return pgyinfo
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.description
|
|
30
|
+
"上传蒲公英"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.available_options
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.is_supported?(platform)
|
|
38
|
+
platform == :ios
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.category
|
|
42
|
+
:building
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
include Fastlane::Helper
|
|
3
|
+
|
|
4
|
+
module Fastlane
|
|
5
|
+
module Actions
|
|
6
|
+
# 上传 AppStore
|
|
7
|
+
class UploadStoreAction < Action
|
|
8
|
+
def self.run(params)
|
|
9
|
+
UI.message("*************| 开始上传 AppStore |*************")
|
|
10
|
+
|
|
11
|
+
other_action.app_store_connect_api_key(
|
|
12
|
+
key_id: Environment.connect_key_id,
|
|
13
|
+
issuer_id: Environment.connect_issuer_id,
|
|
14
|
+
key_filepath: File.expand_path("./AuthKey_#{Environment.connect_key_id}.p8"),
|
|
15
|
+
duration: 1200, # optional (maximum 1200)
|
|
16
|
+
in_house: false # optional but may be required if using match/sigh
|
|
17
|
+
)
|
|
18
|
+
other_action.upload_to_app_store(
|
|
19
|
+
skip_metadata: false,
|
|
20
|
+
skip_screenshots: true,
|
|
21
|
+
force: true,
|
|
22
|
+
submit_for_review: false,
|
|
23
|
+
automatic_release: false,
|
|
24
|
+
release_notes: params[:release_notes]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.description
|
|
30
|
+
"上传 AppStore"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.available_options
|
|
34
|
+
[
|
|
35
|
+
FastlaneCore::ConfigItem.new(
|
|
36
|
+
key: :release_notes,
|
|
37
|
+
description: "更新文案, 格式为 { "zh-Hans" => "修复问题", "en-US" => "bugfix"} ",
|
|
38
|
+
optional: false,
|
|
39
|
+
type: Hash
|
|
40
|
+
)
|
|
41
|
+
]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.is_supported?(platform)
|
|
45
|
+
platform == :ios
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Fastlane
|
|
5
|
+
module Helper
|
|
6
|
+
class CommonHelper
|
|
7
|
+
|
|
8
|
+
def self.read_cached_txt(file_name)
|
|
9
|
+
if File.exist?(file_name)
|
|
10
|
+
File.read(file_name).strip
|
|
11
|
+
else
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.write_cached_txt(file_name, content)
|
|
17
|
+
dir_name = File.dirname(file_name)
|
|
18
|
+
FileUtils.mkdir_p(dir_name) unless Dir.exist?(dir_name)
|
|
19
|
+
|
|
20
|
+
File.open(file_name, "w") { |file| file.write(content) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.get_cached_build_number
|
|
24
|
+
cached_build = read_cached_txt(Constants.BUILD_NUMBER_FILE)
|
|
25
|
+
currentTime = Time.new.strftime("%Y%m%d")
|
|
26
|
+
|
|
27
|
+
if cached_build.nil? || cached_build.empty?
|
|
28
|
+
# 初始化 build
|
|
29
|
+
return "#{currentTime}.00"
|
|
30
|
+
else
|
|
31
|
+
return cached_build
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# 判断字段是否有值
|
|
36
|
+
def self.is_validate_string(variable)
|
|
37
|
+
if variable.nil? || variable.empty?
|
|
38
|
+
return false
|
|
39
|
+
end
|
|
40
|
+
return true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 获取当前 commit_hash 到最新 commit 下所有变更的 swift 文件
|
|
44
|
+
def self.get_git_modified_swift_files(commit_hash)
|
|
45
|
+
modified_files = sh("git diff --name-only --diff-filter=d #{commit_hash}..origin/develop").split("\n")
|
|
46
|
+
swift_files = modified_files.select { |file| file.end_with?('.swift') }
|
|
47
|
+
return swift_files
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 缓存最新 git
|
|
51
|
+
def self.cache_git_commit
|
|
52
|
+
latest_commit = sh("git rev-parse HEAD").strip
|
|
53
|
+
write_cached_txt(COMMIT_HASH_FILE, latest_commit)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 从构建日志中提取 index store 路径的辅助方法
|
|
57
|
+
def self.extract_index_store_path(log_file)
|
|
58
|
+
begin
|
|
59
|
+
log_content = File.read(log_file)
|
|
60
|
+
|
|
61
|
+
# 在日志中查找 DerivedData 路径
|
|
62
|
+
derived_data_match = log_content.match(/DerivedData\/([^\/]+)-([^\/]+)/)
|
|
63
|
+
if derived_data_match
|
|
64
|
+
project_name = derived_data_match[1]
|
|
65
|
+
hash_suffix = derived_data_match[2]
|
|
66
|
+
|
|
67
|
+
# 构建可能的 index store 路径
|
|
68
|
+
derived_data_base = File.expand_path("~/Library/Developer/Xcode/DerivedData")
|
|
69
|
+
project_dir = "#{derived_data_base}/#{project_name}-#{hash_suffix}"
|
|
70
|
+
|
|
71
|
+
# Xcode 14+ 使用 Index.noindex
|
|
72
|
+
index_paths = [
|
|
73
|
+
"#{project_dir}/Index.noindex/DataStore",
|
|
74
|
+
"#{project_dir}/Index/DataStore"
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# 返回第一个存在的路径
|
|
78
|
+
index_paths.each do |path|
|
|
79
|
+
if File.exist?(path)
|
|
80
|
+
puts "*************| 找到 index store: #{path} |*************"
|
|
81
|
+
return path
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
puts "*************| 在构建日志中未找到有效的 index store 路径 |*************"
|
|
87
|
+
return nil
|
|
88
|
+
rescue => e
|
|
89
|
+
puts "*************| 解析构建日志失败: #{e.message} |*************"
|
|
90
|
+
return nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# 生成并打开 html
|
|
95
|
+
def self.generate_and_open_html(title, python_file, origin_file, html_file)
|
|
96
|
+
python_path = File.expand_path("../../python/#{python_file}", __dir__)
|
|
97
|
+
origin_file_path = File.expand_path(origin_file)
|
|
98
|
+
html_file_path = File.expand_path(html_file)
|
|
99
|
+
|
|
100
|
+
system("python3 \"#{python_path}\" \"#{title}\" \"#{origin_file_path}\" \"#{html_file_path}\"")
|
|
101
|
+
system("open \"#{html_file_path}\"")
|
|
102
|
+
|
|
103
|
+
File.delete(origin_file_path)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
module Helper
|
|
3
|
+
module Constants
|
|
4
|
+
# cache 文件
|
|
5
|
+
def self.BUILD_NUMBER_FILE
|
|
6
|
+
"fastlane_cache/build_cache.txt"
|
|
7
|
+
end
|
|
8
|
+
def self.COMMIT_HASH_FILE
|
|
9
|
+
"fastlane_cache/commit_cache.txt"
|
|
10
|
+
end
|
|
11
|
+
def self.SWIFTLINT_ANALYZE_FILE
|
|
12
|
+
"fastlane_cache/temp/swiftlint_analyze_result.txt"
|
|
13
|
+
end
|
|
14
|
+
def self.SWIFTLINT_ANALYZE_HTML_FILE
|
|
15
|
+
"fastlane_cache/html/analyze_lint_report.html"
|
|
16
|
+
end
|
|
17
|
+
def self.DUPLICITY_CODE_MODIFIED_FILE
|
|
18
|
+
"fastlane_cache/temp/duplicity_code_modified_files.txt"
|
|
19
|
+
end
|
|
20
|
+
def self.DUPLICITY_CODE_FILE
|
|
21
|
+
"fastlane_cache/temp/duplicity_code_result.xml"
|
|
22
|
+
end
|
|
23
|
+
def self.DUPLICITY_CODE_HTML_FILE
|
|
24
|
+
"fastlane_cache/html/duplicity_code_report.html"
|
|
25
|
+
end
|
|
26
|
+
def self.UNUSED_CODE_FILE
|
|
27
|
+
"fastlane_cache/temp/unused_code_result.txt"
|
|
28
|
+
end
|
|
29
|
+
def self.UNUSED_CODE_HTML_FILE
|
|
30
|
+
"fastlane_cache/html/unused_code_report.html"
|
|
31
|
+
end
|
|
32
|
+
def self.UNUSED_IMAGE_FILE
|
|
33
|
+
"fastlane_cache/temp/unused_image_result.txt"
|
|
34
|
+
end
|
|
35
|
+
def self.UNUSED_IMAGE_HTML_FILE
|
|
36
|
+
"fastlane_cache/html/unused_image_report.html"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# bulid 产物
|
|
40
|
+
def self.BUILD_LOG_DIR
|
|
41
|
+
"fastlane_cache/build_logs"
|
|
42
|
+
end
|
|
43
|
+
def self.IPA_OUTPUT_DIR
|
|
44
|
+
"fastlane_cache/build_products"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
module Helper
|
|
3
|
+
module Environment
|
|
4
|
+
# 苹果密钥配置
|
|
5
|
+
def self.connect_key_id
|
|
6
|
+
ENV['CONNECT_KEY_ID']
|
|
7
|
+
end
|
|
8
|
+
def self.connect_issuer_id
|
|
9
|
+
ENV['CONNECT_ISSUER_ID']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# 项目配置
|
|
13
|
+
def self.scheme
|
|
14
|
+
ENV['SCHEME_NAME']
|
|
15
|
+
end
|
|
16
|
+
def self.target
|
|
17
|
+
ENV['TARGET_NAME']
|
|
18
|
+
end
|
|
19
|
+
def self.workspace
|
|
20
|
+
ENV['WORKSPACE']
|
|
21
|
+
end
|
|
22
|
+
def self.bundleID
|
|
23
|
+
ENV['BUNDLE_ID']
|
|
24
|
+
end
|
|
25
|
+
def self.extension_bundle_ids
|
|
26
|
+
ENV['EXTENSION_BUNDLE_IDS']&.split(",") || []
|
|
27
|
+
end
|
|
28
|
+
def self.schemes
|
|
29
|
+
ENV['SCHEMES_NAME']&.split(",") || []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# 描述文件配置
|
|
33
|
+
def self.provisioningProfile_folder_name
|
|
34
|
+
ENV['PROFILE_FOLDER_NAME']
|
|
35
|
+
end
|
|
36
|
+
def self.provisioningProfiles_development
|
|
37
|
+
ENV['PROFILE_DEVELOPMENT']
|
|
38
|
+
end
|
|
39
|
+
def self.provisioningProfiles_adhoc
|
|
40
|
+
ENV['PROFILE_ADHOC']
|
|
41
|
+
end
|
|
42
|
+
def self.provisioningProfiles_appstore
|
|
43
|
+
ENV['PROFILE_APPSTORE']
|
|
44
|
+
end
|
|
45
|
+
def self.extension_profiles_development
|
|
46
|
+
ENV['EXTENSION_PROFILES_DEVELOPMENT']&.split(",") || []
|
|
47
|
+
end
|
|
48
|
+
def self.extension_profiles_adhoc
|
|
49
|
+
ENV['EXTENSION_PROFILES_ADHOC']&.split(",") || []
|
|
50
|
+
end
|
|
51
|
+
def self.extension_profiles_appstore
|
|
52
|
+
ENV['EXTENSION_PROFILES_APPSTORE']&.split(",") || []
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# p12 证书配置
|
|
56
|
+
def self.certificate_folder_name
|
|
57
|
+
ENV['PROFILE_FOLDER_NAME']
|
|
58
|
+
end
|
|
59
|
+
def self.certificate_development
|
|
60
|
+
ENV['CERTIFICATE_DEVELOPMENT']
|
|
61
|
+
end
|
|
62
|
+
def self.certificate_distribution
|
|
63
|
+
ENV['CERTIFICATE_DISTRIBUTION']
|
|
64
|
+
end
|
|
65
|
+
def self.certificate_password
|
|
66
|
+
ENV['CERTIFICATE_PASSWORD']
|
|
67
|
+
end
|
|
68
|
+
def self.keychain_password
|
|
69
|
+
ENV['KEYCHAIN_PASSWORD']
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 蒲公英配置
|
|
73
|
+
def self.pgy_api_key
|
|
74
|
+
ENV['PGY_API_KEY']
|
|
75
|
+
end
|
|
76
|
+
def self.pgy_password
|
|
77
|
+
ENV['PGY_PASSWORD']
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# 钉钉配置
|
|
81
|
+
def self.dingdingToken
|
|
82
|
+
ENV['DINGDING_TOKEN']
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'fastlane/plugin/fastci/version'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Fastci
|
|
5
|
+
# Return all .rb files inside the "actions" and "helper" directory
|
|
6
|
+
def self.all_classes
|
|
7
|
+
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# By default we want to import all available actions and helpers
|
|
13
|
+
# A plugin can contain any number of actions and plugins
|
|
14
|
+
Fastlane::Fastci.all_classes.each do |current|
|
|
15
|
+
require current
|
|
16
|
+
end
|