| #!/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() |
| |