blob: f32e73522bd602b7e54ac831b9299abe3a46af20 [file] [log] [blame]
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module to check updates from Git upstream."""
import base_updater
import fileutils
import git_utils
import updater_utils
# pylint: disable=import-error
from manifest import Manifest
class GitUpdater(base_updater.Updater):
"""Updater for Git upstream."""
UPSTREAM_REMOTE_NAME: str = "update_origin"
def is_supported_url(self) -> bool:
return git_utils.is_valid_url(self._proj_path, self._old_identifier.value)
def setup_remote(self) -> None:
remotes = git_utils.list_remotes(self._proj_path)
current_remote_url = None
for name, url in remotes.items():
if name == self.UPSTREAM_REMOTE_NAME:
current_remote_url = url
if current_remote_url is not None and current_remote_url != self._old_identifier.value:
git_utils.remove_remote(self._proj_path, self.UPSTREAM_REMOTE_NAME)
current_remote_url = None
if current_remote_url is None:
git_utils.add_remote(self._proj_path, self.UPSTREAM_REMOTE_NAME,
self._old_identifier.value)
git_utils.fetch(self._proj_path, self.UPSTREAM_REMOTE_NAME)
def check(self) -> None:
"""Checks upstream and returns whether a new version is available."""
self.setup_remote()
possible_alternative_new_ver: str | None = None
if git_utils.is_commit(self._old_identifier.version):
# Update to remote head.
self._new_identifier.version = self.current_head_of_upstream_default_branch()
# Some libraries don't have a tag. We only populate
# _alternative_new_ver if there is a tag newer than _old_ver.
# Checks if there is a tag newer than AOSP's SHA
if (tag := self.latest_tag_of_upstream()) is not None:
possible_alternative_new_ver = tag
else:
# Update to the latest version tag.
tag = self.latest_tag_of_upstream()
if tag is None:
project = fileutils.canonicalize_project_path(self.project_path)
raise RuntimeError(
f"{project} is currently tracking upstream tags but no tags were "
"found in the upstream repository"
)
self._new_identifier.version = tag
# Checks if there is a SHA newer than AOSP's tag
possible_alternative_new_ver = self.current_head_of_upstream_default_branch()
if possible_alternative_new_ver is not None and git_utils.is_ancestor(
self._proj_path,
self._old_identifier.version,
possible_alternative_new_ver
):
self._alternative_new_ver = possible_alternative_new_ver
def latest_tag_of_upstream(self) -> str | None:
tags = git_utils.list_remote_tags(self._proj_path, self.UPSTREAM_REMOTE_NAME)
if not tags:
return None
parsed_tags = [updater_utils.parse_remote_tag(tag) for tag in tags]
tag = updater_utils.get_latest_stable_release_tag(self._old_identifier.version, parsed_tags)
return tag
def current_head_of_upstream_default_branch(self) -> str:
branch = git_utils.detect_default_branch(self._proj_path,
self.UPSTREAM_REMOTE_NAME)
return git_utils.get_sha_for_branch(
self._proj_path, self.UPSTREAM_REMOTE_NAME + '/' + branch)
def update(self) -> None:
"""Updates the package.
Has to call check() before this function.
"""
print(f"Running `git merge {self._new_identifier.version}`...")
git_utils.merge(self._proj_path, self._new_identifier.version)
def _determine_android_fetch_ref(self) -> str:
"""Returns the ref that should be fetched from the android remote."""
# It isn't particularly efficient to reparse the tree for every
# project, but we don't guarantee that all paths passed to updater.sh
# are actually in the same tree so it wouldn't necessarily be correct
# to do this once at the top level. This isn't the slow part anyway,
# so it can be dealt with if that ever changes.
root = fileutils.find_tree_containing(self._proj_path)
manifest = Manifest.for_tree(root)
manifest_path = str(self._proj_path.relative_to(root))
try:
project = manifest.project_with_path(manifest_path)
except KeyError as ex:
raise RuntimeError(
f"Did not find {manifest_path} in {manifest.path} (tree root is {root})"
) from ex
return project.revision