blob: 3abe5be6bb349a13f7cbf7c5f87557722fe1da58 [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 update packages from GitHub archive."""
import json
import re
import shutil
import urllib.request
import archive_utils
import fileutils
import metadata_pb2 # pylint: disable=import-error
import updater_utils
GITHUB_URL_PATTERN = (r'^https:\/\/github.com\/([-\w]+)\/([-\w]+)\/' +
r'(releases\/download\/|archive\/)')
GITHUB_URL_RE = re.compile(GITHUB_URL_PATTERN)
class GithubArchiveUpdater():
"""Updater for archives from GitHub.
This updater supports release archives in GitHub. Version is determined by
release name in GitHub.
"""
VERSION_FIELD = 'tag_name'
def __init__(self, url, proj_path, metadata):
self.proj_path = proj_path
self.metadata = metadata
self.old_url = url
self.owner = None
self.repo = None
self.data = None
self._parse_url(url)
def _parse_url(self, url):
if url.type != metadata_pb2.URL.ARCHIVE:
raise ValueError('Only archive url from Github is supported.')
match = GITHUB_URL_RE.match(url.value)
if match is None:
raise ValueError('Url format is not supported.')
try:
self.owner, self.repo = match.group(1, 2)
except IndexError:
raise ValueError('Url format is not supported.')
def _get_latest_version(self):
"""Checks upstream and returns the latest version name we found."""
url = 'https://api.github.com/repos/{}/{}/releases/latest'.format(
self.owner, self.repo)
with urllib.request.urlopen(url) as request:
self.data = json.loads(request.read().decode())
return self.data[self.VERSION_FIELD]
def _get_current_version(self):
"""Returns the latest version name recorded in METADATA."""
return self.metadata.third_party.version
def _write_metadata(self, url, path):
updated_metadata = metadata_pb2.MetaData()
updated_metadata.CopyFrom(self.metadata)
updated_metadata.third_party.version = self.data[self.VERSION_FIELD]
for metadata_url in updated_metadata.third_party.url:
if metadata_url == self.old_url:
metadata_url.value = url
fileutils.write_metadata(path, updated_metadata)
def check(self):
"""Checks update for package.
Returns True if a new version is available.
"""
latest = self._get_latest_version()
current = self._get_current_version()
print('Current version: {}. Latest version: {}'.format(
current, latest), end='')
return current != latest
def update(self):
"""Updates the package.
Has to call check() before this function.
"""
supported_assets = [
a for a in self.data['assets']
if archive_utils.is_supported_archive(a['browser_download_url'])]
# Finds the minimum sized archive to download.
minimum_asset = min(
supported_assets, key=lambda asset: asset['size'], default=None)
if minimum_asset is not None:
latest_url = minimum_asset.get('browser_download_url')
else:
# Guess the tarball url for source code.
latest_url = 'https://github.com/{}/{}/archive/{}.tar.gz'.format(
self.owner, self.repo, self.data.get('tag_name'))
temporary_dir = None
try:
temporary_dir = archive_utils.download_and_extract(latest_url)
package_dir = archive_utils.find_archive_root(temporary_dir)
self._write_metadata(latest_url, package_dir)
updater_utils.replace_package(package_dir, self.proj_path)
finally:
shutil.rmtree(temporary_dir, ignore_errors=True)
urllib.request.urlcleanup()