       }
     }
   }
+
+  // Copy updated specs to the `${HOME}/.cocoapods/` dir.
+  private static func copyPodspecs(from specsDir: URL, manifest: FirebaseManifest.Manifest) {
+    let path = specsDir.appendingPathComponent("*.{podspec,podspec.json}").path
+    let paths = Shell.executeCommandFromScript("ls \(path)", outputToConsole: false)
+    var candidateSpecs: [String]?
+    switch paths {
+    case let .error(code, output):
+      print("specs are not properly read, \(output)")
+    case let .success(output):
+      candidateSpecs = output.trimmingCharacters(in: .whitespacesAndNewlines)
+        .components(separatedBy: "\n")
+    }
+    guard let specs = candidateSpecs else {
+      print("There are no files ending with `podspec` or `podspec.json` detected.")
+      return
+    }
+    for spec in specs {
+      let specInfo = fetchPodVersion(from: URL(fileURLWithPath: spec))
+      // Create directories `${HOME}/.cocoapods/${Pod}/${version}`
+      let podDirURL = createPodDirctory(
+        specRepoPath: Constants.cocoapodsDir,
+        podName: specInfo.name,
+        version: specInfo.version
+      )
+      // Copy updated podspecs to directories `${HOME}/.cocoapods/${Pod}/${version}`
+      Shell.executeCommand("cp -rf \(spec) \(podDirURL)")
+    }
+  }
+
+  private static func fetchPodVersion(from path: URL) -> (name: String, version: String) {
+    var contents: String = ""
+    var podName: String = ""
+    var version: String = ""
+    do {
+      contents = try String(contentsOfFile: path.path, encoding: .utf8)
+    } catch {
+      fatalError("Could not read the podspec. \(error)")
+    }
+    // Closed source podspecs, e.g. GoogleAppMeasurement.podspec.json.
+    if path.pathExtension == "json" {
+      // Remove both extenstions of `podspec` and `json`.
+      podName = path.deletingPathExtension().deletingPathExtension().lastPathComponent
+    } else if path.pathExtension == "podspec" {
+      podName = path.deletingPathExtension().lastPathComponent
+    }
+
+    guard let versionPattern = Constants.versionFetchPatterns[path.pathExtension] else {
+      fatalError("Regex pattern for \(path.pathExtension) is not found.")
+    }
+
+    do {
+      version = try matchVersion(from: contents, withPattern: versionPattern)
+    } catch VersionFetchError.noMatchesCaught {
+      fatalError(
+        "Podspec from '\(path.path)' cannot find a version with the following regex\n\(versionPattern)"
+      )
+    } catch VersionFetchError.noSubgroupCaught {
+      fatalError(
+        "A subgroup of version from Podspec, '\(path.path)', is not caught from the pattern\n\(versionPattern)"
+      )
+    } catch VersionFetchError.multipleMatches {
+      print("found multiple version matches from \(path.path).")
+      fatalError(
+        "There should have only one version matching the regex pattern, please update the pattern\n\(versionPattern)"
+      )
+    } catch {
+      fatalError("Version is not caught properly. \(error)")
+    }
+    return (podName, version)
+  }
+
+  private static func matchVersion(from content: String,
+                                   withPattern regex: String) throws -> String {
+    let versionMatches = try content.match(regex: regex)
+    if versionMatches.isEmpty {
+      throw VersionFetchError.noMatchesCaught
+    }
+    // One subgroup in the regex should be for the version
+    else if versionMatches[0].count < 2 {
+      throw VersionFetchError.noSubgroupCaught
+    }
+    // There are more than one string matching the regex. There should be only
+    // one version matching the regex.
+    else if versionMatches.count > 1 {
+      print(versionMatches)
+      throw VersionFetchError.multipleMatches
+    }
+    return versionMatches[0][1]
+  }
+
+  private static func createPodDirctory(specRepoPath: String, podName: String,
+                                        version: String) -> URL {
+    guard let specRepoURL = URL(string: specRepoPath) else {
+      fatalError("\(specRepoPath) does not exist.")
+    }
+    let podDirPath = specRepoURL.appendingPathComponent(podName).appendingPathComponent(version)
+    if !FileManager.default.fileExists(atPath: podDirPath.absoluteString) {
+      do {
+        print("create path: \(podDirPath.absoluteString)")
+        try FileManager.default.createDirectory(atPath: podDirPath.absoluteString,
+                                                withIntermediateDirectories: true,
+                                                attributes: nil)
+      } catch {
+        print(error.localizedDescription)
+      }
+    }
+    return podDirPath
+  }
 }
+
+extension String: Error {
+  /// Returns an array of matching groups, which contains matched string and
+  /// subgroups.
+  ///
+  /// - Parameters:
+  ///   - regex: A string of regex.
+  /// - Returns: An array of array containing each match and its subgroups.
+  func match(regex: String) throws -> [[String]] {
+    do {
+      let regex = try NSRegularExpression(pattern: regex, options: [])
+      let nsString = self as NSString
+      let results = regex.matches(
+        in: self,
+        options: [],
+        range: NSMakeRange(0, nsString.length)
+      )
+      return results.map { result in
+        (0 ..< result.numberOfRanges).map {
+          nsString.substring(with: result.range(at: $0))
+        }
+      }
+    } catch {
+      fatalError("regex is invalid\n\(error.localizedDescription)")
+    }
+  }
+}


  // Add a testing tag to the head of the branch.
  private static func addTestingTag(path sdkRepoPath: URL, manifest: FirebaseManifest.Manifest) {
    let manifest = FirebaseManifest.shared
    let testingTag = Constants.testingTagPrefix + manifest.version
    // Add or update the testing tag to the local sdk repo.
    Shell.executeCommand("git tag -af \(testingTag) -m 'spectesting'", workingDir: sdkRepoPath)

  static let specRepo = "https://github.com/firebase/SpecsTesting"
  static let sdkRepo = "https://github.com/firebase/firebase-ios-sdk"
  static let testingTagPrefix = "testing-"
  static let cocoapodsDir =
    "\(ProcessInfo.processInfo.environment["HOME"]!)/.cocoapods/repos/\(localSpecRepoName)"
  static let versionFetchPatterns = [
    "json": "\"version\"[[:space:]]*:[[:space:]]*\"(.*)\"",
    "podspec": "\\.version[[:space:]]*=[[:space:]]*\'([^~=><].*)\'",
  ]
}

struct InitializeSpecTesting {
  enum VersionFetchError: Error {
    case noMatchesCaught
    case multipleMatches
    case noSubgroupCaught
  }

  static func setupRepo(sdkRepoURL: URL) {
    let manifest = FirebaseManifest.shared
    addSpecRepo(repoURL: Constants.specRepo)
    addTestingTag(path: sdkRepoURL, manifest: manifest)
    updatePodspecs(path: sdkRepoURL, manifest: manifest)
    copyPodspecs(from: sdkRepoURL, manifest: manifest)
  }

  // The SpecsTesting repo will be added to `${HOME}/.cocoapods/`, and all
