1# Copyright (C) 2018 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Module to check updates from Git upstream.""" 15 16 17import datetime 18 19import fileutils 20import git_utils 21import metadata_pb2 # pylint: disable=import-error 22import updater_utils 23 24 25class GitUpdater(): 26 """Updater for Git upstream.""" 27 28 def __init__(self, url, proj_path, metadata): 29 if url.type != metadata_pb2.URL.GIT: 30 raise ValueError('Only support GIT upstream.') 31 self.proj_path = proj_path 32 self.metadata = metadata 33 self.upstream_url = url 34 self.upstream_remote_name = None 35 self.android_remote_name = None 36 self.new_version = None 37 38 def _setup_remote(self): 39 remotes = git_utils.list_remotes(self.proj_path) 40 for name, url in remotes.items(): 41 if url == self.upstream_url.value: 42 self.upstream_remote_name = name 43 44 # Guess android remote name. 45 if '/platform/external/' in url: 46 self.android_remote_name = name 47 48 if self.upstream_remote_name is None: 49 self.upstream_remote_name = "update_origin" 50 git_utils.add_remote(self.proj_path, self.upstream_remote_name, 51 self.upstream_url.value) 52 53 git_utils.fetch(self.proj_path, 54 [self.upstream_remote_name, self.android_remote_name]) 55 56 def check(self): 57 """Checks upstream and returns whether a new version is available.""" 58 59 self._setup_remote() 60 if git_utils.is_commit(self.metadata.third_party.version): 61 # Update to remote head. 62 self._check_head() 63 else: 64 # Update to latest version tag. 65 self._check_tag() 66 67 def get_current_version(self): 68 """Returns the latest version name recorded in METADATA.""" 69 return self.metadata.third_party.version 70 71 def get_latest_version(self): 72 """Returns the latest version name in upstream.""" 73 return self.new_version 74 75 def _check_tag(self): 76 tags = git_utils.list_remote_tags(self.proj_path, 77 self.upstream_remote_name) 78 current_ver = self.get_current_version() 79 self.new_version = updater_utils.get_latest_version( 80 current_ver, tags) 81 print('Current version: {}. Latest version: {}'.format( 82 current_ver, self.new_version), end='') 83 84 def _check_head(self): 85 commits = git_utils.get_commits_ahead( 86 self.proj_path, self.upstream_remote_name + '/master', 87 self.android_remote_name + '/master') 88 89 if not commits: 90 self.new_version = self.get_current_version() 91 return 92 93 self.new_version = commits[0] 94 95 commit_time = git_utils.get_commit_time(self.proj_path, commits[-1]) 96 time_behind = datetime.datetime.now() - commit_time 97 print('{} commits ({} days) behind.'.format( 98 len(commits), time_behind.days), end='') 99 100 def _write_metadata(self, path): 101 updated_metadata = metadata_pb2.MetaData() 102 updated_metadata.CopyFrom(self.metadata) 103 updated_metadata.third_party.version = self.new_version 104 fileutils.write_metadata(path, updated_metadata) 105 106 def update(self): 107 """Updates the package. 108 109 Has to call check() before this function. 110 """ 111 upstream_branch = self.upstream_remote_name + '/master' 112 113 print("Running `git merge {merge_branch}`..." 114 .format(merge_branch=self.new_version)) 115 git_utils.merge(self.proj_path, self.new_version) 116 self._write_metadata(self.proj_path) 117 git_utils.add_file(self.proj_path, 'METADATA') 118