cocoapods-privacy 0.6.2 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8c5e00e8a3993c8a37fb069dd5368c1f1bfe5a6b03797c8731785c4ad785683
4
- data.tar.gz: 0aa6ad38e3325f17d55274d3677d80f9ee6cddf29222e3ca56463291229aa167
3
+ metadata.gz: 5cba9b9c9c115fa3aa4ce42e7a16a941ff121244e8293b73ef966d66e83a1d6f
4
+ data.tar.gz: dc65d1598e50bcda55d989d70e515e3197540d43e952d2fa4d3941010f585080
5
5
  SHA512:
6
- metadata.gz: 91368971e928be9c4661b0c0924f2bf77e418ef275d4aaca91daf67ce4cb510306cc9833ecf434d5e3cf72ff965dd1d2be08d72f6ba6362e2a3e20a4260e2cf3
7
- data.tar.gz: ffc0431386fe1e142678ea8874b84f7a3580c3061d58d6ceb0bc799d9154c0c310899d09c50284e5fea26e224150685c2f063be98da1d259e08f24056968e215
6
+ metadata.gz: c72e3521078489bed4c1e4dfea1f9cf229d5ff9a315750bb7043106078225c8e976894306a8997af365b418a6c03132b837c6364895f12d8e7aae5a920db1a5f
7
+ data.tar.gz: 4e2e36942669c75e9ff98be1224989bd4682830cf25ca2c6763b57fb0fd24ded722909e090389aeaf5a76e3a831c68f629945594d94c4783a58857fd655d1d19
@@ -45,7 +45,7 @@ module Confuse
45
45
  func = "public extension #{key} {\n" + values.map { |value|
46
46
  value.split("\n").map { |line| " #{line}" }.join("\n") # 每行前加四个空格
47
47
  }.join("\n\n") + "\n}"
48
- }.join("\n")
48
+ }.join("\n\n")
49
49
  confuse_func_content = funcs
50
50
  puts "混淆(扩展):\n 文件:#{swift_confuse_file_path}\n 内容👇:\n#{confuse_func_content}"
51
51
 
@@ -104,7 +104,7 @@ module Confuse
104
104
  swift_extension_funcBody_map = {}
105
105
 
106
106
  #oc 暴露给swift 的扩展名称
107
- swift_extension = ""
107
+ swift_extension = []
108
108
 
109
109
  # 段注释和单行注释标志
110
110
  in_block_comment = false
@@ -177,24 +177,61 @@ module Confuse
177
177
  if !in_block_comment && !in_line_comment
178
178
 
179
179
  ###----- 处理oc ------
