update-versions.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/usr/bin/python
  2. # Copyright 2018 Google
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """update-versions.py creates a release branch and commit with version updates.
  16. With the required --version parameter, this script will update all files in
  17. the repo based on the versions in Releases/Manifests/{version}.json.
  18. It will create a release branch, push and tag the updates, and push the
  19. updated podspecs to cpdc-internal.
  20. """
  21. import argparse
  22. import json
  23. import os
  24. import subprocess
  25. import sys
  26. import tempfile
  27. test_mode = False # Flag to disable external repo updates
  28. def SetupArguments():
  29. """SetupArguments sets up the set of command-line arguments.
  30. Returns:
  31. Args: The set of command line arguments
  32. """
  33. parser = argparse.ArgumentParser(description='Update Pod Versions')
  34. parser.add_argument('--version', required=True, help='Firebase version')
  35. parser.add_argument(
  36. '--test_mode',
  37. dest='test_mode',
  38. action='store_true',
  39. help='Log commands instead of updating public repo')
  40. parser.add_argument(
  41. '--tag_update',
  42. dest='tag_update',
  43. action='store_true',
  44. help='Update the tags only')
  45. parser.add_argument(
  46. '--base_branch',
  47. dest='base_branch',
  48. default='master',
  49. help='Base branch for new release')
  50. parser.add_argument(
  51. '--push_only',
  52. dest='push_only',
  53. action='store_true',
  54. help='Skip all of script except pushing podspecs to cpdc_internal')
  55. args = parser.parse_args()
  56. return args
  57. def LogOrRun(command):
  58. """Log or run a command depending on test_mode value.
  59. Args:
  60. command: command to log or run.
  61. """
  62. if test_mode:
  63. print 'Log only: {}'.format(command)
  64. else:
  65. os.system(command)
  66. def LogAndRun(command):
  67. """Log and run a command depending on test_mode value.
  68. Args:
  69. command: command to log and run.
  70. """
  71. print 'Log only: {}'.format(command)
  72. os.system(command)
  73. def GetVersionData(git_root, version):
  74. """Update version specifier in FIROptions.m.
  75. Args:
  76. git_root: root of git checkout.
  77. version: the next version to release.
  78. Returns:
  79. Dictionary with pod keys and version values.
  80. """
  81. json_file = os.path.join(git_root, 'Releases', 'Manifests',
  82. '{}.json'.format(version))
  83. if os.path.isfile(json_file):
  84. return json.load(open(json_file))
  85. else:
  86. sys.exit('Missing version file:{}'.format(json_file))
  87. def CreateReleaseBranch(release_branch, base_branch):
  88. """Create and push the release branch.
  89. Args:
  90. release_branch: the name of the git release branch.
  91. """
  92. os.system('git checkout {}'.format(base_branch))
  93. os.system('git pull')
  94. os.system('git checkout -b {}'.format(release_branch))
  95. LogOrRun('git push origin {}'.format(release_branch))
  96. LogOrRun('git branch --set-upstream-to=origin/{} {}'.format(release_branch,
  97. release_branch))
  98. def UpdatePodSpecs(git_root, version_data, firebase_version):
  99. """Update the podspecs with the right version.
  100. Args:
  101. git_root: root of git checkout.
  102. version_data: dictionary of versions to be updated.
  103. firebase_version: the Firebase version.
  104. """
  105. core_podspec = os.path.join(git_root, 'FirebaseCore.podspec')
  106. os.system("sed -i.bak -e \"s/\\(Firebase_VERSION=\\).*'/\\1{}'/\" {}".format(
  107. firebase_version, core_podspec))
  108. for pod, version in version_data.items():
  109. podspec = os.path.join(git_root, '{}.podspec'.format(pod))
  110. os.system("sed -i.bak -e \"s/\\(\\.version.*=[[:space:]]*'\\).*'/\\1{}'/\" "
  111. '{}'.format(version, podspec))
  112. def UpdatePodfiles(git_root, version):
  113. """Update Podfile's to reference the latest Firebase pod.
  114. Args:
  115. git_root: root of git checkout.
  116. version: the next Firebase version to release.
  117. """
  118. firebase_podfile = os.path.join(git_root, 'Example', 'Podfile')
  119. firestore_podfile = os.path.join(git_root, 'Firestore', 'Example', 'Podfile')
  120. collision_podfile = os.path.join(git_root, 'SymbolCollisionTest', 'Podfile')
  121. sed_command = ("sed -i.bak -e \"s#\\(pod "
  122. "'Firebase/CoreOnly',[[:space:]]*'\\).*'#\\1{}'#\" {}")
  123. os.system(sed_command.format(version, firebase_podfile))
  124. os.system(sed_command.format(version, firestore_podfile))
  125. sed_command = ("sed -i.bak -e \"s#\\(pod "
  126. "'Firebase',[[:space:]]*'\\).*'#\\1{}'#\" {}")
  127. os.system(sed_command.format(version, collision_podfile))
  128. def GenerateTag(pod, version):
  129. """ Generate a tag from a pod and a version.
  130. Args:
  131. pod: name of the pod for which to generate the tag.
  132. version: version of the pod to tag.
  133. Returns:
  134. Tag.
  135. """
  136. if pod == "Firebase":
  137. return version
  138. if pod.startswith("Firebase"):
  139. return '{}-{}'.format(pod[len('Firebase'):], version)
  140. if pod.startswith("Google"):
  141. return '{}-{}'.format(pod[len('Google'):], version)
  142. sys.exit("Script does not support generating a tag for {}".format(pod))
  143. def UpdateTags(version_data, first=False):
  144. """Update tags.
  145. Args:
  146. version_data: dictionary of versions to be updated.
  147. firebase_version: the Firebase version.
  148. first: set to true the first time the versions are set.
  149. """
  150. for pod, version in version_data.items():
  151. tag = GenerateTag(pod, version)
  152. if not first:
  153. LogOrRun("git push --delete origin '{}'".format(tag))
  154. LogOrRun("git tag --delete '{}'".format(tag))
  155. LogOrRun("git tag '{}'".format(tag))
  156. LogOrRun("git push origin '{}'".format(tag))
  157. def CheckVersions(version_data, firebase_version):
  158. """Ensure that versions do not already exist as tags.
  159. Args:
  160. version_data: dictionary of versions to be updated.
  161. """
  162. error = False
  163. for pod, version in version_data.items():
  164. tag = GenerateTag(pod, version)
  165. if pod == "Firebase":
  166. if version != firebase_version:
  167. sys.exit("Aborting: Release version must match Firebase pod version.")
  168. find = subprocess.Popen(
  169. ['git', 'tag', '-l', tag],
  170. stdout=subprocess.PIPE).communicate()[0].rstrip()
  171. if tag == find:
  172. print "{} tag already exists".format(tag)
  173. error = True
  174. if error:
  175. sys.exit("Aborting: Remove pre-existing tags and retry")
  176. def GetCpdcInternal():
  177. """Find the firebase repo.
  178. """
  179. tmp_file = tempfile.mktemp()
  180. os.system('pod repo list | grep -B2 sso://cpdc-internal/firebase | head -1 > {}'
  181. .format(tmp_file))
  182. with open(tmp_file,'r') as o:
  183. output_var = ''.join(o.readlines()).strip()
  184. os.system('rm -rf {}'.format(tmp_file))
  185. return output_var
  186. # TODO update PushPodspecs to push dependent pods first, like when FirebaseCore
  187. # depends on the most recent version of GoogleUtilities.
  188. def PushPodspecs(version_data):
  189. """Push podspecs to cpdc-firebase.
  190. Args:
  191. version_data: dictionary of versions to be updated.
  192. """
  193. pods = version_data.keys()
  194. for pod in pods:
  195. LogOrRun('pod cache clean {} --all'.format(pod))
  196. if pod == 'Firebase':
  197. # Do the Firebase pod last
  198. continue
  199. if pod == 'FirebaseFirestore':
  200. warnings_ok = ' --allow-warnings'
  201. else:
  202. warnings_ok = ''
  203. podspec = '{}.podspec'.format(pod)
  204. LogAndRun('pod repo push --skip-tests --use-json {} {}{}'
  205. .format(GetCpdcInternal(), podspec, warnings_ok))
  206. # This command will need to be rerun if any pods need to be pushed from Rapid.
  207. LogAndRun('pod repo push --skip-tests --use-json --skip-import-validation ' +
  208. '--sources=sso://cpdc-internal/firebase.git,https://cdn.cocoapods.org' +
  209. ' {} Firebase.podspec {}'.format(GetCpdcInternal(), warnings_ok))
  210. def UpdateVersions():
  211. """UpdateVersions is the main body to create the branch and change versions.
  212. """
  213. global test_mode
  214. args = SetupArguments()
  215. test_mode = args.test_mode
  216. # Validate version is proper format
  217. major, minor, patch = args.version.split('.')
  218. if (not major.isdigit()) or (not minor.isdigit()) or (not patch.isdigit()):
  219. sys.exit('Invalid version parameter')
  220. git_root = subprocess.Popen(
  221. ['git', 'rev-parse', '--show-toplevel'],
  222. stdout=subprocess.PIPE).communicate()[0].rstrip().decode('utf-8')
  223. version_data = GetVersionData(git_root, args.version)
  224. if not args.push_only:
  225. if args.tag_update:
  226. UpdateTags(version_data)
  227. return
  228. CheckVersions(version_data, args.version)
  229. release_branch = 'release-{}'.format(args.version)
  230. CreateReleaseBranch(release_branch, args.base_branch)
  231. UpdatePodSpecs(git_root, version_data, args.version)
  232. UpdatePodfiles(git_root, args.version)
  233. LogOrRun('git commit -am "Update versions for Release {}"'
  234. .format(args.version))
  235. LogOrRun('git push origin {}'.format(release_branch))
  236. UpdateTags(version_data, True)
  237. PushPodspecs(version_data)
  238. if __name__ == '__main__':
  239. UpdateVersions()