blob: 8bef771c23d5bc490c2f317566a89d0bebb2cc51 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2018 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 os
import sys
import grokmirror
import logging
import fnmatch
import subprocess
logger = logging.getLogger(__name__)
def git_rev_parse_all(gitdir):
args = ['rev-parse', '--all']
retcode, output, error = grokmirror.run_git_command(gitdir, args)
if error:
# Put things we recognize into debug
debug = list()
warn = list()
for line in error.split('\n'):
warn.append(line)
if debug:
logger.debug('Stderr: %s', '\n'.join(debug))
if warn:
logger.warning('Stderr: %s', '\n'.join(warn))
return output
def git_remote_update(args, fullpath):
retcode, output, error = grokmirror.run_git_command(fullpath, args)
if error:
# Put things we recognize into debug
debug = list()
warn = list()
for line in error.split('\n'):
if line.find('From ') == 0:
debug.append(line)
elif line.find('-> ') > 0:
debug.append(line)
else:
warn.append(line)
if debug:
logger.debug('Stderr: %s', '\n'.join(debug))
if warn:
logger.warning('Stderr: %s', '\n'.join(warn))
def dumb_pull_repo(gitdir, remotes, svn=False):
# verify it's a git repo and fetch all remotes
logger.debug('Will pull %s with following remotes: %s', gitdir, remotes)
old_revs = git_rev_parse_all(gitdir)
if not old_revs:
logger.critical('Error opening %s.', gitdir)
logger.critical('Make sure it is a bare git repository.')
sys.exit(1)
try:
grokmirror.lock_repo(gitdir, nonblocking=True)
except IOError:
logger.info('Could not obtain exclusive lock on %s', gitdir)
logger.info('\tAssuming another process is running.')
return False
if svn:
logger.debug('Using git-svn for %s', gitdir)
for remote in remotes:
# arghie-argh-argh
if remote == '*':
remote = '--all'
logger.info('Running git-svn fetch %s in %s', remote, gitdir)
args = ['svn', 'fetch', remote]
git_remote_update(args, gitdir)
else:
# Not an svn remote
myremotes = grokmirror.list_repo_remotes(gitdir)
if not len(myremotes):
logger.info('Repository %s has no defined remotes!', gitdir)
return False
logger.debug('existing remotes: %s', myremotes)
for remote in remotes:
remotefound = False
for myremote in myremotes:
if fnmatch.fnmatch(myremote, remote):
remotefound = True
logger.debug('existing remote %s matches %s', myremote, remote)
args = ['remote', 'update', myremote, '--prune']
logger.info('Updating remote %s in %s', myremote, gitdir)
git_remote_update(args, gitdir)
if not remotefound:
logger.info('Could not find any remotes matching %s in %s', remote, gitdir)
grokmirror.unlock_repo(gitdir)
new_revs = git_rev_parse_all(gitdir)
if old_revs == new_revs:
logger.debug('No new revs, no updates')
return False
logger.debug('New revs found -- new content pulled')
return True
def run_post_update_hook(hookscript, gitdir):
if hookscript == '':
return
if not os.access(hookscript, os.X_OK):
logger.warning('post_update_hook %s is not executable', hookscript)
return
args = [hookscript, gitdir]
logger.debug('Running: %s', ' '.join(args))
(output, error) = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
error = error.decode().strip()
output = output.decode().strip()
if error:
# Put hook stderror into warning
logger.warning('Hook Stderr: %s', error)
if output:
# Put hook stdout into info
logger.info('Hook Stdout: %s', output)
def parse_args():
import argparse
# noinspection PyTypeChecker
op = argparse.ArgumentParser(prog='grok-dumb-pull',
description='Fetch remotes in repositories not managed by grokmirror',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
op.add_argument('-v', '--verbose', dest='verbose', action='store_true',
default=False,
help='Be verbose and tell us what you are doing')
op.add_argument('-s', '--svn', dest='svn', action='store_true',
default=False,
help='The remotes for these repositories are Subversion')
op.add_argument('-r', '--remote-names', dest='remotes', action='append',
default=None,
help='Only fetch remotes matching this name (accepts shell globbing)')
op.add_argument('-u', '--post-update-hook', dest='posthook',
default='',
help='Run this hook after each repository is updated.')
op.add_argument('-l', '--logfile', dest='logfile',
default=None,
help='Put debug logs into this file')
op.add_argument('--version', action='version', version=grokmirror.VERSION)
op.add_argument('paths', nargs='+', help='Full path(s) of the repos to pull')
opts = op.parse_args()
if not len(opts.paths):
op.error('You must provide at least a path to the repos to pull')
return opts
def dumb_pull(paths, verbose=False, svn=False, remotes=None, posthook='', logfile=None):
global logger
loglevel = logging.INFO
logger = grokmirror.init_logger('dumb-pull', logfile, loglevel, verbose)
if remotes is None:
remotes = ['*']
# Find all repositories we are to pull
for entry in paths:
if entry[-4:] == '.git':
if not os.path.exists(entry):
logger.critical('%s does not exist', entry)
continue
logger.debug('Found %s', entry)
didwork = dumb_pull_repo(entry, remotes, svn=svn)
if didwork:
run_post_update_hook(posthook, entry)
else:
logger.debug('Finding all git repos in %s', entry)
for founddir in grokmirror.find_all_gitdirs(entry):
didwork = dumb_pull_repo(founddir, remotes, svn=svn)
if didwork:
run_post_update_hook(posthook, founddir)
def command():
opts = parse_args()
return dumb_pull(
opts.paths, verbose=opts.verbose, svn=opts.svn, remotes=opts.remotes,
posthook=opts.posthook, logfile=opts.logfile)
if __name__ == '__main__':
command()