34
34
Usage:
35
35
# Dryrun (does not update any files on disk but prints out all replacements for
36
36
# preview) - Update versions in default set of files in the repository.
37
- python3 scripts/update_ios_android_dependencies .py --dryrun
37
+ python3 scripts/update_android_ios_dependencies .py --dryrun
38
38
39
- # Update versions in default set of files in the repository
40
- python3 scripts/update_ios_android_dependencies.py
39
+ # Update versions in default set of files in the repository.
40
+ python3 scripts/update_android_ios_dependencies.py
41
+
42
+ # Update versions in default set of files in the repository. (allow updating to
43
+ # experimental versions)
44
+ python3 scripts/update_android_ios_dependencies.py --allow_experimental
41
45
42
46
# Update only Android packages
43
- python3 scripts/update_ios_android_dependencies .py --skip_ios
47
+ python3 scripts/update_android_ios_dependencies .py --skip_ios
44
48
45
49
# Update specific pod files (or directories containing pod files)
46
- python3 scripts/update_ios_android_dependencies .py --podfiles foo/Podfile
50
+ python3 scripts/update_android_ios_dependencies .py --podfiles foo/Podfile
47
51
dir_with_podfiles
48
52
53
+ # Update all Android packages except any names containing androidx and auth
54
+ python3 scripts/update_android_ios_dependencies.py --skip_ios
55
+ --ignore_android_packages auth androidx
56
+
57
+ # Ignore updating any files in gameloop_android and test directories
58
+ python3 scripts/update_android_ios_dependencies.py --ignore_directories test
59
+ gameloop_android
60
+
49
61
Other similar flags:
50
62
--depfiles
51
63
--readmefiles
66
78
import tempfile
67
79
68
80
from collections import defaultdict
69
- from pkg_resources import packaging
70
81
from xml .etree import ElementTree
82
+ from pkg_resources import packaging
71
83
72
84
73
85
def get_files_from_directory (dirpath , file_extension , file_name = None ,
74
- absolute_paths = True ):
86
+ absolute_paths = True , ignore_directories = None ):
75
87
"""Helper function to filter files in directories.
76
88
77
89
Args:
@@ -83,27 +95,35 @@ def get_files_from_directory(dirpath, file_extension, file_name=None,
83
95
absolute_paths (bool, optional): Return absolute paths to files.
84
96
Defaults to True.
85
97
If False, just filenames are returned.
98
+ ignore_directories (list[str], optional): Directory names to ignore.
99
+ Eg: ['gameloop_android', 'do_not_scan_this_directory']
86
100
87
101
Returns:
88
102
list(str): List of files matching the specified criteria.
89
103
List of filenames (if absolute_paths=False), or
90
104
a list of absolute paths (if absolute_paths=True)
91
105
"""
106
+ if ignore_directories is None :
107
+ ignore_directories = []
108
+
92
109
files = []
93
- for dirpath , _ , filenames in os .walk (dirpath ):
110
+ for root , dirs , filenames in os .walk (dirpath ):
111
+ dirs [:] = [directory for directory in dirs
112
+ if directory not in ignore_directories ]
94
113
for filename in filenames :
95
114
if not filename .endswith (file_extension ):
96
115
continue
97
116
if file_name and not file_name == filename :
98
117
continue
99
118
if absolute_paths :
100
- files .append (os .path .join (dirpath , filename ))
119
+ files .append (os .path .join (root , filename ))
101
120
else :
102
121
files .append (filename )
103
122
return files
104
123
105
124
106
- def get_files (dirs_and_files , file_extension , file_name = None ):
125
+ def get_files (dirs_and_files , file_extension , file_name = None ,
126
+ ignore_directories = None ):
107
127
"""Gets the final flat list of files after searching directories.
108
128
109
129
If a directory is passed, it is searched recursively.
@@ -115,6 +135,8 @@ def get_files(dirs_and_files, file_extension, file_name=None):
115
135
Eg: '.gradle'
116
136
file_name (str, optional): Exact file name to search for.
117
137
Defaults to None. Eg: 'foo.gradle'
138
+ ignore_directories (list[str], optional): Directory names to ignore.
139
+ Eg: ['gameloop_android', 'do_not_scan_this_directory']
118
140
119
141
Returns:
120
142
iterable(str): Final list of files after recursively searching dirs.
@@ -125,13 +147,17 @@ def get_files(dirs_and_files, file_extension, file_name=None):
125
147
if not os .path .exists (abspath ):
126
148
continue
127
149
if os .path .isdir (abspath ):
128
- files = files + get_files_from_directory (abspath ,
129
- file_extension = file_extension ,
130
- file_name = file_name )
150
+ files = files + get_files_from_directory (
151
+ abspath ,
152
+ file_extension = file_extension ,
153
+ file_name = file_name ,
154
+ ignore_directories = ignore_directories )
131
155
elif os .path .isfile (abspath ):
132
156
files .append (abspath )
133
157
return files
134
158
159
+ # Regex to match versions with just digits (ignoring things like -alpha, -beta)
160
+ RE_NON_EXPERIMENTAL_VERSION = re .compile ('[0-9.]+$' )
135
161
136
162
########## iOS pods versions update #######################################
137
163
@@ -158,47 +184,73 @@ def get_files(dirs_and_files, file_extension, file_name=None):
158
184
)
159
185
160
186
161
- def get_pod_versions (specs_repo , pods = PODS ):
187
+ def get_pod_versions (specs_repo , pods = PODS , ignore_pods = None ,
188
+ allow_experimental = False ):
162
189
"""Get available pods and their versions from the specs repo
163
190
164
191
Args:
165
192
local_repo_dir (str): Directory mirroring Cocoapods specs repo
166
193
pods (iterable(str), optional): List of pods whose versions we need.
167
194
Defaults to PODS.
168
-
195
+ ignore_pods (list[str], optional): Case insensitive list of substrings
196
+ If any of these substrings are present in the pod name, it will not be
197
+ updated.
198
+ Eg: ['foo', 'bar'] will ignore all pods that have 'foo' or
199
+ 'bar' in their name. For example, 'test_foo', 'test_foo_baz'
200
+ allow_experimental (bool): Allow experimental versions.
201
+ Eg: 1.2.3-alpha, 1.2.3-beta, 1.2.3-rc
169
202
Returns:
170
203
dict: Map of the form {<str>:list(str)}
171
204
Containing a mapping of podnames to available versions.
172
205
"""
206
+ if ignore_pods is None :
207
+ ignore_pods = []
208
+
173
209
all_versions = defaultdict (list )
174
210
logging .info ('Fetching pod versions from Specs repo...' )
175
211
podspec_files = get_files_from_directory (specs_repo ,
176
212
file_extension = '.podspec.json' )
177
213
for podspec_file in podspec_files :
178
214
filename = os .path .basename (podspec_file )
179
- # Example: FirebaseAuth.podspec.json
215
+ # Example: FirebaseAuth.podspec.json --> FirebaseAuth
180
216
podname = filename .split ('.' )[0 ]
181
- if not podname in pods :
217
+ if podname not in pods :
218
+ continue
219
+ if any (ignore_pod .lower () in podname .lower () for ignore_pod in ignore_pods ):
182
220
continue
183
221
parent_dir = os .path .dirname (podspec_file )
184
222
version = os .path .basename (parent_dir )
223
+ if not allow_experimental and '-cppsdk' not in version :
224
+ if not re .match (RE_NON_EXPERIMENTAL_VERSION , version ):
225
+ continue
185
226
all_versions [podname ].append (version )
186
227
187
228
return all_versions
188
229
189
230
190
- def get_latest_pod_versions (specs_repo = None , pods = PODS ):
231
+ def get_latest_pod_versions (specs_repo = None , pods = PODS , ignore_pods = None ,
232
+ allow_experimental = None ):
191
233
"""Get latest versions for specified pods.
192
234
193
235
Args:
194
236
pods (iterable(str) optional): Pods for which we need latest version.
195
237
Defaults to PODS.
196
238
specs_repo (str optional): Local checkout of Cocoapods specs repo.
239
+ ignore_pods (list[str], optional): Case insensitive list of substrings
240
+ If any of these substrings are present in the pod name, it will not be
241
+ updated.
242
+ Eg: ['Foo', 'bar'] will ignore all pods that have 'foo' or
243
+ 'bar' in their name. For example, 'test_foo', 'test_foo_baz'
244
+ allow_experimental (bool): Allow experimental versions.
245
+ Eg: 1.2.3-alpha, 1.2.3-beta, 1.2.3-rc
197
246
198
247
Returns:
199
248
dict: Map of the form {<str>:<str>} containing a mapping of podnames to
200
249
latest version.
201
250
"""
251
+ if ignore_pods is None :
252
+ ignore_pods = []
253
+
202
254
cleanup_required = False
203
255
if specs_repo is None :
204
256
specs_repo = tempfile .mkdtemp (suffix = 'pods' )
@@ -209,7 +261,8 @@ def get_latest_pod_versions(specs_repo=None, pods=PODS):
209
261
# Temporary directory should be cleaned up after use.
210
262
cleanup_required = True
211
263
212
- all_versions = get_pod_versions (specs_repo , pods )
264
+ all_versions = get_pod_versions (specs_repo , pods , ignore_pods ,
265
+ allow_experimental )
213
266
if cleanup_required :
214
267
shutil .rmtree (specs_repo )
215
268
@@ -304,14 +357,16 @@ def modify_pod_file(pod_file, pod_version_map, dryrun=True):
304
357
print ()
305
358
306
359
360
+ # Regex to match lines like:
361
+ # | | Firebase/Auth Cocoapod (8.2.0)
307
362
RE_README_POD_VERSION = re .compile (
308
363
r"\|(?P<spaces>\s+)\| (?P<pod_name>.*) Cocoapod \((?P<version>([0-9.]+))\)" )
309
364
310
365
def modify_readme_file_pods (readme_filepath , version_map , dryrun = True ):
311
366
"""Modify a readme Markdown file to reference correct cocoapods versions.
312
367
313
368
Looks for lines like:
314
- <br>Firebase/AdMob Cocoapod (7.11.0)<br> Firebase/Auth Cocoapod (8.1 .0)
369
+ | | Firebase/Auth Cocoapod (8.2 .0)
315
370
for pods matching the ones in the version map, and modifies them in-place.
316
371
317
372
Args:
@@ -373,7 +428,26 @@ def replace_pod_line(m):
373
428
GMAVEN_GROUP_INDEX = "https://dl.google.com/dl/android/maven2/{0}/group-index.xml"
374
429
375
430
376
- def get_latest_maven_versions ():
431
+ def get_latest_maven_versions (ignore_packages = None , allow_experimental = False ):
432
+ """Gets latest versions of android packages from google Maven repository.
433
+
434
+ Args:
435
+ ignore_packages (list[str], optional): Case insensitive list of substrings
436
+ If any of these substrings are present in the android package name, it
437
+ will not be updated.
438
+ Eg: ['Foo', 'bar'] will ignore all android packages that have 'foo' or
439
+ 'bar' in their name. For example, 'my.android.foo.package',
440
+ 'myfoo.android.package'
441
+ allow_experimental (bool): Allow experimental versions.
442
+ Eg: 1.2.3-alpha, 1.2.3-beta, 1.2.3-rc
443
+
444
+ Returns:
445
+ dict: Dictionary of the form {<str>: list(str)} containing full name of
446
+ android package as the key and its list of versions as value.
447
+ """
448
+ if ignore_packages is None :
449
+ ignore_packages = []
450
+
377
451
latest_versions = {}
378
452
response = requests .get (GMAVEN_MASTER_INDEX )
379
453
index_xml = ElementTree .fromstring (response .content )
@@ -385,8 +459,16 @@ def get_latest_maven_versions():
385
459
for group_child in group_xml :
386
460
package_name = group_child .tag .replace ('-' , '_' )
387
461
package_full_name = group_name + "." + package_name
388
- package_version = group_child .attrib ['versions' ].split (',' )[- 1 ]
389
- latest_versions [package_full_name ] = package_version
462
+ if any (ignore_package .lower ().replace ('-' , '_' ) in package_full_name .lower ()
463
+ for ignore_package in ignore_packages ):
464
+ continue
465
+ versions = group_child .attrib ['versions' ].split (',' )
466
+ if not allow_experimental :
467
+ versions = [version for version in versions
468
+ if re .match (RE_NON_EXPERIMENTAL_VERSION , version ) or '-cppsdk' in version ]
469
+ if versions :
470
+ latest_valid_version = versions [- 1 ]
471
+ latest_versions [package_full_name ] = latest_valid_version
390
472
return latest_versions
391
473
392
474
@@ -453,15 +535,18 @@ def replace_dependency(m):
453
535
print ()
454
536
455
537
538
+ # Regex to match lines like:
539
+ # | | com.google.firebase:firebase-auth:1.2.3
540
+ # | | com.google.firebase:firebase-auth:1.2.3-alpha
456
541
RE_README_ANDROID_VERSION = re .compile (
457
- r"\|(?P<spaces>\s+)\| (?P<pkg>[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+):([0-9. ]+)" )
542
+ r"\|(?P<spaces>\s+)\| (?P<pkg>[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+):([a-zA-Z0-9._- ]+)" )
458
543
459
544
460
545
def modify_readme_file_android (readme_filepath , version_map , dryrun = True ):
461
546
"""Modify a readme Markdown file to reference correct module versions.
462
547
463
548
Looks for lines like:
464
- <br> com.google.firebase:firebase-core:15.0.0<br>
549
+ | | com.google.firebase:firebase-auth:1.2.3
465
550
for modules matching the ones in the version map, and modifies them in-place.
466
551
467
552
Args:
@@ -489,7 +574,7 @@ def replace_module_line(m):
489
574
pkg = m .group ('pkg' ).replace ('-' , '_' ).replace (':' , '.' )
490
575
if pkg not in version_map :
491
576
return m .group (0 )
492
- repl = '|%s| %s:%s ' % (m .group ('spaces' ), m .group ('pkg' ), version_map [pkg ])
577
+ repl = '|%s| %s:%s' % (m .group ('spaces' ), m .group ('pkg' ), version_map [pkg ])
493
578
return repl
494
579
495
580
substituted_pairs = []
@@ -515,8 +600,9 @@ def replace_module_line(m):
515
600
516
601
# Regex to match lines like:
517
602
# implementation 'com.google.firebase:firebase-auth:1.2.3'
603
+ # implementation 'com.google.firebase:firebase-auth:1.2.3-alpha'
518
604
RE_GRADLE_COMPILE_MODULE = re .compile (
519
- r"implementation\s*\'(?P<pkg>[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+):([0-9. ]+)\'" )
605
+ r"implementation\s*\'(?P<pkg>[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+):([a-zA-Z0-9._- ]+)\'" )
520
606
521
607
522
608
def modify_gradle_file (gradle_filepath , version_map , dryrun = True ):
@@ -580,16 +666,33 @@ def parse_cmdline_args():
580
666
help = 'Just print the replaced lines, DO NOT overwrite any files' )
581
667
parser .add_argument ( "--log_level" , default = "info" ,
582
668
help = "Logging level (debug, warning, info)" )
669
+ # TODO: remove default values when we decide to update androidx for
670
+ # gameloop_android as well.
671
+ parser .add_argument ('--ignore_directories' , nargs = '+' ,
672
+ default = ('gameloop_android' , 'scripts' ),
673
+ help = 'Ignore updating any files in these directories (names).' )
674
+ parser .add_argument ('--allow_experimental' , action = 'store_true' ,
675
+ help = 'Allow updating to experimental versions (eg:1.2.3-alpha))' )
583
676
# iOS options
584
677
parser .add_argument ('--skip_ios' , action = 'store_true' ,
585
- help = 'Skip iOS pod version update.' )
678
+ help = 'Skip iOS pod version update completely.' )
679
+ # TODO: remove default values when Ads SDK does not need to be pinned.
680
+ parser .add_argument ('--ignore_ios_pods' , nargs = '+' ,
681
+ default = ('Google-Mobile-Ads-SDK' ,),
682
+ help = 'Ignore iOS pods which have any of the items specified in '
683
+ 'this list as substrings.' )
586
684
parser .add_argument ('--podfiles' , nargs = '+' , default = (os .getcwd (),),
587
685
help = 'List of pod files or directories containing podfiles' )
588
686
parser .add_argument ('--specs_repo' ,
589
687
help = 'Local checkout of github Cocoapods Specs repository' )
590
688
# Android options
591
689
parser .add_argument ('--skip_android' , action = 'store_true' ,
592
- help = 'Skip Android libraries version update.' )
690
+ help = 'Skip Android libraries version update completely.' )
691
+ # TODO: remove default values when Ads SDK does not need to be pinned.
692
+ parser .add_argument ('--ignore_android_packages' , nargs = '+' ,
693
+ default = ('firebase-ads' ,),
694
+ help = 'Ignore Android packages which have any of the items '
695
+ 'specified in this list as substrings.' )
593
696
parser .add_argument ('--depfiles' , nargs = '+' ,
594
697
default = ('Android/firebase_dependencies.gradle' ,
595
698
'release_build_files/Android/firebase_dependencies.gradle' ),
@@ -630,17 +733,21 @@ def main():
630
733
file_name = 'readme' )
631
734
632
735
if not args .skip_ios :
633
- latest_pod_versions_map = get_latest_pod_versions (args .specs_repo , PODS )
634
- pod_files = get_files (args .podfiles , file_extension = '' , file_name = 'Podfile' )
736
+ latest_pod_versions_map = get_latest_pod_versions (args .specs_repo , PODS ,
737
+ set (args .ignore_ios_pods ), args .allow_experimental )
738
+ pod_files = get_files (args .podfiles , file_extension = '' , file_name = 'Podfile' ,
739
+ ignore_directories = set (args .ignore_directories ))
635
740
for pod_file in pod_files :
636
741
modify_pod_file (pod_file , latest_pod_versions_map , args .dryrun )
637
742
for readme_file in readme_files :
638
743
modify_readme_file_pods (readme_file , latest_pod_versions_map , args .dryrun )
639
744
640
745
if not args .skip_android :
641
- latest_android_versions_map = get_latest_maven_versions ()
746
+ latest_android_versions_map = get_latest_maven_versions (
747
+ set (args .ignore_android_packages ), args .allow_experimental )
642
748
dep_files = get_files (args .depfiles , file_extension = '.gradle' ,
643
- file_name = 'firebase_dependencies.gradle' )
749
+ file_name = 'firebase_dependencies.gradle' ,
750
+ ignore_directories = set (args .ignore_directories ))
644
751
for dep_file in dep_files :
645
752
modify_dependency_file (dep_file , latest_android_versions_map , args .dryrun )
646
753
@@ -649,7 +756,8 @@ def main():
649
756
args .dryrun )
650
757
651
758
gradle_files = get_files (args .gradlefiles , file_extension = '.gradle' ,
652
- file_name = 'build.gradle' )
759
+ file_name = 'build.gradle' ,
760
+ ignore_directories = set (args .ignore_directories ))
653
761
for gradle_file in gradle_files :
654
762
modify_gradle_file (gradle_file , latest_android_versions_map , args .dryrun )
655
763
0 commit comments