#!/usr/bin/python -tt # 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 2 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Copyright 2008 Red Hat, Inc - written by seth vidal skvidal at fedoraproject.org # merge repos from arbitrary repo urls import sys import os import shutil import yum from yum.misc import unique, getCacheDir import rpmUtils.arch import operator import createrepo from optparse import OptionParser # eventual plan # take repo paths from cli # produce new repo metadata from merging the two together. # it should always operate globally on all arches in all repos #options: # --repo=1,url, --repo2,url, --repo3,url --exclude=repo,pkgspec, # --exclude=repo,pkgspec, # merge comps and produce a single new comps file #TODO: # excludes? # grab groups and merge # grab other metadata and merge (updateinfo.xml???) # make sqlite dbs class RepoMergeBase(): def __init__(self, repolist=[]): self.repolist = repolist self.outputdir = '%s/merged_repo' % os.getcwd() self.exclude_tuples = [] self.sort_func = None # callback function to magically sort pkgs self.mdconf = createrepo.MetaDataConfig() self.yumbase = yum.YumBase() self.yumbase.conf.cachedir = getCacheDir() self.yumbase.conf.cache = 0 # default to all arches self.archlist = unique(rpmUtils.arch.arches.keys() + rpmUtils.arch.arches.values()) def merge_repos(self): self.yumbase.repos.disableRepo('*') # add our repos and their merge_rank - obviousl count = 0 for r in self.repolist: count +=1 rid = 'repo%s' % count n = self.yumbase.add_enable_repo(rid, baseurls=[r]) n._merge_rank = count #setup our sacks self.yumbase._getSacks(archlist=self.archlist) # repos are sorted so pkg name.arch in repo1 trumps pkg name.arch in repo2 myrepos = self.yumbase.repos.listEnabled() if self.sort_func: myrepos.sort(self.sort_func) else: # sort the repos by _merge_rank # - lowest number is the highest rank (1st place, 2ndplace, etc) myrepos.sort(key=operator.attrgetter('_merge_rank')) #print 'original pkg set: %s' % len(self.yumbase.pkgSack) for repo in myrepos: for pkg in repo.sack: others = self.yumbase.pkgSack.searchNevra(name=pkg.name, arch=pkg.arch) # NOTE the above is definitely going to catch other versions which may # be an invalid comparison if len(others) > 1: for thatpkg in others: if pkg.repoid == thatpkg.repoid: continue if pkg.repo._merge_rank < thatpkg.repo._merge_rank: thatpkg.repo.sack.delPackage(thatpkg) #print 'reduced pkg set: %s' % len(self.yumbase.pkgSack) def write_metadata(self, outputdir=None): self.mdconf.pkglist = self.yumbase.pkgSack self.mdconf.directory = self.outputdir if outputdir: self.mdconf.directory = outputdir #if os.path.exists(self.mdconf.directory): # shutil.rmtree(self.mdconf.directory) # maybe something a bit less crazy os.makedirs(self.mdconf.directory) mdgen = createrepo.MetaDataGenerator(config_obj=self.mdconf) mdgen.doPkgMetadata() mdgen.doRepoMetadata() mdgen.doFinalMove() def parse_args(args): usage = """ repomerge: take 2 or more repositories and merge their metadata into a new repo repomerge --repo=url --repo=url --outputdir=/some/path""" parser = OptionParser(version = "repomerge 0.1", usage=usage) # query options parser.add_option("-r", "--repo", dest='repos', default=[], action="append", help="repo url") parser.add_option("-a", "--archlist", default=[], action="append", help="Defaults to all arches - otherwise specify arches") parser.add_option("-d", "--database", default=False, action="store_true") parser.add_option("-o", "--outputdir", default=None) (opts, argsleft) = parser.parse_args() if len(opts.repos) < 2: parser.print_usage() sys.exit(1) # sort out the comma-separated crap we somehow inherited. archlist = [] for a in opts.archlist: for arch in a.split(','): archlist.append(arch) opts.archlist = archlist return opts def main(args): opts = parse_args(args) rm = RepoMergeBase(opts.repos) if opts.archlist: rm.archlist = opts.archlist if opts.outputdir: rm.outputdir = opts.outputdir if opts.database: rm.mdconf.database = True rm.merge_repos() rm.write_metadata() if __name__ == "__main__": main(sys.argv[1:])