180
- # 查找并处理注解:__attribute__((annotate("BB_?Confuse:xxx")))
181
- #.*?: 匹配任意字符(包括冒号),但它会尽量少匹配直到遇到冒号为止。
182
- #.*? 匹配冒号后面的一些字符(任意字符,直到遇到下一个双引号)。
183
- #这样可以确保匹配的字符串中至少包含一个冒号。
184
- line_scrub.scan(/__attribute__\(\(annotate\("(.*?:.*?)"\)\)\)/) do |match|
185
- # # 将注解内容按逗号分割功能,并去重
186
- commands = match.first.split(',').map(&:strip)
187
- commands.map do |commandInfo|
188
- commandKeyAndValue = commandInfo.split(':')
189
- commandKey = commandKeyAndValue.first
190
- commandValue = commandKeyAndValue.last
191
- if commandKey && commandKey =~ /BB_?Confuse/
192
- swift_extension = commandValue
193
- end
180
+ #### 检测BBConfuseOCToSwift("xxxx") 用来处理oc函数混淆暴露给swift
181
+ line_scrub.scan(/^BBConfuseOCToSwift\(\"(.*?)\"\)/) do |match|
182
+ literals = match.first.split(',').map(&:strip)
183
+ unless literals.empty?
184
+ swift_extension = literals
194
185
  end
195
186
  end
196
187
 
197
- # 查找并处理注解:__attribute__((annotate("xxx")))
188
+ ### 检测BBConfuseOCClass("xxxx") 用来处理oc 类混淆
189
+ line_scrub.gsub!(/^BBConfuseOCClass\(\"(.*?)\"\)/) do |match|
190
+ modified = true #开启编辑
191
+ original_api = $1
192
+ new_api = encrypted_api([original_api]).first
193
+ puts "asasd = #{new_api} "
194
+ "BBConfuseOCClass(\"#{new_api}\")"
195
+ end
196
+
197
+
198
+ #### 检测BBConfuseOCClass("xxxx") 用来处理oc 类混淆
199
+ # line_scrub.scan(/^BBConfuseOCClass\(\"(.*?)\"\)/) do |match|
200
+ # puts "match = #{match}"
201
+ # apis = [match.first]
202
+ # puts "apis = #{apis}"
203
+ # new_parts = encrypted_api(apis)
204
+ # apis.each_with_index do |seg, index|
205
+ # apis_define_map[seg.strip] = new_parts[index]
206
+ # end
207
+ # end
208
+
209
+ # @property (nonatomic, assign) BOOL coldLaunch BBConfuseOCProperty;
210
+ #### 检测@protperty(xxxx)mmm *nnn BBConfuseOCProperty 用来处理oc 属性混淆
211
+ line_scrub.scan(/^@property\s*\((.*?)\)\s*(\w+)\s*([\*&]?)\s*(\w+)\s*BBConfuseOCProperty/) do |match|
212
+ puts "BBConfuseOCProperty = #{match}"
213
+ is_nullable = match.first.include?("nullable") || match.first.include?("weak")
214
+ return_type = match.second + match[2]
215
+ property_name = match[3]
216
+ apis = [property_name]
217
+ new_parts = encrypted_api(apis)
218
+
219
+ if !swift_extension.empty? && return_type && !return_type.empty?
220
+ swift_return_type = ConfuseUtils.convert_oc_type_to_swift(return_type)
221
+ swift_property_body = SwiftCallAssembly.assemblyProperty(property_name,new_parts.first,swift_return_type,is_nullable)
222
+
223
+ swift_extension.each do |extension|
224
+ swift_extension_funcBody_map[extension] ||= []
225
+ swift_extension_funcBody_map[extension].push(swift_property_body)
226
+ end
227
+ end
228
+
229
+ apis.each_with_index do |seg, index|
230
+ apis_define_map[seg.strip] = new_parts[index]
231
+ end
232
+ end
233
+
234
+ #### BBConfuseOCFunc 用来处理oc 函数混淆
198
235
  # ^ 匹配行首 + (BOOL)
199
236
  # [-+] 匹配 + 或 - 符号(ObjC 方法声明开头) +
200
237
  # \s* 匹配 0 个或多个空白字符 空格
@@ -206,17 +243,22 @@ module Confuse
206
243
  # \s*__attribute__\(\(annotate\( 匹配固定字符串 __att
207
244
  regular = ConfuseUtils.oc_func_regular(line_scrub,false)
208
245
  if regular
209
- regular = /#{regular}\s*__attribute__\(\(annotate\(/
246
+ regular = /#{regular}\s*BBConfuseOCFunc/
210
247
  line_scrub.scan(regular) do |match|
211
248
  apis = [match.second]
212
249
  new_parts = encrypted_api(apis)
213
- funcStr = match.first.sub(';','').sub(/__attribute__\(\(annotate\(["][^"]*["]\)\)\).*/, '')
214
- swift_method_declaration,params = ObjCMethodAPIConverter.convert(funcStr)
250
+ funcStr = match.first.sub(';','').sub(/BBConfuseOCFunc.*/, '')
251
+ is_nullable, return_type, swift_method_declaration ,params = ObjCMethodAPIConverter.convert(funcStr)
252
+
215
253
  if !swift_extension.empty? && swift_method_declaration && !swift_method_declaration.empty?
216
- swift_func_body = SwiftCallAssembly.assembly(new_parts.first,swift_method_declaration,params)
217
- swift_extension_funcBody_map[swift_extension] ||= []
218
- swift_extension_funcBody_map[swift_extension].push(swift_func_body)
254
+ swift_func_body = SwiftCallAssembly.assembly(new_parts.first, swift_method_declaration, params, is_nullable, return_type)
255
+
256
+ swift_extension.each do |extension|
257
+ swift_extension_funcBody_map[extension] ||= []
258
+ swift_extension_funcBody_map[extension].push(swift_func_body)
259
+ end
219
260
  end
261
+
220
262
  apis.each_with_index do |seg, index|
221
263
  apis_define_map[seg.strip] = new_parts[index]
222
264
  end
@@ -480,9 +522,30 @@ module Confuse
480
522
  end
481
523
  end
482
524
  end
525
+
526
+ # 用 apis_define_map 中对应的 value,更新 swift_extension_funcBody_map 中相同 key 的 key, 避免AClass同时定义了类混淆且暴露给swift, swift 扩展使用原先未混淆的类的问题
527
+ # update_func_body_map_keys!($apis_define_map, $swift_extension_funcBody_map)
528
+
483
529
  return $apis_define_map, $swift_extension_funcBody_map
484
530
  end
485
531
 
532
+ # 用 apis_define_map 中对应的 value,更新 swift_extension_funcBody_map 中相同 key 的 key, 避免AClass同时定义了类混淆且暴露给swift, swift 扩展使用原先未混淆的类的问题
533
+ def update_func_body_map_keys!(apis_define_map, swift_extension_funcBody_map)
534
+ new_map = {}
535
+
536
+ swift_extension_funcBody_map.each do |key, value|
537
+ if apis_define_map.has_key?(key)
538
+ confuse_value = apis_define_map[key]
539
+ new_key = confuse_value
540
+ new_map[new_key] = value
541
+ else
542
+ new_map[key] = value
543
+ end
544
+ end
545
+
546
+ swift_extension_funcBody_map.replace(new_map)
547
+ end
548
+
486
549
 
487
550
  def encrypted_api(apis)
488
551
  encrypted_api = ""
@@ -163,4 +163,114 @@ module ConfuseUtils
163
163
  end
164
164
  end
165
165
 
166
+ # Convert the Objective-C return type to a Swift type
167
+ def self.convert_oc_type_to_swift(return_type)
168
+ # 去除类型额外的修饰
169
+ is_nullable = (return_type =~ /\b(__nullable|nullable|_Nullable)\b/)
170
+ clean_type = return_type.gsub(/\b(__nullable|nullable|_Nullable|__nonnull|nonnull|_Nonnull)\b/, '').strip
171
+ # 去除类型末尾的指针符号和周围空格,生成基础类型名
172
+ base_type = clean_type.gsub(/(\w+)\s*\*+\z/, '\1').strip
173
+
174
+ # base_type = return_type
175
+ # puts "base_type = #{return_type}"
176
+
177
+ case base_type
178
+ # Foundation 基础类型
179
+ when "NSString" then "String"
180
+ when "NSMutableString" then "String" # Swift 中 String 是值类型
181
+ when "NSArray" then "Array<Any>"
182
+ when "NSMutableArray" then "Array<Any>"
183
+ when "NSSet" then "Set<Any>"
184
+ when "NSMutableSet" then "Set<Any>"
185
+ when "NSDictionary" then "Dictionary<AnyHashable, Any>"
186
+ when "NSMutableDictionary" then "Dictionary<AnyHashable, Any>"
187
+ when "NSError" then "Error?"
188
+ when "NSURL" then "URL"
189
+ when "NSData" then "Data"
190
+ when "NSNumber" then "NSNumber" # 特殊处理需要结合实际数值类型
191
+ when "NSDate" then "Date"
192
+
193
+ # 基础数值类型
194
+ when "NSInteger" then "Int"
195
+ when "NSUInteger" then "UInt"
196
+ when "CGFloat" then "CGFloat"
197
+ when "CGPoint" then "CGPoint"
198
+ when "CGRect" then "CGRect"
199
+ when "CGSize" then "CGSize"
200
+ when "BOOL" then "Bool"
201
+ when "Boolean" then "Bool"
202
+ when "double" then "Double"
203
+ when "float" then "Float"
204
+ when "long" then "Int"
205
+ when "long long" then "Int64"
206
+
207
+
208
+ # 特殊类型
209
+ when "id" then "AnyObject"
210
+ when "Class" then "AnyClass"
211
+ when "SEL" then "Selector"
212
+ when "Protocol" then "Protocol"
213
+ when "instancetype" then "Self"
214
+ when "void" then "Void"
215
+
216
+ # # # 函数指针/Block 类型(需二次处理)
217
+ # when /void\s*\(\^\)/ then convert_block_to_closure(return_type)
218
+ # when /(.+?)\s*\(\^\)/ then convert_block_to_closure(return_type)
219
+
220
+ # # 指针类型保留原始表示
221
+ # when /(.+?\s*\*+)/ then return_type
222
+
223
+ else
224
+ # # 处理Block类型
225
+ if clean_type.match(/^\s*(?:void|.+?)\s*\(\^\)/)
226
+ convert_block_to_closure(clean_type)
227
+ # 处理自定义的block
228
+ elsif clean_type =~ /(.*?[B|b]lock$)/
229
+ if is_nullable
230
+ "#{clean_type}?"
231
+ else
232
+ "@escaping #{clean_type}"
233
+ end
234
+ # 处理其他指针类型(去掉指针符号后返回基础类型名)
235
+ elsif clean_type.include?('*')
236
+ base_type
237
+ else
238
+ # 默认保留原类型
239
+ clean_type
240
+ end
241
+ end
242
+ end
243
+
244
+ # # 测试用例
245
+ # puts convert_block_to_closure('void (^)()') # 输出: () -> Void
246
+ # puts convert_block_to_closure('NSInteger (^)(BOOL)') # 输出: (Bool) -> Int
247
+ # puts convert_block_to_closure('NSString *(^)(NSInteger)') # 输出: (Int) -> String?
248
+ # puts convert_block_to_closure('void (^)(NSInteger, BOOL)') # 输出: (Int, Bool) -> Void
249
+ def self.convert_block_to_closure(block_signature)
250
+ # 正则表达式解释:
251
+ # ^([\w\s\*]+)\s*\(\^\)\s*(?:\((.*?)\))?\s*$
252
+ # 1. ([\w\s\*]+): 捕获返回值类型(如 void、NSInteger、NSString * 等)。
253
+ # 2. \(\^\): 匹配 '^' 表示这是一个 block。
254
+ # 3. (?:\((.*?)\))? : 可选地捕获参数列表(括号中的内容)。
255
+ if block_signature =~ /^([\w\s\*]+)\s*\(\^\)\s*(?:\((.*?)\))?\s*$/
256
+ return_type = $1.strip
257
+ params = $2 ? $2.split(',').map(&:strip) : []
258
+
259
+ # 转换返回值类型
260
+ swift_return_type = convert_oc_type_to_swift(return_type)
261
+
262
+ # 转换参数类型
263
+ swift_params = params.map do |param|
264
+ param_parts = param.split(' ')
265
+ param_type =convert_oc_type_to_swift(param_parts.first)
266
+ "#{param_type}"
267
+ end.join(", ").gsub("Void","")
268
+
269
+ # 构造最终的 Swift 闭包类型
270
+ result = "@escaping (#{swift_params}) -> #{swift_return_type}"
271
+ else
272
+ raise ArgumentError, "Invalid block signature: #{block_signature}"
273
+ end
274
+ end
275
+
166
276
  end
@@ -33,8 +33,11 @@ module Confuse
33
33
  # puts "param_section = #{param_section}"
34
34
  # puts "method_name = #{method_name}"
35
35
 
36
+ is_nullable = return_type.include?("nullable")
37
+
36
38
  # Convert return type to Swift equivalent
37
39
  return_type = convert_oc_type_to_swift(return_type)
40
+ return_type.gsub!(Regexp.union('nullable', 'nonnull', 'null_unspecified', 'null_resettable'), '')
38
41
 
39
42
  # Extract parameters from the method declaration
40
43
  params = extract_parameters(param_section)
@@ -44,77 +47,14 @@ module Confuse
44
47
  swift_method_declaration = "#{preFuncDes}public func #{method_name}(#{params.join(', ')})"
45
48
  swift_method_declaration += " -> #{return_type}" unless return_type == "Void"
46
49
 
47
- return swift_method_declaration, extract_parameter_names(param_section)
50
+ return is_nullable, return_type, swift_method_declaration, extract_parameter_names(param_section)
48
51
  end
49
52
 
50
53
  private
51
54
 
52
55
  # Convert the Objective-C return type to a Swift type
53
56
  def self.convert_oc_type_to_swift(return_type)
54
- # 去除类型末尾的指针符号和周围空格,生成基础类型名
55
- # base_type = return_type.gsub(/\s*\*+$/, '').strip
56
- base_type = return_type.gsub(/(\w+)\s*\*+\z/, '\1').strip
57
- # base_type = return_type
58
- # puts "base_type = #{return_type}"
59
-
60
- case base_type
61
- # Foundation 基础类型
62
- when "NSString" then "String"
63
- when "NSMutableString" then "String" # Swift 中 String 是值类型
64
- when "NSArray" then "Array<Any>"
65
- when "NSMutableArray" then "Array<Any>"
66
- when "NSSet" then "Set<Any>"
67
- when "NSMutableSet" then "Set<Any>"
68
- when "NSDictionary" then "Dictionary<AnyHashable, Any>"
69
- when "NSMutableDictionary" then "Dictionary<AnyHashable, Any>"
70
- when "NSError" then "Error?"
71
- when "NSURL" then "URL"
72
- when "NSData" then "Data"
73
- when "NSNumber" then "NSNumber" # 特殊处理需要结合实际数值类型
74
- when "NSDate" then "Date"
75
-
76
- # 基础数值类型
77
- when "NSInteger" then "Int"
78
- when "NSUInteger" then "UInt"
79
- when "CGFloat" then "CGFloat"
80
- when "CGPoint" then "CGPoint"
81
- when "CGRect" then "CGRect"
82
- when "CGSize" then "CGSize"
83
- when "BOOL" then "Bool"
84
- when "Boolean" then "Bool"
85
- when "double" then "Double"
86
- when "float" then "Float"
87
- when "long" then "Int"
88
- when "long long" then "Int64"
89
-
90
-
91
- # 特殊类型
92
- when "id" then "Any"
93
- when "Class" then "AnyClass"
94
- when "SEL" then "Selector"
95
- when "Protocol" then "Protocol"
96
- when "instancetype" then "Self"
97
- when "void" then "Void"
98
-
99
- # # # 函数指针/Block 类型(需二次处理)
100
- # when /void\s*\(\^\)/ then convert_block_to_closure(return_type)
101
- # when /(.+?)\s*\(\^\)/ then convert_block_to_closure(return_type)
102
-
103
- # # 指针类型保留原始表示
104
- # when /(.+?\s*\*+)/ then return_type
105
-
106
- else
107
- # # 处理Block类型
108
- if return_type.match(/^\s*(?:void|.+?)\s*\(\^\)/)
109
- convert_block_to_closure(return_type)
110
- # 处理其他指针类型(去掉指针符号后返回基础类型名)
111
- elsif return_type.include?('*')
112
- base_type
113
- else
114
- # 默认保留原类型
115
- return_type
116
- end
117
- end
57
+ return ConfuseUtils.convert_oc_type_to_swift(return_type)
118
58
  end
119
59
 
120
60
  # Extract parameters from the parameter section
@@ -160,37 +100,7 @@ module Confuse
160
100
  end
161
101
 
162
102
 
163
- # # 测试用例
164
- # puts convert_block_to_closure('void (^)()') # 输出: () -> Void
165
- # puts convert_block_to_closure('NSInteger (^)(BOOL)') # 输出: (Bool) -> Int
166
- # puts convert_block_to_closure('NSString *(^)(NSInteger)') # 输出: (Int) -> String?
167
- # puts convert_block_to_closure('void (^)(NSInteger, BOOL)') # 输出: (Int, Bool) -> Void
168
- def self.convert_block_to_closure(block_signature)
169
- # 正则表达式解释:
170
- # ^([\w\s\*]+)\s*\(\^\)\s*(?:\((.*?)\))?\s*$
171
- # 1. ([\w\s\*]+): 捕获返回值类型(如 void、NSInteger、NSString * 等)。
172
- # 2. \(\^\): 匹配 '^' 表示这是一个 block。
173
- # 3. (?:\((.*?)\))? : 可选地捕获参数列表(括号中的内容)。
174
- if block_signature =~ /^([\w\s\*]+)\s*\(\^\)\s*(?:\((.*?)\))?\s*$/
175
- return_type = $1.strip
176
- params = $2 ? $2.split(',').map(&:strip) : []
177
-
178
- # 转换返回值类型
179
- swift_return_type = convert_oc_type_to_swift(return_type)
180
-
181
- # 转换参数类型
182
- swift_params = params.map do |param|
183
- param_parts = param.split(' ')
184
- param_type =convert_oc_type_to_swift(param_parts.first)
185
- "#{param_type}"
186
- end.join(", ").gsub("Void","")
187
-
188
- # 构造最终的 Swift 闭包类型
189
- result = "@escaping (#{swift_params}) -> #{swift_return_type}"
190
- else
191
- raise ArgumentError, "Invalid block signature: #{block_signature}"
192
- end
193
- end
103
+
194
104
  end
195
105
  end
196
106
 
@@ -4,11 +4,12 @@ module Confuse
4
4
  #method_confuse 用来替换掉函数名 儒 BB_Confuse_asdasfas
5
5
  #swift 完整函数 如 @objc public func calculateAreaWithWidth(width: (Bool, String) -> Void, height: CGFloat) -> CGFloat
6
6
  #params 参数名数组 width, height
7
- def self.assembly(method_confuse,swift_method_declaration,params)
8
- is_return = true
7
+ def self.assembly(method_confuse,swift_method_declaration,params, is_nullable, return_type)
8
+ is_return = (return_type != "Void")
9
9
  is_static = swift_method_declaration.start_with?("static")
10
10
  caller = is_static ? "Self" : "self"
11
11
  returnFlag = is_return ? "return " : ""
12
+ optional = is_nullable ? "?" : ""
12
13
 
13
14
  params = params.map.with_index do |param, index|
14
15
  if index == 0
@@ -17,7 +18,17 @@ module Confuse
17
18
  "#{param}: #{param}"
18
19
  end
19
20
  end
20
- funcBody = "#{swift_method_declaration} {\n #{returnFlag}#{caller}.#{method_confuse}(#{params.join(', ')})\n}"
21
+ funcBody = "#{swift_method_declaration}#{optional} {\n #{returnFlag}#{caller}.#{method_confuse}(#{params.join(', ')})\n}"
22
+ return funcBody
23
+ end
24
+
25
+ #把oc属性组装成swift 计算属性
26
+ def self.assemblyProperty(property_origin, property_confuse, return_type, is_nullable)
27
+
28
+ final_return_type = (is_nullable ? "#{return_type}?" : return_type)
29
+
30
+ #如果是Any
31
+ funcBody = "public var #{property_origin} : #{final_return_type} {\n get {\n #{property_confuse}\n }\n set {\n self.#{property_confuse} = newValue\n }\n}\n"
21
32
  return funcBody
22
33
  end
23
34
  end
@@ -69,6 +69,11 @@ module Pod
69
69
  project = Xcodeproj::Project.open(File.basename(project_path))
70
70
  target = project.targets.first
71
71
  plist_path = target.resolved_build_setting('INFOPLIST_FILE', resolve_against: target).first.last
72
+
73
+ if project_path.nil? || plist_path.nil? || project_path.empty? || plist_path.empty?
74
+ return
75
+ end
76
+
72
77
  plist_path = File.join(File.dirname(project_path), plist_path)
73
78
  plist = Plist.parse_xml(plist_path)
74
79
  puts plist_path
@@ -76,6 +81,7 @@ module Pod
76
81
  unless plist.nil?
77
82
  is_confuse = plist['ISConfuse']
78
83
  if is_confuse.nil?
84
+ puts "None key for ISConfuse"
79
85
  # 没有 --confuse 命令和 Info 配置, 不处理
80
86
  elsif is_confuse == true
81
87
  enable_confuse()
@@ -1,3 +1,3 @@
1
1
  module CocoapodsPrivacy
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-privacy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - youhui
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-29 00:00:00.000000000 Z
11
+ date: 2025-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler