1#!/usr/bin/env python3 2 3import os, sys, zipfile 4import argparse 5import subprocess 6 7#### #### 8# This scripts updates LibraryVersions.k (see $LIBRARYVERSIONS_REL_PATH) based on the artifacts 9# in Google Maven (see $GMAVEN_BASE_URL). It will only numerically increment alpha or beta versions. 10# It will NOT change stability suffixes and it will NOT increment the version of a RC 11# or stable library. These changes should be done manually and purposefully. 12#### #### 13 14LIBRARYVERSIONS_REL_PATH = 'buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt' 15FRAMEWORKS_SUPPORT_FULL_PATH = os.path.abspath(os.path.join(os.getcwd(), '..')) 16LIBRARYVERSIONS_FULL_PATH = os.path.join(FRAMEWORKS_SUPPORT_FULL_PATH, LIBRARYVERSIONS_REL_PATH) 17GMAVEN_BASE_URL = 'https://dl.google.com/dl/android/maven2/androidx/' 18summary_log = [] 19exclude_dirs = [] 20 21# Defines an artifact in terms of its Maven Coorindates: artifactId, groupId, version 22class MavenCoordinates: 23 def __init__(self, artifactId, version): 24 self.artifactId = artifactId 25 self.version = version 26 self.groupId = self.get_groupId_from_artifactId(artifactId) 27 def get_groupId_from_artifactId(self, artifactId): 28 # By convention, androidx namespace is declared as: 29 # androidx.${groupId}:${groupId}-${optionalArtifactIdSuffix}:${version} 30 # So, artifactId == "${groupId}-${optionalArtifactIdSuffix}" 31 return artifactId.split('-')[0] 32 33def print_e(*args, **kwargs): 34 print(*args, file=sys.stderr, **kwargs) 35 36def should_update_artifact(commlineArgs, groupId, artifactId): 37 # Tells whether to update the given artifact based on the command-line arguments 38 should_update = False 39 if (commlineArgs.groups) or (commlineArgs.artifacts): 40 if (commlineArgs.groups) and (groupId in commlineArgs.groups): 41 should_update = True 42 if (commlineArgs.artifacts) and (artifactId in commlineArgs.artifacts): 43 should_update = True 44 else: 45 should_update = True 46 return should_update 47 48def print_change_summary(): 49 print("\n --- SUMMARY --- ") 50 for change in summary_log: 51 print(change) 52 53def read_in_lines_from_file(file_path): 54 if not os.path.exists(file_path): 55 print_e("File path does not exist: %s" % file_path) 56 exit(1) 57 else: 58 with open(file_path, 'r') as f: 59 lv_lines = f.readlines() 60 return lv_lines 61 62def write_lines_to_file(file_path, lines): 63 if not os.path.exists(file_path): 64 print_e("File path does not exist: %s" % file_path) 65 exit(1) 66 # Open file for writing and update all lines 67 with open(file_path, 'w') as f: 68 f.writelines(lines) 69 70def get_artifactId_from_LibraryVersions_line(line): 71 artifactId = line.split('val')[1] 72 artifactId = artifactId.split('=')[0] 73 artifactId = artifactId.strip(' ') 74 artifactId = artifactId.lower() 75 artifactId = artifactId.replace('_', '-') 76 return artifactId 77 78def get_version_or_macro_from_LibraryVersions_line(line): 79 ## Sample input: 'val ACTIVITY = Version("1.0.0-alpha04")' 80 ## Sample output: '1.0.0-alpha04', True 81 ## Sample input: 'val ACTIVITY = FRAGMENT' 82 ## Sample output: 'fragment', False 83 is_resolved = False 84 version = "" 85 if 'Version(' in line and '\"' in line: 86 is_resolved = True 87 version = line.split('\"')[1] 88 else: 89 is_resolved = False 90 version = line.split('=')[-1].strip(' \n').lower().replace('_','-') 91 return version, is_resolved 92 93def get_tot_artifact_list(lv_lines): 94 resolved_artifact_list = [] 95 unresolved_versions_list = [] 96 for cur_line in lv_lines: 97 # Skip any line that doesn't declare a version 98 if 'val' not in cur_line: continue 99 artifactId = get_artifactId_from_LibraryVersions_line(cur_line) 100 version, is_resolved = get_version_or_macro_from_LibraryVersions_line(cur_line) 101 artifact = MavenCoordinates(artifactId, version) 102 if is_resolved: 103 resolved_artifact_list.append(artifact) 104 else: 105 # Artifact version specification is a reference to another artifact's 106 # version -> it needs to be resolved 107 unresolved_versions_list.append(artifact) 108 # Resolve variable references in unresolved_versions_list to complete resolved_artifact_list. 109 # This is needed because version MACROs can be a copy another version MACRO. For example: 110 # val ARCH_CORE = Version("2.0.0") 111 # val ARCH_CORE_TESTING = ARCH_CORE 112 for unresolved_artifact in unresolved_versions_list: 113 artifactId_to_copy = unresolved_artifact.version 114 for resolved_artifact in resolved_artifact_list: 115 if resolved_artifact.artifactId == artifactId_to_copy: 116 unresolved_artifact.version = resolved_artifact.version 117 break 118 resolved_artifact_list.append(unresolved_artifact) 119 return resolved_artifact_list 120 121def does_exist_on_gmaven(groupId, artifactId, version): 122 print("Checking GMaven for %s-%s..." % (artifactId, version), end = '') 123 # URLS are of the format: 124 # https://dl.google.com/dl/android/maven2/androidx/${groupId}/${artifactId}/${version}/${artifactId}-${version}.pom 125 artifactUrl = GMAVEN_BASE_URL + groupId + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version + '.pom' 126 try: 127 # Curl the url to see if artifact pom exists 128 curl_output = subprocess.check_output('curl -s %s' % artifactUrl, shell=True) 129 except subprocess.CalledProcessError: 130 print_e('FAIL: Failed to curl url: ' % artifactUrl) 131 return None 132 if '404' in curl_output.decode(): 133 print("version is good") 134 return False 135 else: 136 print("version is OUT OF DATE") 137 return True 138 139def increment_alpha_beta_version(version): 140 # Only increment alpha and beta versions. 141 # rc and stable should never need to be incremented in the androidx-main branch 142 # Suffix changes should be done manually. 143 changed = False 144 if 'alpha' in version or 'beta' in version: 145 changed = True 146 # Assure we don't violate a version naming policy 147 if not version[-1:].isdigit(): 148 print_e("--- --- \n Version %s has violated version naming policy! Please fix." % version) 149 exit(1) 150 if version[-2:].isdigit(): 151 new_version = int(version[-2:]) + 1 152 formatted_version = "%02d" % (new_version,) 153 return version[:-2] + formatted_version, changed 154 else: 155 # Account for single digit versions with no preceding 0 (the leading 0 will be added) 156 new_version = int(version[-1:]) + 1 157 formatted_version = "%02d" % (new_version,) 158 return version[:-1] + formatted_version, changed 159 else: 160 return version, changed 161 162def artifactId_to_kotlin_macro(artifactId): 163 return artifactId.replace('-','_').upper() 164 165def update_artifact_version(lv_lines, artifact): 166 num_lines = len(lv_lines) 167 for i in range(num_lines): 168 cur_line = lv_lines[i] 169 # Skip any line that doesn't declare a version 170 if 'val' not in cur_line: continue 171 artifactId = get_artifactId_from_LibraryVersions_line(cur_line) 172 if artifactId == artifact.artifactId: 173 new_version, ver_was_updated = increment_alpha_beta_version(artifact.version) 174 if ver_was_updated: 175 # Only modify line if the version was actually changed 176 lv_lines[i] =" val " + artifactId_to_kotlin_macro(artifactId) + " = Version(\"" + new_version + "\")\n" 177 summary_log.append("Updated %s to FROM %s TO %s" % (artifactId.upper(), artifact.version, new_version)) 178 # Assert incremented version doesn't exist 179 if does_exist_on_gmaven(artifact.groupId, artifact.artifactId, new_version): 180 print_e("--- --- \n Incremented version of %s from %s to %s, but %s has already been published!\ 181 This needs to be fixed manually." % (artifact.artifactId, artifact.version, new_version, new_version)) 182 exit(1) 183 184def update_api(): 185 try: 186 os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 187 curl_output = subprocess.check_output('./gradlew updateApi', shell=True) 188 os.chdir(os.path.dirname(os.path.abspath(__file__))) 189 except subprocess.CalledProcessError: 190 print_e('FAIL: Failed gradle task updateApi!') 191 return None 192 193def update_library_versions(args): 194 # Open LibraryVersions.kt file for reading and get all lines 195 libraryversions_lines = read_in_lines_from_file(LIBRARYVERSIONS_FULL_PATH) 196 tot_artifact_list = get_tot_artifact_list(libraryversions_lines) 197 # Loop through every library version and update the version, if necessary 198 versions_changed = False 199 for artifact in tot_artifact_list: 200 if should_update_artifact(args, artifact.groupId, artifact.artifactId): 201 print("Updating %s " % artifact.artifactId) 202 if does_exist_on_gmaven(artifact.groupId, artifact.artifactId, artifact.version): 203 update_artifact_version(libraryversions_lines, artifact) 204 versions_changed = True 205 if versions_changed: 206 write_lines_to_file(LIBRARYVERSIONS_FULL_PATH, libraryversions_lines) 207 update_api() 208 summary_log.append("These changes have not been committed. Please double check before uploading.") 209 else: 210 summary_log.append("No changes needed. All versions are update to date :)") 211 212 213if __name__ == '__main__': 214 # cd into directory of script 215 os.chdir(os.path.dirname(os.path.abspath(__file__))) 216 217 # Set up input arguments 218 parser = argparse.ArgumentParser( 219 description=('This script increments versions in LibraryVersions.kt based on artifacts released to Google Maven.')) 220 parser.add_argument( 221 '--groups', metavar='groupId', nargs='+', 222 help="""If specified, only increments the version for libraries whose groupId contains the listed text. 223 For example, if you specify \"--groups paging slice lifecycle\", then this 224 script will increment the version of each library with groupId beginning with \"androidx.paging\", \"androidx.slice\", 225 or \"androidx.lifecycle\"""") 226 parser.add_argument( 227 '--artifacts', metavar='artifactId', nargs='+', 228 help="""If specified, only increments the version for libraries whose artifactId contains the listed text. 229 For example, if you specify \"--artifacts core slice-view lifecycle-common\", then this 230 script will increment the version for specific artifacts \"androidx.core:core\", \"androidx.slice:slice-view\", 231 and \"androidx.lifecycle:lifecycle-common\"""") 232 args = parser.parse_args() 233 update_library_versions(args) 234 print_change_summary() 235 236