blob: 5d69536b6c1431a5521c2836d997a8b30edfdcdf [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2020 by The Linux Foundation and contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import os
import logging
import fnmatch
import grokmirror
from pathlib import Path
# default basic logger. We override it later.
logger = logging.getLogger(__name__)
def get_repo_size(fullpath):
reposize = 0
obj_info = grokmirror.get_repo_obj_info(fullpath)
if 'alternate' in obj_info:
altpath = grokmirror.get_altrepo(fullpath)
reposize = get_repo_size(altpath)
reposize += int(obj_info['size'])
reposize += int(obj_info['size-pack'])
logger.debug('%s size: %s', fullpath, reposize)
return reposize
def generate_bundles(config, outdir, gitargs, revlistargs, maxsize, include):
# uses advisory lock, so its safe even if we die unexpectedly
manifest = grokmirror.read_manifest(config['core'].get('manifest'))
toplevel = os.path.realpath(config['core'].get('toplevel'))
if gitargs:
gitargs = gitargs.split()
if revlistargs:
revlistargs = revlistargs.split()
for repo in manifest.keys():
logger.debug('Checking %s', repo)
# Does it match our globbing pattern?
found = False
for tomatch in include:
if fnmatch.fnmatch(repo, tomatch) or fnmatch.fnmatch(repo, tomatch.lstrip('/')):
found = True
break
if not found:
logger.debug('%s does not match include list, skipping', repo)
continue
repo = repo.lstrip('/')
fullpath = os.path.join(toplevel, repo)
bundledir = os.path.join(outdir, repo.replace('.git', ''))
Path(bundledir).mkdir(parents=True, exist_ok=True)
repofpr = grokmirror.get_repo_fingerprint(toplevel, repo)
logger.debug('%s fingerprint is %s', repo, repofpr)
# Do we have a bundle file already?
bfile = os.path.join(bundledir, 'clone.bundle')
bfprfile = os.path.join(bundledir, '.fingerprint')
logger.debug('Looking for %s', bfile)
if os.path.exists(bfile):
# Do we have a bundle fingerprint?
logger.debug('Found existing bundle in %s', bfile)
if os.path.exists(bfprfile):
with open(bfprfile) as fh:
bfpr = fh.read().strip()
logger.debug('Read bundle fingerprint from %s: %s', bfprfile, bfpr)
if bfpr == repofpr:
logger.info(' skipped: %s (unchanged)', repo)
continue
logger.debug('checking size of %s', repo)
total_size = get_repo_size(fullpath)/1024/1024
if total_size > maxsize:
logger.info(' skipped: %s (%s > %s)', repo, total_size, maxsize)
continue
fullargs = gitargs + ['bundle', 'create', bfile] + revlistargs
logger.debug('Full git args: %s', fullargs)
logger.info(' generate: %s', bfile)
ecode, out, err = grokmirror.run_git_command(fullpath, fullargs)
if ecode == 0:
with open(bfprfile, 'w') as fh:
fh.write(repofpr)
logger.debug('Wrote %s into %s', repofpr, bfprfile)
return 0
def parse_args():
import argparse
# noinspection PyTypeChecker
op = argparse.ArgumentParser(prog='grok-bundle',
description='Generate clone.bundle files for use with "repo"',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
op.add_argument('-v', '--verbose', action='store_true',
default=False,
help='Be verbose and tell us what you are doing')
op.add_argument('-c', '--config',
required=True,
help='Location of the configuration file')
op.add_argument('-o', '--outdir',
required=True,
help='Location where to store bundle files')
op.add_argument('-g', '--gitargs',
default='-c core.compression=9',
help='extra args to pass to git')
op.add_argument('-r', '--revlistargs',
default='--branches HEAD',
help='Rev-list args to use')
op.add_argument('-s', '--maxsize', type=int,
default=2,
help='Maximum size of git repositories to bundle (in GiB)')
op.add_argument('-i', '--include', nargs='*',
default='*',
help='List repositories to bundle (accepts shell globbing)')
op.add_argument('--version', action='version', version=grokmirror.VERSION)
opts = op.parse_args()
return opts
def grok_bundle(cfgfile, outdir, gitargs, revlistargs, maxsize, include, verbose=False):
global logger
config = grokmirror.load_config_file(cfgfile)
logfile = config['core'].get('log', None)
if config['core'].get('loglevel', 'info') == 'debug':
loglevel = logging.DEBUG
else:
loglevel = logging.INFO
logger = grokmirror.init_logger('bundle', logfile, loglevel, verbose)
return generate_bundles(config, outdir, gitargs, revlistargs, maxsize, include)
def command():
opts = parse_args()
retval = grok_bundle(
opts.config, opts.outdir, opts.gitargs, opts.revlistargs, opts.maxsize, opts.include, verbose=opts.verbose)
sys.exit(retval)
if __name__ == '__main__':
command()