| #!/usr/bin/env python3 | |
| import os, sys, zipfile | |
| import argparse | |
| import subprocess | |
| #### #### | |
| # This scripts updates LibraryVersions.k (see $LIBRARYVERSIONS_REL_PATH) based on the artifacts | |
| # in Google Maven (see $GMAVEN_BASE_URL). It will only numerically increment alpha or beta versions. | |
| # It will NOT change stability suffixes and it will NOT increment the version of a RC | |
| # or stable library. These changes should be done manually and purposefully. | |
| #### #### | |
| LIBRARYVERSIONS_REL_PATH = 'buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt' | |
| FRAMEWORKS_SUPPORT_FULL_PATH = os.path.abspath(os.path.join(os.getcwd(), '..')) | |
| LIBRARYVERSIONS_FULL_PATH = os.path.join(FRAMEWORKS_SUPPORT_FULL_PATH, LIBRARYVERSIONS_REL_PATH) | |
| GMAVEN_BASE_URL = 'https://dl.google.com/dl/android/maven2/androidx/' | |
| summary_log = [] | |
| exclude_dirs = [] | |
| # Defines an artifact in terms of its Maven Coorindates: artifactId, groupId, version | |
| class MavenCoordinates: | |
| def __init__(self, artifactId, version): | |
| self.artifactId = artifactId | |
| self.version = version | |
| self.groupId = self.get_groupId_from_artifactId(artifactId) | |
| def get_groupId_from_artifactId(self, artifactId): | |
| # By convention, androidx namespace is declared as: | |
| # androidx.${groupId}:${groupId}-${optionalArtifactIdSuffix}:${version} | |
| # So, artifactId == "${groupId}-${optionalArtifactIdSuffix}" | |
| return artifactId.split('-')[0] | |
| def print_e(*args, **kwargs): | |
| print(*args, file=sys.stderr, **kwargs) | |
| def should_update_artifact(commlineArgs, groupId, artifactId): | |
| # Tells whether to update the given artifact based on the command-line arguments | |
| should_update = False | |
| if (commlineArgs.groups) or (commlineArgs.artifacts): | |
| if (commlineArgs.groups) and (groupId in commlineArgs.groups): | |
| should_update = True | |
| if (commlineArgs.artifacts) and (artifactId in commlineArgs.artifacts): | |
| should_update = True | |
| else: | |
| should_update = True | |
| return should_update | |
| def print_change_summary(): | |
| print("\n --- SUMMARY --- ") | |
| for change in summary_log: | |
| print(change) | |
| def read_in_lines_from_file(file_path): | |
| if not os.path.exists(file_path): | |
| print_e("File path does not exist: %s" % file_path) | |
| exit(1) | |
| else: | |
| with open(file_path, 'r') as f: | |
| lv_lines = f.readlines() | |
| return lv_lines | |
| def write_lines_to_file(file_path, lines): | |
| if not os.path.exists(file_path): | |
| print_e("File path does not exist: %s" % file_path) | |
| exit(1) | |
| # Open file for writing and update all lines | |
| with open(file_path, 'w') as f: | |
| f.writelines(lines) | |
| def get_artifactId_from_LibraryVersions_line(line): | |
| artifactId = line.split('val')[1] | |
| artifactId = artifactId.split('=')[0] | |
| artifactId = artifactId.strip(' ') | |
| artifactId = artifactId.lower() | |
| artifactId = artifactId.replace('_', '-') | |
| return artifactId | |
| def get_version_or_macro_from_LibraryVersions_line(line): | |
| ## Sample input: 'val ACTIVITY = Version("1.0.0-alpha04")' | |
| ## Sample output: '1.0.0-alpha04', True | |
| ## Sample input: 'val ACTIVITY = FRAGMENT' | |
| ## Sample output: 'fragment', False | |
| is_resolved = False | |
| version = "" | |
| if 'Version(' in line and '\"' in line: | |
| is_resolved = True | |
| version = line.split('\"')[1] | |
| else: | |
| is_resolved = False | |
| version = line.split('=')[-1].strip(' \n').lower().replace('_','-') | |
| return version, is_resolved | |
| def get_tot_artifact_list(lv_lines): | |
| resolved_artifact_list = [] | |
| unresolved_versions_list = [] | |
| for cur_line in lv_lines: | |
| # Skip any line that doesn't declare a version | |
| if 'val' not in cur_line: continue | |
| artifactId = get_artifactId_from_LibraryVersions_line(cur_line) | |
| version, is_resolved = get_version_or_macro_from_LibraryVersions_line(cur_line) | |
| artifact = MavenCoordinates(artifactId, version) | |
| if is_resolved: | |
| resolved_artifact_list.append(artifact) | |
| else: | |
| # Artifact version specification is a reference to another artifact's | |
| # version -> it needs to be resolved | |
| unresolved_versions_list.append(artifact) | |
| # Resolve variable references in unresolved_versions_list to complete resolved_artifact_list. | |
| # This is needed because version MACROs can be a copy another version MACRO. For example: | |
| # val ARCH_CORE = Version("2.0.0") | |
| # val ARCH_CORE_TESTING = ARCH_CORE | |
| for unresolved_artifact in unresolved_versions_list: | |
| artifactId_to_copy = unresolved_artifact.version | |
| for resolved_artifact in resolved_artifact_list: | |
| if resolved_artifact.artifactId == artifactId_to_copy: | |
| unresolved_artifact.version = resolved_artifact.version | |
| break | |
| resolved_artifact_list.append(unresolved_artifact) | |
| return resolved_artifact_list | |
| def does_exist_on_gmaven(groupId, artifactId, version): | |
| print("Checking GMaven for %s-%s..." % (artifactId, version), end = '') | |
| # URLS are of the format: | |
| # https://dl.google.com/dl/android/maven2/androidx/${groupId}/${artifactId}/${version}/${artifactId}-${version}.pom | |
| artifactUrl = GMAVEN_BASE_URL + groupId + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version + '.pom' | |
| try: | |
| # Curl the url to see if artifact pom exists | |
| curl_output = subprocess.check_output('curl -s %s' % artifactUrl, shell=True) | |
| except subprocess.CalledProcessError: | |
| print_e('FAIL: Failed to curl url: ' % artifactUrl) | |
| return None | |
| if '404' in curl_output.decode(): | |
| print("version is good") | |
| return False | |
| else: | |
| print("version is OUT OF DATE") | |
| return True | |
| def increment_alpha_beta_version(version): | |
| # Only increment alpha and beta versions. | |
| # rc and stable should never need to be incremented in the androidx-main branch | |
| # Suffix changes should be done manually. | |
| changed = False | |
| if 'alpha' in version or 'beta' in version: | |
| changed = True | |
| # Assure we don't violate a version naming policy | |
| if not version[-1:].isdigit(): | |
| print_e("--- --- \n Version %s has violated version naming policy! Please fix." % version) | |
| exit(1) | |
| if version[-2:].isdigit(): | |
| new_version = int(version[-2:]) + 1 | |
| formatted_version = "%02d" % (new_version,) | |
| return version[:-2] + formatted_version, changed | |
| else: | |
| # Account for single digit versions with no preceding 0 (the leading 0 will be added) | |
| new_version = int(version[-1:]) + 1 | |
| formatted_version = "%02d" % (new_version,) | |
| return version[:-1] + formatted_version, changed | |
| else: | |
| return version, changed | |
| def artifactId_to_kotlin_macro(artifactId): | |
| return artifactId.replace('-','_').upper() | |
| def update_artifact_version(lv_lines, artifact): | |
| num_lines = len(lv_lines) | |
| for i in range(num_lines): | |
| cur_line = lv_lines[i] | |
| # Skip any line that doesn't declare a version | |
| if 'val' not in cur_line: continue | |
| artifactId = get_artifactId_from_LibraryVersions_line(cur_line) | |
| if artifactId == artifact.artifactId: | |
| new_version, ver_was_updated = increment_alpha_beta_version(artifact.version) | |
| if ver_was_updated: | |
| # Only modify line if the version was actually changed | |
| lv_lines[i] =" val " + artifactId_to_kotlin_macro(artifactId) + " = Version(\"" + new_version + "\")\n" | |
| summary_log.append("Updated %s to FROM %s TO %s" % (artifactId.upper(), artifact.version, new_version)) | |
| # Assert incremented version doesn't exist | |
| if does_exist_on_gmaven(artifact.groupId, artifact.artifactId, new_version): | |
| print_e("--- --- \n Incremented version of %s from %s to %s, but %s has already been published!\ | |
| This needs to be fixed manually." % (artifact.artifactId, artifact.version, new_version, new_version)) | |
| exit(1) | |
| def update_api(): | |
| try: | |
| os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| curl_output = subprocess.check_output('./gradlew updateApi', shell=True) | |
| os.chdir(os.path.dirname(os.path.abspath(__file__))) | |
| except subprocess.CalledProcessError: | |
| print_e('FAIL: Failed gradle task updateApi!') | |
| return None | |
| def update_library_versions(args): | |
| # Open LibraryVersions.kt file for reading and get all lines | |
| libraryversions_lines = read_in_lines_from_file(LIBRARYVERSIONS_FULL_PATH) | |
| tot_artifact_list = get_tot_artifact_list(libraryversions_lines) | |
| # Loop through every library version and update the version, if necessary | |
| versions_changed = False | |
| for artifact in tot_artifact_list: | |
| if should_update_artifact(args, artifact.groupId, artifact.artifactId): | |
| print("Updating %s " % artifact.artifactId) | |
| if does_exist_on_gmaven(artifact.groupId, artifact.artifactId, artifact.version): | |
| update_artifact_version(libraryversions_lines, artifact) | |
| versions_changed = True | |
| if versions_changed: | |
| write_lines_to_file(LIBRARYVERSIONS_FULL_PATH, libraryversions_lines) | |
| update_api() | |
| summary_log.append("These changes have not been committed. Please double check before uploading.") | |
| else: | |
| summary_log.append("No changes needed. All versions are update to date :)") | |
| if __name__ == '__main__': | |
| # cd into directory of script | |
| os.chdir(os.path.dirname(os.path.abspath(__file__))) | |
| # Set up input arguments | |
| parser = argparse.ArgumentParser( | |
| description=('This script increments versions in LibraryVersions.kt based on artifacts released to Google Maven.')) | |
| parser.add_argument( | |
| '--groups', metavar='groupId', nargs='+', | |
| help="""If specified, only increments the version for libraries whose groupId contains the listed text. | |
| For example, if you specify \"--groups paging slice lifecycle\", then this | |
| script will increment the version of each library with groupId beginning with \"androidx.paging\", \"androidx.slice\", | |
| or \"androidx.lifecycle\"""") | |
| parser.add_argument( | |
| '--artifacts', metavar='artifactId', nargs='+', | |
| help="""If specified, only increments the version for libraries whose artifactId contains the listed text. | |
| For example, if you specify \"--artifacts core slice-view lifecycle-common\", then this | |
| script will increment the version for specific artifacts \"androidx.core:core\", \"androidx.slice:slice-view\", | |
| and \"androidx.lifecycle:lifecycle-common\"""") | |
| args = parser.parse_args() | |
| update_library_versions(args) | |
| print_change_summary() | |