-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
One version release process #6724
Changes from all commits
7afc091
14312f2
55f644f
b63b8d9
0e25828
a098a53
20c112f
ef97538
3dde28b
071175d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// The manifest contents for a release. | ||
/// Version should be updated every release. | ||
/// The version and releasing fields of the non-Firebase pods should be reviewed every release. | ||
public let shared = Manifest( | ||
version: "7.0.0", | ||
pods: [ | ||
Pod("GoogleUtilities", isFirebase: false, podVersion: "7.0.0", releasing: false), | ||
Pod("GoogleDataTransport", isFirebase: false, podVersion: "8.0.0", releasing: true), | ||
|
||
Pod("FirebaseCoreDiagnostics"), | ||
Pod("FirebaseCore"), | ||
Pod("FirebaseInstallations"), | ||
Pod("FirebaseInstanceID"), | ||
Pod("GoogleAppMeasurement", isClosedSource: true), | ||
Pod("FirebaseAnalytics", isClosedSource: true), | ||
Pod("FirebaseABTesting"), | ||
Pod("FirebaseAppDistribution", isBeta: true), | ||
Pod("FirebaseAuth"), | ||
Pod("FirebaseCrashlytics"), | ||
Pod("FirebaseDatabase"), | ||
Pod("FirebaseDynamicLinks"), | ||
Pod("FirebaseFirestore", allowWarnings: true), | ||
Pod("FirebaseFirestoreSwift", isBeta: true), | ||
Pod("FirebaseFunctions"), | ||
Pod("FirebaseInAppMessaging", isBeta: true), | ||
Pod("FirebaseMessaging"), | ||
Pod("FirebasePerformance", isClosedSource: true), | ||
Pod("FirebaseRemoteConfig"), | ||
Pod("FirebaseStorage"), | ||
Pod("FirebaseStorageSwift", isBeta: true), | ||
Pod("FirebaseMLCommon", isClosedSource: true, isBeta: true), | ||
Pod("FirebaseMLModelInterpreter", isClosedSource: true, isBeta: true), | ||
Pod("FirebaseMLVision", isClosedSource: true, isBeta: true), | ||
Pod("Firebase", allowWarnings: true), | ||
] | ||
) | ||
|
||
/// Manifest describing the contents of a Firebase release. | ||
public struct Manifest { | ||
public let version: String | ||
public let pods: [Pod] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// Struct describing Firebase pods to release. | ||
public struct Pod { | ||
public let name: String | ||
public let isClosedSource: Bool | ||
public let isBeta: Bool | ||
public let isFirebase: Bool | ||
public let allowWarnings: Bool // Allow validation warnings. Ideally these should all be false | ||
public let podVersion: String? // Non-Firebase pods have their own version | ||
public let releasing: Bool // Non-Firebase pods may not release | ||
|
||
init(_ name: String, | ||
isClosedSource: Bool = false, | ||
isBeta: Bool = false, | ||
isFirebase: Bool = true, | ||
allowWarnings: Bool = false, | ||
podVersion: String? = nil, | ||
releasing: Bool = true) { | ||
self.name = name | ||
self.isClosedSource = isClosedSource | ||
self.isBeta = isBeta | ||
self.isFirebase = isFirebase | ||
self.allowWarnings = allowWarnings | ||
self.podVersion = podVersion | ||
self.releasing = releasing | ||
} | ||
|
||
public func podspecName() -> String { | ||
return isClosedSource ? "\(name).podspec.json" : "\(name).podspec" | ||
} | ||
|
||
/// Closed source pods do not validate on Xcode 12 until they support the ARM simulator slice. | ||
public func skipImportValidation() -> String { | ||
if isClosedSource || name == "Firebase" { | ||
return "-skip-import-validation" | ||
} else { | ||
return "" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
import FirebaseManifest | ||
import Utils | ||
|
||
struct InitializeRelease { | ||
static func setupRepo(gitRoot: URL) -> String { | ||
let manifest = FirebaseManifest.shared | ||
let branch = createReleaseBranch(path: gitRoot, version: manifest.version) | ||
updatePodspecs(path: gitRoot, manifest: manifest) | ||
updatePodfiles(path: gitRoot, version: manifest.version) | ||
return branch | ||
} | ||
|
||
/// The branch is based on the minor version to represent this is the branch for subsequent | ||
/// patches. | ||
private static func createReleaseBranch(path: URL, version: String) -> String { | ||
let versionParts = version.split(separator: ".") | ||
let minorVersion = "\(versionParts[0]).\(versionParts[1])" | ||
let branch = "release-\(minorVersion)" | ||
Shell.executeCommand("git checkout master", workingDir: path) | ||
Shell.executeCommand("git pull", workingDir: path) | ||
Shell.executeCommand("git checkout -b \(branch)", workingDir: path) | ||
return branch | ||
} | ||
|
||
/// Update the podspec versions. | ||
private static func updatePodspecs(path: URL, manifest: FirebaseManifest.Manifest) { | ||
for pod in manifest.pods { | ||
if !pod.isClosedSource { | ||
if pod.name == "Firebase" { | ||
updateFirebasePodspec(path: path, manifest: manifest) | ||
} else { | ||
let version = pod.podVersion ?? | ||
(pod.isBeta ? manifest.version + "-beta" : manifest.version) | ||
|
||
// Patch the new version to the podspec's version attribute. | ||
Shell.executeCommand("sed -i.bak -e \"s/\\(\\.version.*=[[:space:]]*'\\).*'/\\1" + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would appreciate a brief command on what this command is doing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
"\(version)'/\" \(pod.name).podspec", workingDir: path) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// This function patches the versions in the Firebase.podspec. It uses Swift instead of sed | ||
// like the other version patching. | ||
// TODO: Choose one or the other mechanism. | ||
// TODO: If we keep Swift, consider using Scanner. | ||
private static func updateFirebasePodspec(path: URL, manifest: FirebaseManifest.Manifest) { | ||
let podspecFile = path.appendingPathComponent("Firebase.podspec") | ||
var contents = "" | ||
do { | ||
contents = try String(contentsOfFile: podspecFile.path, encoding: .utf8) | ||
} catch { | ||
fatalError("Could not read Firebase podspec. \(error)") | ||
} | ||
let firebaseVersion = manifest.version | ||
for firebasePod in manifest.pods { | ||
if !firebasePod.isFirebase { | ||
continue | ||
} | ||
let pod = firebasePod.name | ||
let version = firebasePod.isBeta ? firebaseVersion + "-beta" : firebaseVersion | ||
if pod == "Firebase" { | ||
// TODO: This then block is redundant with the updatePodspecs function above and is left | ||
// until we decide to go with Swift or sed. | ||
// Replace version in string like s.version = '6.9.0' | ||
guard let range = contents.range(of: "s.version") else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: I would move content of if-else block to separate methods. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is mostly unchanged from the old Firebase podspec updater. See deletion below. I added some comments about consolidating to either always using more compact sed commands or potentially more readable Swift. Deferring for now. |
||
fatalError("Could not find version of Firebase pod in podspec at \(podspecFile)") | ||
} | ||
var versionStartIndex = contents.index(range.upperBound, offsetBy: 1) | ||
while contents[versionStartIndex] != "'" { | ||
versionStartIndex = contents.index(versionStartIndex, offsetBy: 1) | ||
} | ||
var versionEndIndex = contents.index(versionStartIndex, offsetBy: 1) | ||
while contents[versionEndIndex] != "'" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added TODO. |
||
versionEndIndex = contents.index(versionEndIndex, offsetBy: 1) | ||
} | ||
contents.removeSubrange(versionStartIndex ... versionEndIndex) | ||
contents.insert(contentsOf: "'" + version + "'", at: versionStartIndex) | ||
} else { | ||
// Replace version in string like ss.dependency 'FirebaseCore', '6.3.0' | ||
guard let range = contents.range(of: pod) else { | ||
// This pod is not a top-level Firebase pod dependency. | ||
continue | ||
} | ||
var versionStartIndex = contents.index(range.upperBound, offsetBy: 2) | ||
while !contents[versionStartIndex].isWholeNumber { | ||
versionStartIndex = contents.index(versionStartIndex, offsetBy: 1) | ||
} | ||
var versionEndIndex = contents.index(versionStartIndex, offsetBy: 1) | ||
while contents[versionEndIndex] != "'" { | ||
versionEndIndex = contents.index(versionEndIndex, offsetBy: 1) | ||
} | ||
contents.removeSubrange(versionStartIndex ... versionEndIndex) | ||
contents.insert(contentsOf: version + "'", at: versionStartIndex) | ||
} | ||
} | ||
do { | ||
try contents.write(to: podspecFile, atomically: false, encoding: .utf8) | ||
} catch { | ||
fatalError("Failed to write \(podspecFile.path). \(error)") | ||
} | ||
} | ||
|
||
private static func updatePodfiles(path: URL, version: String) { | ||
// Update the Podfiles across the repo. | ||
let firestorePodfile = path.appendingPathComponent("Firestore") | ||
.appendingPathComponent("Example") | ||
let collisionPodfile = path.appendingPathComponent("SymbolCollisionTest") | ||
let sedCommand = "sed -i.bak -e \"s#\\(pod " + | ||
"'Firebase/CoreOnly',[[:space:]]*'\\).*'#\\1\(version)'#\" Podfile" | ||
Shell.executeCommand(sedCommand, workingDir: firestorePodfile) | ||
|
||
let sedCommand2 = "sed -i.bak -e \"s#\\(pod " + | ||
"'Firebase',[[:space:]]*'\\).*'#\\1\(version)'#\" Podfile" | ||
Shell.executeCommand(sedCommand2, workingDir: collisionPodfile) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
import FirebaseManifest | ||
import Utils | ||
|
||
enum Push { | ||
static func pushPodsToCPDC(gitRoot: URL) { | ||
let cpdcLocation = findCpdc(gitRoot: gitRoot) | ||
let manifest = FirebaseManifest.shared | ||
|
||
for pod in manifest.pods { | ||
if !pod.releasing { | ||
continue | ||
} | ||
let warningsOK = pod.allowWarnings ? " --allow-warnings" : "" | ||
|
||
Shell.executeCommand("pod repo push --skip-tests --use-json \(warningsOK) \(cpdcLocation) " + | ||
pod.skipImportValidation() + " \(pod.podspecName()) " + | ||
"--sources=sso://cpdc-internal/firebase.git,https://cdn.cocoapods.org", | ||
workingDir: gitRoot) | ||
} | ||
} | ||
|
||
private static func findCpdc(gitRoot: URL) -> String { | ||
let command = "pod repo list | grep -B2 sso://cpdc-internal/firebase | head -1" | ||
let result = Shell.executeCommandFromScript(command, workingDir: gitRoot) | ||
switch result { | ||
case let .error(code, output): | ||
fatalError(""" | ||
`pod --version` failed with exit code \(code) | ||
Output from `pod repo list`: | ||
\(output) | ||
""") | ||
case let .success(output): | ||
print(output) | ||
return output.trimmingCharacters(in: .whitespacesAndNewlines) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
import FirebaseManifest | ||
import Utils | ||
|
||
enum Tags { | ||
static func create(gitRoot: URL) { | ||
let manifest = FirebaseManifest.shared | ||
createTag(gitRoot: gitRoot, tag: "CocoaPods-\(manifest.version)") | ||
createTag(gitRoot: gitRoot, tag: "CocoaPods-\(manifest.version)-beta") | ||
|
||
for pod in manifest.pods { | ||
if pod.isFirebase { | ||
continue | ||
} | ||
if !pod.name.starts(with: "Google") { | ||
fatalError("Unrecognized Other Pod: \(pod.name). Only Google prefix is recognized") | ||
} | ||
guard let version = pod.podVersion else { | ||
fatalError("Non-Firebase pod \(pod.name) is missing a version") | ||
} | ||
let tag = pod.name.replacingOccurrences(of: "Google", with: "") + "-" + version | ||
createTag(gitRoot: gitRoot, tag: tag) | ||
} | ||
} | ||
|
||
private static func createTag(gitRoot: URL, tag: String) { | ||
Shell.executeCommand("git tag \(tag)", workingDir: gitRoot) | ||
Shell.executeCommand("git push origin \(tag)", workingDir: gitRoot) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: IMO the manifest declaration should be in a separate file to reduce the noise when reading the manifest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.