pindo 5.21.4 → 5.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/pindo/base/git_handler.rb +21 -2
- data/lib/pindo/base/xcodeproj_compat.rb +42 -0
- data/lib/pindo/module/task/model/build/ios_build_dev_task.rb +34 -4
- data/lib/pindo/module/xcode/res/xcode_res_handler.rb +87 -26
- data/lib/pindo/module/xcode/scene_lifecycle_guard.rb +16 -5
- data/lib/pindo/module/xcode/xcode_build_config.rb +47 -3
- data/lib/pindo/version.rb +1 -1
- data/lib/pindo.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4ccbec1218f28dfa10b000e89759db3e8167c0ea598637a0f8436a557ab14bc
|
|
4
|
+
data.tar.gz: 737550673bf62315c4ab30652496a82bcda55001991af18c2c5da1b019b9cf83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52e831191efa0d0092fdd2333658ece13ff82f004255d5dfe55b5f75a295b5d46356fc56956323b8b526bd6dfdda5fa10b15e5e144e971fb70bd629bb5268e57
|
|
7
|
+
data.tar.gz: 6fa3ad96ffe7951bcec719e8c9a3ffad2540e807c4d451ba6a3dc16fbe59e85a2520f874d72ae589b72ebd47828df71c0482dfcedbdb310e58d6c99f49e24efb
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
2
3
|
require 'pindo/base/executable'
|
|
3
4
|
require 'pindo/base/informative'
|
|
4
5
|
require 'etc'
|
|
@@ -556,6 +557,16 @@ module Pindo
|
|
|
556
557
|
|
|
557
558
|
|
|
558
559
|
|
|
560
|
+
# 仓库是否已有提交(git init 后尚未首次提交的仓库没有 HEAD)
|
|
561
|
+
# @param git_root [String] Git 根目录
|
|
562
|
+
# @return [Boolean]
|
|
563
|
+
def head_commit_exists?(git_root:)
|
|
564
|
+
_stdout, _stderr, status = Open3.capture3('git', '-C', git_root, 'rev-parse', '--verify', '--quiet', 'HEAD')
|
|
565
|
+
status.success?
|
|
566
|
+
rescue SystemCallError
|
|
567
|
+
false
|
|
568
|
+
end
|
|
569
|
+
|
|
559
570
|
# 获取未提交的文件列表
|
|
560
571
|
# @param project_dir [String] 项目目录路径
|
|
561
572
|
# @return [Array<String>, nil] 返回未提交的文件列表,如果没有则返回 nil
|
|
@@ -970,6 +981,15 @@ module Pindo
|
|
|
970
981
|
# @param default_process_type [Symbol, nil] 非交互模式下的默认处理方式,默认为 nil
|
|
971
982
|
# @return [Symbol] UncommittedFilesProcessType 枚举常量
|
|
972
983
|
def get_uncommitted_files_process_type(project_dir:, interactive: true, default_process_type: nil)
|
|
984
|
+
# 0. 空仓库没有 HEAD,后续的暂存区重置、版本计算、分支同步和打 tag 都无法进行
|
|
985
|
+
git_root = git_root_directory(local_repo_dir: project_dir)
|
|
986
|
+
if git_root && !head_commit_exists?(git_root: git_root)
|
|
987
|
+
raise Informative, "当前 Git 仓库还没有任何提交,无法计算版本号和打 tag:#{git_root}\n" \
|
|
988
|
+
"请先完成首次提交后重新执行:\n" \
|
|
989
|
+
" git -C \"#{git_root}\" add -A\n" \
|
|
990
|
+
" git -C \"#{git_root}\" commit -m \"chore: 初始化工程\""
|
|
991
|
+
end
|
|
992
|
+
|
|
973
993
|
# 1. 检查是否有未提交的文件
|
|
974
994
|
uncommitted_files = get_uncommitted_files(project_dir: project_dir)
|
|
975
995
|
|
|
@@ -986,8 +1006,7 @@ module Pindo
|
|
|
986
1006
|
return default_process_type
|
|
987
1007
|
end
|
|
988
1008
|
|
|
989
|
-
# 4.
|
|
990
|
-
git_root = git_root_directory(local_repo_dir: project_dir)
|
|
1009
|
+
# 4. 获取当前分支名
|
|
991
1010
|
unless git_root
|
|
992
1011
|
Funlog.instance.fancyinfo_warning("当前目录不是Git仓库")
|
|
993
1012
|
return Pindo::UncommittedFilesProcessType::SKIP_WITH_TAG
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'xcodeproj'
|
|
2
|
+
|
|
3
|
+
module Pindo
|
|
4
|
+
# Xcode 26.4+/27 工程格式兼容补丁(xcodeproj 1.28.1 尚未支持,见 CocoaPods/Xcodeproj#1017)
|
|
5
|
+
#
|
|
6
|
+
# CocoaPods 与 fastlane gym 在 pindo 进程内运行,共用同一份补丁。
|
|
7
|
+
module XcodeprojCompat
|
|
8
|
+
# 新格式把 shellScript 存为按行拆分的数组,合并回字符串
|
|
9
|
+
module ShellScriptArray
|
|
10
|
+
def shell_script=(value)
|
|
11
|
+
super(value.is_a?(Array) ? value.join("\n") : value)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# 保存时保留 xcodeproj 不认识的字段(如 Copy Files 阶段的 dstSubfolder),
|
|
16
|
+
# 否则这些字段会被丢弃,导致扩展等产物的嵌入位置丢失
|
|
17
|
+
module UnknownAttributes
|
|
18
|
+
def configure_with_plist(objects_by_uuid_plist)
|
|
19
|
+
object_plist = objects_by_uuid_plist[uuid]
|
|
20
|
+
known_keys = self.class.attributes.map(&:plist_name) << 'isa'
|
|
21
|
+
@pindo_unknown_attributes = object_plist.reject { |key, _| known_keys.include?(key) }
|
|
22
|
+
|
|
23
|
+
# 暂时移除未知字段,避免 xcodeproj 对已保留的字段输出误导性警告
|
|
24
|
+
@pindo_unknown_attributes.each_key { |key| object_plist.delete(key) }
|
|
25
|
+
super
|
|
26
|
+
ensure
|
|
27
|
+
object_plist&.merge!(@pindo_unknown_attributes || {})
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def to_hash_as(method = :to_hash)
|
|
33
|
+
plist = super
|
|
34
|
+
(@pindo_unknown_attributes || {}).each { |key, value| plist[key] = value unless plist.key?(key) }
|
|
35
|
+
plist
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Xcodeproj::Project::Object::PBXShellScriptBuildPhase.prepend(Pindo::XcodeprojCompat::ShellScriptArray)
|
|
42
|
+
Xcodeproj::Project::Object::AbstractObject.prepend(Pindo::XcodeprojCompat::UnknownAttributes)
|
|
@@ -142,7 +142,8 @@ module Pindo
|
|
|
142
142
|
project_dir: @project_path,
|
|
143
143
|
workflow_packname: workflow_packname,
|
|
144
144
|
project_id: project_id,
|
|
145
|
-
bundle_id: @bundle_id
|
|
145
|
+
bundle_id: @bundle_id,
|
|
146
|
+
bundle_id_prefix: Pindo::XcodeBuildConfig.bundle_id_prefix_from(config_bundle_id)
|
|
146
147
|
)
|
|
147
148
|
|
|
148
149
|
# 下载并替换 Icon,优先使用 packageProjectIcon,为空则回退到 projectIcon
|
|
@@ -159,7 +160,7 @@ module Pindo
|
|
|
159
160
|
)
|
|
160
161
|
|
|
161
162
|
unless icon_replace_success
|
|
162
|
-
raise Informative, "Icon
|
|
163
|
+
raise Informative, "Icon 下载或替换失败,具体原因见上方日志(Icon URL: #{icon_url})"
|
|
163
164
|
end
|
|
164
165
|
else
|
|
165
166
|
Funlog.warning("未配置 Icon URL,跳过 Icon 替换")
|
|
@@ -196,8 +197,8 @@ module Pindo
|
|
|
196
197
|
commit_short = commit_hash ? commit_hash[0..7] : "unknown"
|
|
197
198
|
Funlog.instance.fancyinfo_success("自己计算版本号: #{build_version}, Build: #{build_number}, Commit: #{commit_short}")
|
|
198
199
|
else
|
|
199
|
-
# 不是 Git 仓库且没有从 GitCommitTask
|
|
200
|
-
|
|
200
|
+
# 不是 Git 仓库且没有从 GitCommitTask 获取到版本号:沿用工程版本号,但补齐为 JPS 要求的 major.minor.patch
|
|
201
|
+
normalize_project_version
|
|
201
202
|
return
|
|
202
203
|
end
|
|
203
204
|
end
|
|
@@ -210,6 +211,35 @@ module Pindo
|
|
|
210
211
|
)
|
|
211
212
|
end
|
|
212
213
|
|
|
214
|
+
# 非 Git 仓库时沿用工程原有版本号,仅补齐格式(如 Xcode 模板默认的 1.0 => 1.0.0),否则 JPS 拒收
|
|
215
|
+
def normalize_project_version
|
|
216
|
+
version, build_number = Pindo::XcodeBuildConfig.current_app_version(project_dir: @project_path)
|
|
217
|
+
normalized = Pindo::XcodeBuildConfig.normalize_marketing_version(version)
|
|
218
|
+
|
|
219
|
+
if normalized.nil?
|
|
220
|
+
Funlog.warning("非Git仓库,保持工程原有版本号 #{version.inspect};JPS 要求 major.minor.patch 格式,上传可能失败")
|
|
221
|
+
return
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
if normalized == version.to_s.strip
|
|
225
|
+
Funlog.warning("非Git仓库且未从 GitCommitTask 获取到版本号,保持工程原有版本号 #{normalized}")
|
|
226
|
+
return
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
Funlog.warning("非Git仓库,工程版本号 #{version} 补齐为 #{normalized}")
|
|
230
|
+
Pindo::XcodeBuildConfig.update_ios_project_version(
|
|
231
|
+
project_dir: @project_path,
|
|
232
|
+
version: normalized,
|
|
233
|
+
build_number: build_number.to_s.strip.empty? ? '1' : build_number
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# 当前环境配置仓库中的主 Bundle ID(如 workhard 为 com.eyesofstorms.*),与证书使用同一来源
|
|
238
|
+
def config_bundle_id
|
|
239
|
+
require 'pindo/config/ios_config_parser'
|
|
240
|
+
Pindo::IosConfigParser.instance.bundle_id
|
|
241
|
+
end
|
|
242
|
+
|
|
213
243
|
# 检查配置
|
|
214
244
|
def pull_config
|
|
215
245
|
# 检查 IosConfigParser 单例中是否已加载配置
|
|
@@ -12,38 +12,69 @@ module Pindo
|
|
|
12
12
|
@project_obj = Xcodeproj::Project.open(proj_fullname)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
DEFAULT_APP_ICON_NAME = "AppIcon".freeze
|
|
16
|
+
APP_ICON_NAME_SETTING = "ASSETCATALOG_COMPILER_APPICON_NAME".freeze
|
|
17
|
+
|
|
18
|
+
# 查找工程的 App 图标集目录(<图标名>.appiconset),找不到返回 nil(不抛异常)
|
|
16
19
|
def find_xcodeproj_icon_path()
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
icon_dir_name = "#{app_icon_name}.appiconset"
|
|
21
|
+
|
|
22
|
+
# 优先在主 target 自己的 asset catalog 中查找
|
|
23
|
+
icon_path = main_target_asset_catalogs
|
|
24
|
+
.map { |catalog| File.join(catalog, icon_dir_name) }
|
|
25
|
+
.find { |path| File.directory?(path) }
|
|
26
|
+
return icon_path if icon_path
|
|
27
|
+
|
|
28
|
+
icon_path_array = Dir.glob(File.join(@project_obj.project_dir, "**", icon_dir_name))
|
|
29
|
+
if icon_path_array.size > 1
|
|
30
|
+
icon_path = icon_path_array.find{ |filename| File.directory?(filename) && filename.include?("Assets.xcassets/#{icon_dir_name}")}
|
|
31
|
+
elsif icon_path_array.size == 1
|
|
32
|
+
icon_path = icon_path_array.first
|
|
33
|
+
end
|
|
19
34
|
|
|
20
|
-
|
|
35
|
+
return nil if icon_path.nil? || icon_path.empty? || !File.exist?(icon_path)
|
|
36
|
+
icon_path
|
|
37
|
+
end
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
if icon_path.nil? || icon_path.empty? || !File.exist?(icon_path)
|
|
34
|
-
icon_path_array = Dir.glob(File.join(project_dir, "**", "AppIcon.appiconset"))
|
|
35
|
-
if icon_path_array.size > 1
|
|
36
|
-
icon_path = icon_path_array.find{ |filename| File.directory?(filename) && filename.include?("Assets.xcassets/AppIcon.appiconset")}
|
|
37
|
-
elsif icon_path_array.size == 1
|
|
38
|
-
icon_path = icon_path_array.first
|
|
39
|
-
end
|
|
39
|
+
# 查找 App 图标集目录;工程没有图标集时(如 Xcode 新模板工程),
|
|
40
|
+
# 在主 target 的 asset catalog 中创建,并设置 ASSETCATALOG_COMPILER_APPICON_NAME
|
|
41
|
+
def ensure_app_icon_path()
|
|
42
|
+
icon_path = find_xcodeproj_icon_path()
|
|
43
|
+
return icon_path if icon_path
|
|
44
|
+
|
|
45
|
+
catalog = main_target_asset_catalogs.first
|
|
46
|
+
if catalog.nil?
|
|
47
|
+
raise Informative, "没有找到Xcode icon 目录:主 target 中既没有 #{app_icon_name}.appiconset,也没有可存放图标的 .xcassets"
|
|
40
48
|
end
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
icon_path = File.join(catalog, "#{app_icon_name}.appiconset")
|
|
51
|
+
FileUtils.mkdir_p(icon_path)
|
|
52
|
+
assign_app_icon_name
|
|
53
|
+
puts " ✓ 工程中没有 App 图标集,已创建: #{icon_path}"
|
|
54
|
+
icon_path
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 主 App target 配置的图标名,未配置时为 AppIcon
|
|
58
|
+
def app_icon_name()
|
|
59
|
+
names = main_app_target&.build_configurations&.map { |config| config.build_settings[APP_ICON_NAME_SETTING] } || []
|
|
60
|
+
name = names.compact.map(&:to_s).find { |value| !value.strip.empty? }
|
|
61
|
+
name || DEFAULT_APP_ICON_NAME
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 主 App target 使用的 asset catalog(含文件夹同步方式管理的资源)
|
|
65
|
+
def main_target_asset_catalogs()
|
|
66
|
+
target = main_app_target
|
|
67
|
+
return [] if target.nil?
|
|
68
|
+
|
|
69
|
+
referenced = target.resources_build_phase.files_references
|
|
70
|
+
.select { |file| file.respond_to?(:real_path) && file.real_path.to_s.end_with?(".xcassets") }
|
|
71
|
+
.map { |file| file.real_path.to_s }
|
|
72
|
+
|
|
73
|
+
synchronized = synchronized_group_paths(target).flat_map do |group_path|
|
|
74
|
+
Dir.glob(File.join(group_path, "**", "*.xcassets")).sort
|
|
44
75
|
end
|
|
45
76
|
|
|
46
|
-
|
|
77
|
+
(referenced + synchronized).uniq.select { |path| File.directory?(path) }
|
|
47
78
|
end
|
|
48
79
|
|
|
49
80
|
# 工程是否包含 AppIcon 目录(macOS 命令行工具等无 icon 工程返回 false)
|
|
@@ -68,7 +99,7 @@ module Pindo
|
|
|
68
99
|
end
|
|
69
100
|
|
|
70
101
|
def install_icon_res(new_icon_dir:nil)
|
|
71
|
-
icon_path =
|
|
102
|
+
icon_path = ensure_app_icon_path()
|
|
72
103
|
begin
|
|
73
104
|
FileUtils.rm_rf(icon_path)
|
|
74
105
|
FileUtils.mkdir_p(icon_path)
|
|
@@ -265,6 +296,36 @@ module Pindo
|
|
|
265
296
|
|
|
266
297
|
end
|
|
267
298
|
|
|
299
|
+
private
|
|
300
|
+
|
|
301
|
+
def main_app_target()
|
|
302
|
+
@project_obj.targets.find do |target|
|
|
303
|
+
target.respond_to?(:product_type) && target.product_type.to_s.include?(Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application])
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# 文件夹同步(PBXFileSystemSynchronizedRootGroup)方式管理的 target 目录
|
|
308
|
+
def synchronized_group_paths(target)
|
|
309
|
+
return [] unless target.respond_to?(:file_system_synchronized_groups)
|
|
310
|
+
|
|
311
|
+
target.file_system_synchronized_groups
|
|
312
|
+
.map { |group| Xcodeproj::Project::Object::GroupableHelper.real_path(group).to_s }
|
|
313
|
+
.select { |path| File.directory?(path) }
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# 未配置图标名的 target 补上 ASSETCATALOG_COMPILER_APPICON_NAME,使图标集参与编译
|
|
317
|
+
def assign_app_icon_name()
|
|
318
|
+
target = main_app_target
|
|
319
|
+
return if target.nil?
|
|
320
|
+
|
|
321
|
+
name = app_icon_name
|
|
322
|
+
missing = target.build_configurations.select { |config| config.build_settings[APP_ICON_NAME_SETTING].to_s.strip.empty? }
|
|
323
|
+
return if missing.empty?
|
|
324
|
+
|
|
325
|
+
missing.each { |config| config.build_settings[APP_ICON_NAME_SETTING] = name }
|
|
326
|
+
@project_obj.save
|
|
327
|
+
end
|
|
328
|
+
|
|
268
329
|
end
|
|
269
330
|
|
|
270
331
|
end
|
|
@@ -21,6 +21,11 @@ module Pindo
|
|
|
21
21
|
/func\s+application\s*\([^)]*configurationForConnecting\b/
|
|
22
22
|
).freeze
|
|
23
23
|
|
|
24
|
+
# SwiftUI App 生命周期(@main 入口遵循 App 协议)本身基于 UIScene
|
|
25
|
+
SWIFTUI_APP_PATTERN = /^[ \t]*@main\b[^{]*?\b(?:struct|class|enum)\s+\w+\s*:[^{]*\bApp\b/.freeze
|
|
26
|
+
|
|
27
|
+
SCENE_ADOPTION_SOURCE_PATTERN = Regexp.union(SCENE_CONFIGURATION_PATTERN, SWIFTUI_APP_PATTERN).freeze
|
|
28
|
+
|
|
24
29
|
SOURCE_EXTENSIONS = %w[.m .mm .swift].freeze
|
|
25
30
|
|
|
26
31
|
EXCLUDED_DIRECTORY_NAMES = %w[.git Pods build DerivedData].freeze
|
|
@@ -47,7 +52,7 @@ module Pindo
|
|
|
47
52
|
)
|
|
48
53
|
return if scene_manifest_declared?(info_plist_path)
|
|
49
54
|
return if scene_manifest_generated?(release_config)
|
|
50
|
-
return if
|
|
55
|
+
return if sources_adopt_scene_lifecycle?(project_dir)
|
|
51
56
|
|
|
52
57
|
raise Informative, adoption_error_message(sdk_version, info_plist_path)
|
|
53
58
|
end
|
|
@@ -91,13 +96,19 @@ module Pindo
|
|
|
91
96
|
# Xcode 只在 GENERATE_INFOPLIST_FILE = YES 时采用 INFOPLIST_KEY_* 设置
|
|
92
97
|
def scene_manifest_generated?(release_config)
|
|
93
98
|
settings = release_config.build_settings
|
|
94
|
-
settings
|
|
95
|
-
settings
|
|
99
|
+
iphoneos_setting(settings, 'GENERATE_INFOPLIST_FILE').to_s.casecmp?('YES') &&
|
|
100
|
+
iphoneos_setting(settings, SCENE_MANIFEST_GENERATION_SETTING).to_s.casecmp?('YES')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# iOS 真机构建实际生效的值:[sdk=iphoneos*] 条件设置优先于无条件设置(多平台模板只写条件设置)
|
|
104
|
+
def iphoneos_setting(settings, key)
|
|
105
|
+
_name, conditional = settings.find { |name, _value| name.start_with?("#{key}[sdk=iphoneos") }
|
|
106
|
+
conditional.nil? ? settings[key] : conditional
|
|
96
107
|
end
|
|
97
108
|
|
|
98
|
-
def
|
|
109
|
+
def sources_adopt_scene_lifecycle?(project_dir)
|
|
99
110
|
each_source_file(project_dir).any? do |path|
|
|
100
|
-
File.binread(path).match?(
|
|
111
|
+
File.binread(path).match?(SCENE_ADOPTION_SOURCE_PATTERN)
|
|
101
112
|
end
|
|
102
113
|
end
|
|
103
114
|
|
|
@@ -11,6 +11,18 @@ module Pindo
|
|
|
11
11
|
|
|
12
12
|
class XcodeBuildConfig
|
|
13
13
|
|
|
14
|
+
# 未提供任何 Bundle ID 线索时,工作流推导 Bundle ID 使用的前缀
|
|
15
|
+
DEFAULT_BUNDLE_ID_PREFIX = "com.heroneverdie101".freeze
|
|
16
|
+
|
|
17
|
+
# 从 Bundle ID 提取前缀:通配 ID 去掉 ".*",具体 ID 去掉最后一段
|
|
18
|
+
# @param bundle_id [String, nil] 如 "com.example.*" 或 "com.example.app"
|
|
19
|
+
# @return [String, nil] 如 "com.example";无法提取时返回 nil
|
|
20
|
+
def self.bundle_id_prefix_from(bundle_id)
|
|
21
|
+
value = bundle_id.to_s.strip
|
|
22
|
+
prefix = value.end_with?(".*") ? value.chomp(".*") : value.rpartition(".").first
|
|
23
|
+
prefix.empty? || prefix.include?("*") ? nil : prefix
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
# 使用package_name一次性更新Display Name、Bundle ID和URL Schemes(基于package_name)
|
|
15
27
|
# 优化版本:只读写一次plist文件,提高性能
|
|
16
28
|
# 注意:此函数只添加基于package_name的URL Scheme,基于实际Bundle ID的URL Scheme
|
|
@@ -19,8 +31,9 @@ module Pindo
|
|
|
19
31
|
# @param workflow_packname [String] 工作流的名称(如:"Test Demo")
|
|
20
32
|
# @param project_id [String] JPS项目ID(可选,用于添加快捷操作)
|
|
21
33
|
# @param bundle_id [String] 显式指定的 Bundle ID(可选,来自 --bundleid)
|
|
34
|
+
# @param bundle_id_prefix [String] 工作流推导 Bundle ID 的前缀(可选,如 "com.example")
|
|
22
35
|
# @return [Boolean] 是否成功更新
|
|
23
|
-
def self.update_project_with_workflow(project_dir: nil, workflow_packname: nil, project_id: nil, bundle_id: nil)
|
|
36
|
+
def self.update_project_with_workflow(project_dir: nil, workflow_packname: nil, project_id: nil, bundle_id: nil, bundle_id_prefix: nil)
|
|
24
37
|
raise ArgumentError, "项目目录不能为空" if project_dir.nil?
|
|
25
38
|
raise ArgumentError, "Workflow Package Name不能为空" if workflow_packname.nil?
|
|
26
39
|
|
|
@@ -30,10 +43,14 @@ module Pindo
|
|
|
30
43
|
|
|
31
44
|
# Bundle ID 优先使用显式传入的值(--bundleid),未传时按工作流名推导。
|
|
32
45
|
# 通配符(如 com.example.*)不能作为工程的 PRODUCT_BUNDLE_IDENTIFIER,
|
|
33
|
-
# 与 BaseCertOperator#configure_target_build_settings
|
|
46
|
+
# 与 BaseCertOperator#configure_target_build_settings 的处理保持一致,退回推导值,
|
|
47
|
+
# 推导值的前缀依次取 bundle_id_prefix、通配 ID 的前缀、默认前缀。
|
|
34
48
|
explicit_bundle_id = bundle_id.to_s.strip
|
|
35
49
|
use_explicit_bundle_id = !explicit_bundle_id.empty? && !explicit_bundle_id.include?("*")
|
|
36
|
-
|
|
50
|
+
derived_prefix = bundle_id_prefix.to_s.strip
|
|
51
|
+
derived_prefix = bundle_id_prefix_from(explicit_bundle_id).to_s if derived_prefix.empty?
|
|
52
|
+
derived_prefix = DEFAULT_BUNDLE_ID_PREFIX if derived_prefix.empty?
|
|
53
|
+
final_bundle_id = use_explicit_bundle_id ? explicit_bundle_id : "#{derived_prefix}.#{package_scheme}"
|
|
37
54
|
|
|
38
55
|
project_fullname = Dir.glob(File.join(project_dir, "/*.xcodeproj")).max_by {|f| File.mtime(f)}
|
|
39
56
|
return false if project_fullname.nil?
|
|
@@ -315,6 +332,33 @@ module Pindo
|
|
|
315
332
|
# @param version [String] 版本号
|
|
316
333
|
# @param build_number [Integer] Build号
|
|
317
334
|
# @return [Boolean] 是否成功更新
|
|
335
|
+
# 把纯数字版本补齐为 major.minor.patch(JPS 要求的格式),如 "1.0" => "1.0.0"
|
|
336
|
+
# @param version [String, nil] 工程中的版本号
|
|
337
|
+
# @return [String, nil] 补齐后的版本号;含变量、后缀或超过三段时返回 nil
|
|
338
|
+
def self.normalize_marketing_version(version)
|
|
339
|
+
value = version.to_s.strip
|
|
340
|
+
return nil unless value.match?(/\A\d+(\.\d+){0,2}\z/)
|
|
341
|
+
|
|
342
|
+
segments = value.split(".")
|
|
343
|
+
(segments + ["0"] * (3 - segments.size)).join(".")
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# 读取主 App target 当前的版本号与 Build 号(Release 配置优先)
|
|
347
|
+
# @param project_dir [String] 项目目录
|
|
348
|
+
# @return [Array(String, String)] [MARKETING_VERSION, CURRENT_PROJECT_VERSION],取不到时为 nil
|
|
349
|
+
def self.current_app_version(project_dir:)
|
|
350
|
+
xcodeproj_path = find_xcodeproj(project_dir)
|
|
351
|
+
return [nil, nil] if xcodeproj_path.nil?
|
|
352
|
+
|
|
353
|
+
target = Xcodeproj::Project.open(xcodeproj_path).targets.find do |t|
|
|
354
|
+
t.respond_to?(:product_type) && t.product_type.to_s.eql?("com.apple.product-type.application")
|
|
355
|
+
end
|
|
356
|
+
return [nil, nil] if target.nil?
|
|
357
|
+
|
|
358
|
+
config = target.build_configurations.find { |c| c.name == "Release" } || target.build_configurations.first
|
|
359
|
+
[config.build_settings['MARKETING_VERSION'], config.build_settings['CURRENT_PROJECT_VERSION']]
|
|
360
|
+
end
|
|
361
|
+
|
|
318
362
|
def self.update_ios_project_version(project_dir: nil, version: nil, build_number: nil)
|
|
319
363
|
raise ArgumentError, "项目目录不能为空" if project_dir.nil?
|
|
320
364
|
raise ArgumentError, "版本号不能为空" if version.nil?
|
data/lib/pindo/version.rb
CHANGED
data/lib/pindo.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pindo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.21.
|
|
4
|
+
version: 5.21.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- wade
|
|
@@ -272,6 +272,7 @@ files:
|
|
|
272
272
|
- lib/pindo/base/plaininformative.rb
|
|
273
273
|
- lib/pindo/base/plistbuddyexecutable.rb
|
|
274
274
|
- lib/pindo/base/standarderror.rb
|
|
275
|
+
- lib/pindo/base/xcodeproj_compat.rb
|
|
275
276
|
- lib/pindo/client/applovinclient.rb
|
|
276
277
|
- lib/pindo/client/aws3sclient.rb
|
|
277
278
|
- lib/pindo/client/bossclient.rb
|