@@ -26,14 +26,27 @@ extension Constants {
26
26
static let specRepo = " https://github.com/firebase/SpecsTesting "
27
27
static let sdkRepo = " https://github.com/firebase/firebase-ios-sdk "
28
28
static let testingTagPrefix = " testing- "
29
+ static let cocoapodsDir =
30
+ " \( ProcessInfo . processInfo. environment [ " HOME " ] !) /.cocoapods/repos/ \( localSpecRepoName) "
31
+ static let versionFetchPatterns = [
32
+ " json " : " \" version \" [[:space:]]*:[[:space:]]* \" (.*) \" " ,
33
+ " podspec " : " \\ .version[[:space:]]*=[[:space:]]* \' ([^~=><].*) \' " ,
34
+ ]
29
35
}
30
36
31
37
struct InitializeSpecTesting {
38
+ enum VersionFetchError : Error {
39
+ case noMatchesCaught
40
+ case multipleMatches
41
+ case noSubgroupCaught
42
+ }
43
+
32
44
static func setupRepo( sdkRepoURL: URL ) {
33
45
let manifest = FirebaseManifest . shared
34
46
addSpecRepo ( repoURL: Constants . specRepo)
35
47
addTestingTag ( path: sdkRepoURL, manifest: manifest)
36
48
updatePodspecs ( path: sdkRepoURL, manifest: manifest)
49
+ copyPodspecs ( from: sdkRepoURL, manifest: manifest)
37
50
}
38
51
39
52
// The SpecsTesting repo will be added to `${HOME}/.cocoapods/`, and all
@@ -53,7 +66,6 @@ struct InitializeSpecTesting {
53
66
54
67
// Add a testing tag to the head of the branch.
55
68
private static func addTestingTag( path sdkRepoPath: URL , manifest: FirebaseManifest . Manifest ) {
56
- let manifest = FirebaseManifest . shared
57
69
let testingTag = Constants . testingTagPrefix + manifest. version
58
70
// Add or update the testing tag to the local sdk repo.
59
71
Shell . executeCommand ( " git tag -af \( testingTag) -m 'spectesting' " , workingDir: sdkRepoPath)
@@ -83,4 +95,140 @@ struct InitializeSpecTesting {
83
95
}
84
96
}
85
97
}
98
+
99
+ // Copy updated specs to the `${HOME}/.cocoapods/` dir.
100
+ private static func copyPodspecs( from specsDir: URL , manifest: FirebaseManifest . Manifest ) {
101
+ let path = specsDir. appendingPathComponent ( " *.{podspec,podspec.json} " ) . path
102
+ let paths = Shell . executeCommandFromScript ( " ls \( path) " , outputToConsole: false )
103
+ var candidateSpecs : [ String ] ?
104
+ switch paths {
105
+ case let . error( code, output) :
106
+ print ( " specs are not properly read, \( output) " )
107
+ case let . success( output) :
108
+ candidateSpecs = output. trimmingCharacters ( in: . whitespacesAndNewlines)
109
+ . components ( separatedBy: " \n " )
110
+ }
111
+ guard let specs = candidateSpecs else {
112
+ print ( " There are no files ending with `podspec` or `podspec.json` detected. " )
113
+ return
114
+ }
115
+ for spec in specs {
116
+ let specInfo = fetchPodVersion ( from: URL ( fileURLWithPath: spec) )
117
+ // Create directories `${HOME}/.cocoapods/${Pod}/${version}`
118
+ let podDirURL = createPodDirctory (
119
+ specRepoPath: Constants . cocoapodsDir,
120
+ podName: specInfo. name,
121
+ version: specInfo. version
122
+ )
123
+ // Copy updated podspecs to directories `${HOME}/.cocoapods/${Pod}/${version}`
124
+ Shell . executeCommand ( " cp -rf \( spec) \( podDirURL) " )
125
+ }
126
+ }
127
+
128
+ private static func fetchPodVersion( from path: URL ) -> ( name: String , version: String ) {
129
+ var contents : String = " "
130
+ var podName : String = " "
131
+ var version : String = " "
132
+ do {
133
+ contents = try String ( contentsOfFile: path. path, encoding: . utf8)
134
+ } catch {
135
+ fatalError ( " Could not read the podspec. \( error) " )
136
+ }
137
+ // Closed source podspecs, e.g. GoogleAppMeasurement.podspec.json.
138
+ if path. pathExtension == " json " {
139
+ // Remove both extenstions of `podspec` and `json`.
140
+ podName = path. deletingPathExtension ( ) . deletingPathExtension ( ) . lastPathComponent
141
+ } else if path. pathExtension == " podspec " {
142
+ podName = path. deletingPathExtension ( ) . lastPathComponent
143
+ }
144
+
145
+ guard let versionPattern = Constants . versionFetchPatterns [ path. pathExtension] else {
146
+ fatalError ( " Regex pattern for \( path. pathExtension) is not found. " )
147
+ }
148
+
149
+ do {
150
+ version = try matchVersion ( from: contents, withPattern: versionPattern)
151
+ } catch VersionFetchError . noMatchesCaught {
152
+ fatalError (
153
+ " Podspec from ' \( path. path) ' cannot find a version with the following regex \n \( versionPattern) "
154
+ )
155
+ } catch VersionFetchError . noSubgroupCaught {
156
+ fatalError (
157
+ " A subgroup of version from Podspec, ' \( path. path) ', is not caught from the pattern \n \( versionPattern) "
158
+ )
159
+ } catch VersionFetchError . multipleMatches {
160
+ print ( " found multiple version matches from \( path. path) . " )
161
+ fatalError (
162
+ " There should have only one version matching the regex pattern, please update the pattern \n \( versionPattern) "
163
+ )
164
+ } catch {
165
+ fatalError ( " Version is not caught properly. \( error) " )
166
+ }
167
+ return ( podName, version)
168
+ }
169
+
170
+ private static func matchVersion( from content: String ,
171
+ withPattern regex: String ) throws -> String {
172
+ let versionMatches = try content. match ( regex: regex)
173
+ if versionMatches. isEmpty {
174
+ throw VersionFetchError . noMatchesCaught
175
+ }
176
+ // One subgroup in the regex should be for the version
177
+ else if versionMatches [ 0 ] . count < 2 {
178
+ throw VersionFetchError . noSubgroupCaught
179
+ }
180
+ // There are more than one string matching the regex. There should be only
181
+ // one version matching the regex.
182
+ else if versionMatches. count > 1 {
183
+ print ( versionMatches)
184
+ throw VersionFetchError . multipleMatches
185
+ }
186
+ return versionMatches [ 0 ] [ 1 ]
187
+ }
188
+
189
+ private static func createPodDirctory( specRepoPath: String , podName: String ,
190
+ version: String ) -> URL {
191
+ guard let specRepoURL = URL ( string: specRepoPath) else {
192
+ fatalError ( " \( specRepoPath) does not exist. " )
193
+ }
194
+ let podDirPath = specRepoURL. appendingPathComponent ( podName) . appendingPathComponent ( version)
195
+ if !FileManager. default. fileExists ( atPath: podDirPath. absoluteString) {
196
+ do {
197
+ print ( " create path: \( podDirPath. absoluteString) " )
198
+ try FileManager . default. createDirectory ( atPath: podDirPath. absoluteString,
199
+ withIntermediateDirectories: true ,
200
+ attributes: nil )
201
+ } catch {
202
+ print ( error. localizedDescription)
203
+ }
204
+ }
205
+ return podDirPath
206
+ }
207
+ }
208
+
209
+ extension String : Error {
210
+ /// Returns an array of matching groups, which contains matched string and
211
+ /// subgroups.
212
+ ///
213
+ /// - Parameters:
214
+ /// - regex: A string of regex.
215
+ /// - Returns: An array of array containing each match and its subgroups.
216
+ func match( regex: String ) throws -> [ [ String ] ] {
217
+ do {
218
+ let regex = try NSRegularExpression ( pattern: regex, options: [ ] )
219
+ let nsString = self as NSString
220
+ let results = regex. matches (
221
+ in: self ,
222
+ options: [ ] ,
223
+ range: NSMakeRange ( 0 , nsString. length)
224
+ )
225
+ return results. map { result in
226
+ ( 0 ..< result. numberOfRanges) . map {
227
+ nsString. substring ( with: result. range ( at: $0) )
228
+ }
229
+ }
230
+ } catch {
231
+ fatalError ( " regex is invalid \n \( error. localizedDescription) " )
232
+ }
233
+ }
86
234
}
0 commit comments