#!/usr/bin/env python # # Copyright (c) 2019-2020,2022 Arm Limited. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # # 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 # # 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. # import argparse import json import os import subprocess import sys def print_args(args, **kwargs): cwd = kwargs['cwd'] if isinstance(args, list): args = ' '.join(args) print('%s$ %s' % (cwd, args)) def check_call(args, **kwargs): print_args(args, **kwargs) return subprocess.check_call(args, **kwargs) def check_output(args, **kwargs): print_args(args, **kwargs) return subprocess.check_output(args, **kwargs) ############################################################################### # Git class ############################################################################### class Git(object): def __init__(self, pwd, path, name, fetchurl, pushurl=None, revision='master'): self.pwd = pwd self.path = path self.name = name self.absolutepath = os.path.join(pwd, path) self.fetchurl = fetchurl self.pushurl = pushurl self.revision = revision def checkout_and_update(self): self.init() self.remote_add(self.name, self.revision, self.fetchurl, self.pushurl) self.fetch(self.name, self.revision) self.checkout(self.name, 'FETCH_HEAD') def init(self): if not os.path.exists(self.absolutepath): os.makedirs(self.absolutepath) if not os.path.exists(os.path.join(self.absolutepath, '.git')): check_output(['git', 'init'], cwd=self.absolutepath) def remote_add(self, name, revision, fetchurl, pushurl): remotes = self.__get_remotes() if name in remotes: if fetchurl != remotes[name]['fetch']: raise Exception("Fetch url '%s' from repository for remote '%s' does not match fetch url '%s' from manifest." % (fetchurl, name, remotes[name]['fetch'])) if pushurl not in (None, remotes[name]['push']): raise Exception("Push url '%s' from repository for remote '%s' does not match push url '%s' from manifest." % (pushurl, name, remotes[name]['push'])) else: check_output(['git', 'remote', 'add', '-m', revision, name, fetchurl], cwd=self.absolutepath) if pushurl: check_output(['git', 'remote', 'set-url', '--add', '--push', name, pushurl], cwd=self.absolutepath) def fetch(self, name, revision): check_output(['git', 'fetch', name, revision], cwd=self.absolutepath) def checkout(self, name, revision): rev = self.__get_rev(name, revision) check_output(['git', 'checkout', rev], stderr=subprocess.STDOUT, cwd=self.absolutepath) def get_dict(self, sha1): data = {} data['path'] = self.path data['name'] = self.name data['fetchurl'] = self.fetchurl if self.pushurl: data['pushurl'] = self.pushurl if sha1: data['revision'] = self.__get_rev(self.name, self.revision) else: data['revision'] = self.revision return data def __get_rev(self, name, revision): try: rev = check_output(['git', 'rev-parse', name + '/' + revision], stderr=subprocess.STDOUT, cwd=self.absolutepath) except: rev = check_output(['git', 'rev-parse', revision], cwd=self.absolutepath) return rev.decode('utf-8').strip() def __get_remotes(self): remotes = {} for remote in check_output(['git', 'remote'], cwd=self.absolutepath).decode('utf-8').splitlines(): fetch = check_output(['git', 'remote', 'get-url', remote], cwd=self.absolutepath).decode('utf-8').strip() push = check_output(['git', 'remote', 'get-url', '--push', remote], cwd=self.absolutepath).decode('utf-8').strip() remotes[remote] = { 'fetch': fetch, 'push': push } return remotes ############################################################################### # Externals class ############################################################################### class Externals: def __init__(self, config): self.externals = [] self.load_config(config) def load_config(self, config): self.pwd = os.path.dirname(os.path.realpath(config)) with open(config, 'r') as fp: data = json.load(fp) for ext in data['externals']: git = Git(self.pwd, ext['path'], ext.get('name', 'origin'), ext['fetchurl'], ext.get('pushurl', None), ext['revision']) self.externals.append(git) def fetch(self): for ext in self.externals: ext.checkout_and_update() def get_dict(self, sha1): data = { 'externals': [] } for ext in self.externals: data['externals'].append(ext.get_dict(sha1)) return data def dump(self, fhandle, sha1): fhandle.write(json.dumps(self.get_dict(sha1), sort_keys=False, separators=(',', ': '), indent=4)) ############################################################################### # Handle functions ############################################################################### def handle_fetch(args): externals = Externals(args.configuration) externals.fetch() def handle_dump(args): externals = Externals(args.configuration) externals.dump(sys.stdout, args.sha1) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Fetch external repositories.') parser.add_argument('-c', '--configuration', default='externals.json', help='Externals configuration file') subparsers = parser.add_subparsers() subparser = subparsers.add_parser('fetch', description='Fetch external repositories.') subparser.set_defaults(func=handle_fetch) subparser = subparsers.add_parser('dump', description='Dump configuration.') subparser.set_defaults(func=handle_dump) subparser.add_argument('-s', '--sha1', action='store_true', help='Replace revision with current SHA-1') args = parser.parse_args() if 'func' not in args: parser.print_help() sys.exit(2) sys.exit(args.func(args))