#!/usr/bin/python # Copyright (C) 2011 Codethink Limited # # 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; version 2 of the License. # # 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import cliapp import json import logging import os __version__ = '0.0' class Lorry(cliapp.Application): def add_settings(self): self.settings.string(['working-area', 'w'], 'use DIR as the working area (for holding ' 'intermediate git repositories, etc)', metavar='DIR') self.settings.string(['gitorious-base-url'], 'prefix project names with URL when constructing ' 'the git URL on Gitorious (default: %default)', metavar='URL', default='git@gitorious.org:baserock-morphs') self.settings.boolean(['pull-only'], 'only pull from upstreams, do not push to ' 'gitorious') self.settings.boolean(['verbose', 'v'], 'report what is going on to stdout') def process_args(self, args): for arg in args: self.progress('Processing spec file %s' % arg) with open(arg) as f: specs = json.load(f) for name in sorted(specs.keys()): self.gitify(name, specs[name]) self.progress('Done') def gitify(self, name, spec): self.progress('Getting %s from %s' % (name, spec['url'])) table = { 'bzr': self.gitify_bzr, 'cvs': self.gitify_cvs, 'git': self.mirror_git, 'svn': self.gitify_svn, } vcstype = spec['type'] if vcstype not in table: raise cliapp.AppException('Unknown VCS type %s' % vcstype) dirname = self.dirname(name) if not os.path.exists(dirname): os.mkdir(dirname) gitdir = os.path.join(dirname, 'git') table[vcstype](dirname, gitdir, spec) if not self.settings['pull-only']: self.push_to_gitorious(name, gitdir) def mirror_git(self, dirname, gitdir, spec): if not os.path.exists(gitdir): self.progress('.. doing initial clone') self.run_program(['git', 'clone', spec['url'], gitdir]) else: self.progress('.. updating existing clone') self.run_program(['git', 'pull'], cwd=gitdir) def gitify_bzr(self, dirname, gitdir, spec): self.progress('.. fast-exporting from bzr') export = os.path.join(dirname, 'fast-export') self.run_program(['bzr', 'fast-export', '--quiet', spec['url'], export]) if not os.path.exists(gitdir): self.progress('.. creating git repo') os.mkdir(gitdir) self.run_program(['git', 'init', '.'], cwd=gitdir) self.progress('.. reading fast-export data') with open(export) as f: data = f.read() self.progress('.. fast-importing into git') self.run_program(['git', 'fast-import'], stdin=data, cwd=gitdir) def gitify_svn(self, dirname, gitdir, spec): if not os.path.exists(gitdir): self.progress('.. doing initial clone') os.mkdir(gitdir) self.run_program(['git', 'svn', 'clone', spec['url'], gitdir]) else: self.progress('.. updating existing clone') self.run_program(['git', 'svn', 'rebase'], cwd=gitdir) def gitify_cvs(self, dirname, gitdir, spec): self.run_program(['git', 'cvsimport', '-d', spec['url'], '-C', gitdir, spec['module']]) def push_to_gitorious(self, project_name, gitdir): out = self.run_program(['git', 'remote', 'show'], cwd=gitdir) if 'gitorious' not in out.splitlines(): self.progress('.. adding gitorious as a remote') url = ('%s/%s.git' % (self.settings['gitorious-base-url'], project_name)) self.run_program(['git', 'remote', 'add', 'gitorious', url], cwd=gitdir) self.progress('.. pushing to gitorious') self.run_program(['git', 'push', 'gitorious', 'master'], cwd=gitdir) def run_program(self, argv, **kwargs): exit, out, err = self.runcmd_unchecked(argv, **kwargs) logging.debug('Command: %s\nExit: %s\nStdout:\n%sStderr:\n%s' % (argv, exit, self.indent(out), self.indent(err))) if exit != 0: raise Exception('%s failed (exit code %s):\n%s' % (' '.join(argv), exit, self.indent(err))) return out def indent(self, string): return ''.join(' %s\n' % line for line in string.splitlines()) def dirname(self, project_name): assert '/' not in project_name assert '\0' not in project_name return os.path.join(self.settings['working-area'], project_name) def progress(self, msg): logging.debug(msg) if self.settings['verbose']: self.output.write('%s\n' % msg) if __name__ == '__main__': Lorry(version=__version__).run()