1*3c875a21SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 2*3c875a21SAndroid Build Coastguard Worker# 3*3c875a21SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*3c875a21SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*3c875a21SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*3c875a21SAndroid Build Coastguard Worker# 7*3c875a21SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*3c875a21SAndroid Build Coastguard Worker# 9*3c875a21SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*3c875a21SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*3c875a21SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*3c875a21SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*3c875a21SAndroid Build Coastguard Worker# limitations under the License. 14*3c875a21SAndroid Build Coastguard Worker"""Module to check updates from Git upstream.""" 15*3c875a21SAndroid Build Coastguard Worker 16*3c875a21SAndroid Build Coastguard Workerimport base_updater 17*3c875a21SAndroid Build Coastguard Workerimport fileutils 18*3c875a21SAndroid Build Coastguard Workerimport git_utils 19*3c875a21SAndroid Build Coastguard Workerimport updater_utils 20*3c875a21SAndroid Build Coastguard Worker# pylint: disable=import-error 21*3c875a21SAndroid Build Coastguard Workerfrom color import Color, color_string 22*3c875a21SAndroid Build Coastguard Workerfrom manifest import Manifest 23*3c875a21SAndroid Build Coastguard Workerimport metadata_pb2 # type: ignore 24*3c875a21SAndroid Build Coastguard Worker 25*3c875a21SAndroid Build Coastguard Worker 26*3c875a21SAndroid Build Coastguard Workerclass GitUpdater(base_updater.Updater): 27*3c875a21SAndroid Build Coastguard Worker """Updater for Git upstream.""" 28*3c875a21SAndroid Build Coastguard Worker UPSTREAM_REMOTE_NAME: str = "update_origin" 29*3c875a21SAndroid Build Coastguard Worker 30*3c875a21SAndroid Build Coastguard Worker def is_supported_url(self) -> bool: 31*3c875a21SAndroid Build Coastguard Worker return git_utils.is_valid_url(self._proj_path, self._old_identifier.value) 32*3c875a21SAndroid Build Coastguard Worker 33*3c875a21SAndroid Build Coastguard Worker def setup_remote(self) -> None: 34*3c875a21SAndroid Build Coastguard Worker remotes = git_utils.list_remotes(self._proj_path) 35*3c875a21SAndroid Build Coastguard Worker current_remote_url = None 36*3c875a21SAndroid Build Coastguard Worker for name, url in remotes.items(): 37*3c875a21SAndroid Build Coastguard Worker if name == self.UPSTREAM_REMOTE_NAME: 38*3c875a21SAndroid Build Coastguard Worker current_remote_url = url 39*3c875a21SAndroid Build Coastguard Worker 40*3c875a21SAndroid Build Coastguard Worker if current_remote_url is not None and current_remote_url != self._old_identifier.value: 41*3c875a21SAndroid Build Coastguard Worker git_utils.remove_remote(self._proj_path, self.UPSTREAM_REMOTE_NAME) 42*3c875a21SAndroid Build Coastguard Worker current_remote_url = None 43*3c875a21SAndroid Build Coastguard Worker 44*3c875a21SAndroid Build Coastguard Worker if current_remote_url is None: 45*3c875a21SAndroid Build Coastguard Worker git_utils.add_remote(self._proj_path, self.UPSTREAM_REMOTE_NAME, 46*3c875a21SAndroid Build Coastguard Worker self._old_identifier.value) 47*3c875a21SAndroid Build Coastguard Worker 48*3c875a21SAndroid Build Coastguard Worker git_utils.fetch(self._proj_path, self.UPSTREAM_REMOTE_NAME) 49*3c875a21SAndroid Build Coastguard Worker 50*3c875a21SAndroid Build Coastguard Worker def set_custom_version(self, custom_version: str) -> None: 51*3c875a21SAndroid Build Coastguard Worker super().set_custom_version(custom_version) 52*3c875a21SAndroid Build Coastguard Worker if not git_utils.list_branches_with_commit(self._proj_path, custom_version, self.UPSTREAM_REMOTE_NAME): 53*3c875a21SAndroid Build Coastguard Worker raise RuntimeError( 54*3c875a21SAndroid Build Coastguard Worker f"Can not upgrade to {custom_version}. This version does not belong to any branches.") 55*3c875a21SAndroid Build Coastguard Worker 56*3c875a21SAndroid Build Coastguard Worker def set_new_versions_for_commit(self, latest_sha: str, latest_tag: str | None = None) -> None: 57*3c875a21SAndroid Build Coastguard Worker self._new_identifier.version = latest_sha 58*3c875a21SAndroid Build Coastguard Worker if latest_tag is not None and git_utils.is_ancestor( 59*3c875a21SAndroid Build Coastguard Worker self._proj_path, self._old_identifier.version, latest_tag): 60*3c875a21SAndroid Build Coastguard Worker self._alternative_new_ver = latest_tag 61*3c875a21SAndroid Build Coastguard Worker 62*3c875a21SAndroid Build Coastguard Worker def set_new_versions_for_tag(self, latest_sha: str, latest_tag: str | None = None) -> None: 63*3c875a21SAndroid Build Coastguard Worker if latest_tag is None: 64*3c875a21SAndroid Build Coastguard Worker project = fileutils.canonicalize_project_path(self.project_path) 65*3c875a21SAndroid Build Coastguard Worker print(color_string( 66*3c875a21SAndroid Build Coastguard Worker f"{project} is currently tracking upstream tags but either no " 67*3c875a21SAndroid Build Coastguard Worker "tags were found in the upstream repository or the tag does not " 68*3c875a21SAndroid Build Coastguard Worker "belong to any branch. No latest tag available", Color.STALE 69*3c875a21SAndroid Build Coastguard Worker )) 70*3c875a21SAndroid Build Coastguard Worker self._new_identifier.ClearField("version") 71*3c875a21SAndroid Build Coastguard Worker self._alternative_new_ver = latest_sha 72*3c875a21SAndroid Build Coastguard Worker return 73*3c875a21SAndroid Build Coastguard Worker self._new_identifier.version = latest_tag 74*3c875a21SAndroid Build Coastguard Worker if git_utils.is_ancestor( 75*3c875a21SAndroid Build Coastguard Worker self._proj_path, self._old_identifier.version, latest_sha): 76*3c875a21SAndroid Build Coastguard Worker self._alternative_new_ver = latest_sha 77*3c875a21SAndroid Build Coastguard Worker 78*3c875a21SAndroid Build Coastguard Worker def check(self) -> None: 79*3c875a21SAndroid Build Coastguard Worker """Checks upstream and returns whether a new version is available.""" 80*3c875a21SAndroid Build Coastguard Worker self.setup_remote() 81*3c875a21SAndroid Build Coastguard Worker 82*3c875a21SAndroid Build Coastguard Worker latest_sha = self.current_head_of_upstream_default_branch() 83*3c875a21SAndroid Build Coastguard Worker latest_tag = self.latest_tag_of_upstream() 84*3c875a21SAndroid Build Coastguard Worker 85*3c875a21SAndroid Build Coastguard Worker if git_utils.is_commit(self._old_identifier.version): 86*3c875a21SAndroid Build Coastguard Worker self.set_new_versions_for_commit(latest_sha, latest_tag) 87*3c875a21SAndroid Build Coastguard Worker else: 88*3c875a21SAndroid Build Coastguard Worker self.set_new_versions_for_tag(latest_sha, latest_tag) 89*3c875a21SAndroid Build Coastguard Worker 90*3c875a21SAndroid Build Coastguard Worker def latest_tag_of_upstream(self) -> str | None: 91*3c875a21SAndroid Build Coastguard Worker tags = git_utils.list_remote_tags(self._proj_path, self.UPSTREAM_REMOTE_NAME) 92*3c875a21SAndroid Build Coastguard Worker if not tags: 93*3c875a21SAndroid Build Coastguard Worker return None 94*3c875a21SAndroid Build Coastguard Worker 95*3c875a21SAndroid Build Coastguard Worker parsed_tags = [updater_utils.parse_remote_tag(tag) for tag in tags] 96*3c875a21SAndroid Build Coastguard Worker tag = updater_utils.get_latest_stable_release_tag(self._old_identifier.version, parsed_tags) 97*3c875a21SAndroid Build Coastguard Worker if not git_utils.list_branches_with_commit(self._proj_path, tag, self.UPSTREAM_REMOTE_NAME): 98*3c875a21SAndroid Build Coastguard Worker return None 99*3c875a21SAndroid Build Coastguard Worker 100*3c875a21SAndroid Build Coastguard Worker return tag 101*3c875a21SAndroid Build Coastguard Worker 102*3c875a21SAndroid Build Coastguard Worker def current_head_of_upstream_default_branch(self) -> str: 103*3c875a21SAndroid Build Coastguard Worker branch = git_utils.detect_default_branch(self._proj_path, 104*3c875a21SAndroid Build Coastguard Worker self.UPSTREAM_REMOTE_NAME) 105*3c875a21SAndroid Build Coastguard Worker return git_utils.get_sha_for_branch( 106*3c875a21SAndroid Build Coastguard Worker self._proj_path, self.UPSTREAM_REMOTE_NAME + '/' + branch) 107*3c875a21SAndroid Build Coastguard Worker 108*3c875a21SAndroid Build Coastguard Worker def update(self) -> None: 109*3c875a21SAndroid Build Coastguard Worker """Updates the package. 110*3c875a21SAndroid Build Coastguard Worker Has to call check() before this function. 111*3c875a21SAndroid Build Coastguard Worker """ 112*3c875a21SAndroid Build Coastguard Worker print(f"Running `git merge {self._new_identifier.version}`...") 113*3c875a21SAndroid Build Coastguard Worker git_utils.merge(self._proj_path, self._new_identifier.version) 114*3c875a21SAndroid Build Coastguard Worker 115*3c875a21SAndroid Build Coastguard Worker def _determine_android_fetch_ref(self) -> str: 116*3c875a21SAndroid Build Coastguard Worker """Returns the ref that should be fetched from the android remote.""" 117*3c875a21SAndroid Build Coastguard Worker # It isn't particularly efficient to reparse the tree for every 118*3c875a21SAndroid Build Coastguard Worker # project, but we don't guarantee that all paths passed to updater.sh 119*3c875a21SAndroid Build Coastguard Worker # are actually in the same tree so it wouldn't necessarily be correct 120*3c875a21SAndroid Build Coastguard Worker # to do this once at the top level. This isn't the slow part anyway, 121*3c875a21SAndroid Build Coastguard Worker # so it can be dealt with if that ever changes. 122*3c875a21SAndroid Build Coastguard Worker root = fileutils.find_tree_containing(self._proj_path) 123*3c875a21SAndroid Build Coastguard Worker manifest = Manifest.for_tree(root) 124*3c875a21SAndroid Build Coastguard Worker manifest_path = str(self._proj_path.relative_to(root)) 125*3c875a21SAndroid Build Coastguard Worker try: 126*3c875a21SAndroid Build Coastguard Worker project = manifest.project_with_path(manifest_path) 127*3c875a21SAndroid Build Coastguard Worker except KeyError as ex: 128*3c875a21SAndroid Build Coastguard Worker raise RuntimeError( 129*3c875a21SAndroid Build Coastguard Worker f"Did not find {manifest_path} in {manifest.path} (tree root is {root})" 130*3c875a21SAndroid Build Coastguard Worker ) from ex 131*3c875a21SAndroid Build Coastguard Worker return project.revision 132