diff --git a/README b/README index f351c313..34f436e9 100644 --- a/README +++ b/README @@ -10,6 +10,10 @@ https://forgesvn1.novell.com/svn/opensuse/trunk/buildservice/src/clientlib/pytho INSTALLATION: +RPM packages are here (yum repository): +http://repos.opensuse.org/opensuse/repositories/openSUSE:Tools/ + +To install from svn, do python setup.py build python setup.py install # create a symlink 'osc' in your path pointing to osc.py. @@ -28,10 +32,8 @@ on SUSE anyway): CONFIGURATION: -For authentication, put your account data into your ~/.netrc file, like this -line: - machine api.opensuse.org login $login password $pass - +osc uses authentication data if it finds it in .netrc, otherwise it will ask +for username/password once, and store it in ~/.oscrc. @@ -49,6 +51,7 @@ osc co Apache subversion foo # single file # update working copy osc up osc up +osc up * # from within a project dir, update all packages # check in osc ci # current dir @@ -58,20 +61,22 @@ osc ci file1 file2 ... # show status osc st osc st +osc st file1 file2 ... # initialize a source directory to be a # working copy of project package osc init -# schedule file foo to be added / deleted -osc add foo -osc rm foo +# schedule files foo to be added / deleted +osc add file1 file2 ... +osc rm file1 file2 ... -# add all unknown files and remove all missing files +# add all unknown files AND remove all missing files osc addremove # show diff -osc diff [file] +osc diff # current dir +osc diff file1 file2 ... # show build results (xml) osc results @@ -90,3 +95,26 @@ osc meta Apache osc meta Apache subversion osc id username + + + +HINT FOR W3M USERS + +Putting the following in the file ~/.w3m/passwd will make +w3m know the credentials for the buildservice servers: + +""" +host api.opensuse.org + port 80 + realm Authentication required + login foo + password bar + +host build.opensuse.org + port 80 + realm openSUSE Build Service + login foo + password bar +""" + +chmod 0600 ~/.w3m/passwd diff --git a/TODO b/TODO index b428e9bb..7b0ac231 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,27 @@ - implement 'info' command - implement 'mv' command +- implement 'trigger-rebuild' update: handle local modifications checkin: - fix argument handling - - aggregate files to check in per directory - handle error if PUT fails, so the change is not committed to localmeta -diff: use rev numbers in diff header lines + +results: aggregate results of all packages in a project + + +split functionality that needs prj/pac as commandline arguments into a seperate tool (oscremote) + +implement a package search / indexing + +edit user data + + + + + diff --git a/osc-wrapper.py b/osc-wrapper.py index 2544946e..1658efc3 100644 --- a/osc-wrapper.py +++ b/osc-wrapper.py @@ -1,4 +1,8 @@ #!/usr/bin/env python + +# this wrapper exists so it can be put into /usr/bin, but still allows the +# python module to be called within the source directory during development + from osc import commandline from osc.core import init_basicauth diff --git a/osc/commandline.py b/osc/commandline.py index 6233c47d..3b5d5314 100755 --- a/osc/commandline.py +++ b/osc/commandline.py @@ -7,7 +7,6 @@ from core import * - def main(): cmd = sys.argv[1] @@ -47,18 +46,22 @@ def main(): print ''.join(show_project_meta(project)) elif cmd == 'diff': - wd = os.curdir - package = store_read_package(wd) - project = store_read_project(wd) - if len(sys.argv) > 2: - filename = sys.argv[2] - if filename: - print get_source_file_diff(project, package, filename) - else: + args = parseargs() + pacs = findpacs(args) + + for p in pacs: + if p.todo == []: + for i in p.filenamelist: + if p.status(i) == 'M': + p.todo.append(i) + d = [] - for filename in meta_get_filelist(project, package): - d.append(get_source_file_diff(project, package, filename)) - print ''.join(d) + for filename in p.todo: + d.append('Index: %s\n' % filename) + d.append('===================================================================\n') + d.append(get_source_file_diff(p.dir, filename, p.rev)) + if d: + print ''.join(d) elif cmd == 'co' or cmd == 'checkout': @@ -85,31 +88,20 @@ def main(): elif cmd == 'st' or cmd == 'status': args = parseargs() + pacs = findpacs(args) - for arg in args: + for p in pacs: - if os.path.isfile(arg): - wd = os.path.dirname(arg) - filenames = [ os.path.basename(arg) ] - elif os.path.isdir(arg): - wd = arg - package = store_read_package(wd) - project = store_read_project(wd) - filenames = meta_get_filelist(project, package) + # no files given as argument? Take all files in current dir + if not p.todo: + p.todo = p.filenamelist + p.filenamelist_unvers - # add files which are not listed in _meta - for i in os.listdir(arg): - if i not in filenames and i not in exclude_stuff: - filenames.insert(0, i) - - os.chdir(wd) - check_store_version() - - filelist = localmeta_get_filelist() - for filename in filenames: - s = get_file_status(project, package, filename, filelist=filelist) - if not s.startswith(' '): - print s + for filename in p.todo: + s = p.status(filename) + if s == 'F': + print statfrmt('!', filename) + elif s != ' ': + print statfrmt(s, filename) elif cmd == 'add': @@ -123,155 +115,122 @@ def main(): if not os.path.exists(filename): print "file '%s' does not exist" % filename sys.exit(1) - for filename in filenames: - localmeta_addfile(filename) - print 'A ', filename + + pacs = findpacs(filenames) + + for pac in pacs: + for filename in pac.todo: + if filename in exclude_stuff: + continue + + pac.addfile(filename) + print statfrmt('A', filename) + elif cmd == 'addremove': args = parseargs() + pacs = findpacs(args) + for p in pacs: - for arg in args: + p.todo = p.filenamelist + p.filenamelist_unvers - if os.path.isfile(arg): - wd = os.path.dirname(arg) - filenames = [ os.path.basename(arg) ] - elif os.path.isdir(arg): - wd = arg - package = store_read_package(wd) - project = store_read_project(wd) - filenames = meta_get_filelist(project, package) + for filename in p.todo: + if filename in exclude_stuff: + continue + state = p.status(filename) + if state == '?': + p.addfile(filename) + print statfrmt('A', filename) + elif state == '!': + p.put_on_deletelist(filename) + p.write_deletelist() + os.unlink(os.path.join(p.storedir, filename)) + print statfrmt('D', filename) - # add files which are not listed in _meta - for i in os.listdir(arg): - if i not in filenames and i not in exclude_stuff: - filenames.insert(0, i) - - - - filelist = localmeta_get_filelist() - for filename in filenames: - st = get_file_status(project, package, filename, filelist=filelist) - if st.startswith('?'): - localmeta_addfile(filename) - print 'A ', filename - elif st.startswith('!'): - print 'D ', filename - localmeta_removefile(filename) elif cmd == 'ci' or cmd == 'checkin': + init_basicauth() + args = parseargs() - for arg in args: + pacs = findpacs(args) - if os.path.isfile(arg): - wd = os.path.dirname(arg) - filenames = [ os.path.basename(arg) ] - elif os.path.isdir(arg): - wd = arg - package = store_read_package(wd) - project = store_read_project(wd) - filenames = meta_get_filelist(project, package) + for p in pacs: + p.todo = p.filenamelist_unvers + p.filenamelist - # add files which are not listed in _meta - for i in os.listdir(arg): - if i not in filenames and i not in exclude_stuff: - filenames.insert(0, i) - - os.chdir(wd) - - files_to_send = [] - files_to_delete = [] - - filelist = localmeta_get_filelist() - for filename in filenames: - st = get_file_status(project, package, filename, filelist=filelist) - if st.startswith('A') or st.startswith('M'): - files_to_send.append(filename) + for filename in p.todo: + st = p.status(filename) + if st == 'A' or st == 'M': + p.todo_send.append(filename) print 'Sending %s' % filename - elif st.startswith('D'): - files_to_delete.append(filename) + elif st == 'D': + p.todo_delete.append(filename) print 'Deleting %s' % filename - if not files_to_send and not files_to_delete: - print 'nothing to do' - sys.exit(0) + if not p.todo_send and not p.todo_delete: + print 'nothing to do for package %s' % p.name + continue print 'Transmitting file data ', - for filename in files_to_send: - put_source_file(project, package, filename) - copy_file(filename, os.path.join(store, filename)) - for filename in files_to_delete: - del_source_file(project, package, filename) + for filename in p.todo_send: + put_source_file(p.prjname, p.name, os.path.join(p.dir, filename)) + #copy_file(filename, os.path.join(store, filename)) + for filename in p.todo_delete: + del_source_file(p.prjname, p.name, filename) + p.to_be_deleted.remove(filename) + + p.update_filesmeta() + p.write_deletelist() print elif cmd == 'up' or cmd == 'update': args = parseargs() + pacs = findpacs(args) - for arg in args: - - if os.path.isfile(arg): - wd = os.path.dirname(arg) - filenames = [ os.path.basename(arg) ] - elif os.path.isdir(arg): - wd = arg - package = store_read_package(wd) - project = store_read_project(wd) - - ## add files which are not listed in _meta - #for i in os.listdir(arg): - # if i not in filenames and i not in exclude_stuff: - # filenames.insert(0, i) - - olddir = os.getcwd() - os.chdir(wd) - check_store_version() + for p in pacs: # save filelist before replacing the meta file - filenames = localmeta_get_filelist() - os.chdir(store) - # update filelist - f = open('_files', 'w') - f.write(''.join(show_files_meta(project, package))) - f.close() + saved_filenames = p.filenamelist + p.update_filesmeta() + p = Package(p.dir) # which files do no longer exist upstream? disappeared = [] - upstream_files = meta_get_filelist(project, package) - for filename in filenames: - if filename not in upstream_files: + for filename in saved_filenames: + if filename not in p.filenamelist: disappeared.append(filename) - for filename in filenames: + for filename in saved_filenames: if filename in disappeared: - print 'D %s' % filename - os.unlink(filename) + print statfrmt('D', filename) + p.delfile(filename) continue - get_source_file(project, package, filename) - wcfilename = os.path.join(os.pardir, os.path.basename(filename)) + for filename in p.filenamelist: - if not os.path.exists(wcfilename): - print 'A %s' % filename - copy_file(filename, wcfilename) - - elif dgst(wcfilename) != dgst(filename): - print 'U %s' % filename - copy_file(filename, wcfilename) - - else: + state = p.status(filename) + if state == 'M': + print 'file %s is locally modified... fixme' % filename + p.updatefile(filename) + print statfrmt('U', filename) + elif state == '!': + p.updatefile(filename) + print 'Restored \'%s\'' % filename + elif state == 'F': + p.updatefile(filename) + print statfrmt('A', filename) + elif state == ' ': pass - # get current meta file - f = open('_meta', 'w') - f.write(''.join(show_package_meta(project, package))) - f.close() - os.chdir(olddir) + p.update_pacmeta() + + print 'At revision %s.' % p.rev @@ -283,25 +242,26 @@ def main(): sys.exit(1) args = parseargs() + pacs = findpacs(args) - for arg in args: + for p in pacs: - olddir = os.getcwd() + for filename in p.todo: + p.put_on_deletelist(filename) + p.write_deletelist() + try: + os.unlink(os.path.join(p.dir, filename)) + os.unlink(os.path.join(p.storedir, filename)) + except: + pass + print statfrmt('D', filename) - wd = os.path.dirname(arg) or os.curdir - filename = arg - - os.chdir(wd) - - localmeta_removefile(filename) - print 'D %s' % filename - - os.chdir(olddir) elif cmd == 'id': print ''.join(get_user_id(sys.argv[2])) + elif cmd == 'platforms': if len(sys.argv) > 2: project = sys.argv[2] @@ -310,7 +270,6 @@ def main(): print '\n'.join(get_platforms()) - elif cmd == 'results_meta': wd = os.curdir package = store_read_package(wd) @@ -321,6 +280,7 @@ def main(): else: for platform in get_platforms_of_project(project): print ''.join(show_results_meta(project, package, platform)) + elif cmd == 'results': if len(sys.argv) > 3: @@ -347,9 +307,19 @@ def main(): arch = sys.argv[3] print ''.join(get_log(project, package, platform, arch)) + + elif cmd == 'hist' or cmd == 'history': + args = parseargs() + pacs = findpacs(args) + + for p in pacs: + print ''.join(get_history(p.prjname, p.name)) + + else: print "unknown command '%s'" % cmd + if __name__ == '__main__': init_basicauth() main() diff --git a/osc/core.py b/osc/core.py index b5ee7a02..3bda7821 100755 --- a/osc/core.py +++ b/osc/core.py @@ -5,20 +5,15 @@ # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. -__version__ = '0.2' +__version__ = '0.3' import os import sys import urllib2 -import netrc from urlparse import urlunsplit import cElementTree as ET from cStringIO import StringIO -# the needed entry in .netrc looks like this: -# machine api.opensuse.org login your_login password your_pass -info = netrc.netrc() -username, account, password = info.authenticators("api.opensuse.org") from xml.dom.ext.reader import Sax2 from xml.dom.ext import PrettyPrint @@ -31,6 +26,222 @@ store = '.osc' exclude_stuff = [store, '.svn', 'CVS'] +class File: + """represent a file, including its metadata""" + def __init__(self, name, md5, size, mtime): + self.name = name + self.md5 = md5 + self.size = size + self.mtime = mtime + def __str__(self): + return self.name + + +class Package: + """represent a package (its directory) and read/keep/write its metadata""" + def __init__(self, workingdir): + self.dir = workingdir + self.storedir = os.path.join(self.dir, store) + + check_store_version(self.dir) + + self.prjname = store_read_project(self.dir) + self.name = store_read_package(self.dir) + + files_tree = read_filemeta(self.dir) + files_tree_root = files_tree.getroot() + + self.rev = files_tree_root.get('rev') + + self.filenamelist = [] + self.filelist = [] + for node in files_tree_root.findall('entry'): + f = File(node.get('name'), + node.get('md5'), + node.get('size'), + node.get('mtime')) + self.filelist.append(f) + self.filenamelist.append(f.name) + + self.to_be_deleted = read_tobedeleted(self.dir) + + self.todo = [] + self.todo_send = [] + self.todo_delete = [] + + # gather unversioned files (the ones not listed in _meta) + self.filenamelist_unvers = [] + for i in os.listdir(self.dir): + if i in exclude_stuff: + continue + if not i in self.filenamelist: + self.filenamelist_unvers.append(i) + + def addfile(self, n): + st = os.stat(os.path.join(self.dir, n)) + f = File(n, dgst(os.path.join(self.dir, n)), st[6], st[8]) + self.filelist.append(f) + self.filenamelist.append(n) + self.filenamelist_unvers.remove(n) + copy_file(os.path.join(self.dir, n), os.path.join(self.storedir, n)) + + def delfile(self, n): + os.unlink(os.path.join(self.dir, n)) + os.unlink(os.path.join(self.storedir, n)) + + def put_on_deletelist(self, n): + if n not in self.to_be_deleted: + self.to_be_deleted.append(n) + + def write_deletelist(self): + fname = os.path.join(self.storedir, '_to_be_deleted') + f = open(fname, 'w') + f.write('\n'.join(self.to_be_deleted)) + f.write('\n') + f.close() + + def updatefile(self, n): + filename = os.path.join(self.dir, n) + storefilename = os.path.join(self.storedir, n) + mtime = int(self.findfilebyname(n).mtime) + + get_source_file(self.prjname, self.name, n, targetfilename=filename) + os.utime(filename, (-1, mtime)) + + copy_file(filename, storefilename) + os.utime(storefilename, (-1, mtime)) + + def update_filesmeta(self): + meta = ''.join(show_files_meta(self.prjname, self.name)) + f = open(os.path.join(self.storedir, '_files'), 'w') + f.write(meta) + f.close() + + def update_pacmeta(self): + meta = ''.join(show_package_meta(self.prjname, self.name)) + f = open(os.path.join(self.storedir, '_meta'), 'w') + f.write(meta) + f.close() + + def findfilebyname(self, n): + for i in self.filelist: + if i.name == n: + return i + + def status(self, n): + """ + status can be: + + file storefile file present STATUS + exists exists in _files + + x x - 'A' + x x x 'M', if digest differs, else ' ' + x - - '?' + x - x 'D' and listed in _to_be_deleted + - x x '!' + - x - 'D' (when file in working copy is already deleted) + - - x 'F' (new in repo, but not yet in working copy) + - - - NOT DEFINED + + """ + + known_by_meta = False + exists = False + exists_in_store = False + if n in self.filenamelist: + known_by_meta = True + if os.path.exists(os.path.join(self.dir, n)): + exists = True + if os.path.exists(os.path.join(self.storedir, n)): + exists_in_store = True + + + if exists and not exists_in_store and known_by_meta: + state = 'D' + elif n in self.to_be_deleted: + state = 'D' + elif exists and exists_in_store and known_by_meta: + #print self.findfilebyname(n) + if dgst(os.path.join(self.dir, n)) != self.findfilebyname(n).md5: + state = 'M' + else: + state = ' ' + elif exists and not exists_in_store and not known_by_meta: + state = '?' + elif exists and exists_in_store and not known_by_meta: + state = 'A' + elif not exists and exists_in_store and known_by_meta: + state = '!' + elif not exists and not exists_in_store and known_by_meta: + state = 'F' + elif not exists and exists_in_store and not known_by_meta: + state = 'D' + elif not exists and not exists_in_store and not known_by_meta: + print '%s: not exists and not exists_in_store and not nown_by_meta' % n + print 'this code path should never be reached!' + sys.exit(1) + + return state + + + def merge(self, otherpac): + self.todo += otherpac.todo + + def __str__(self): + r = """ +name: %s +prjname: %s +workingdir: %s +localfilelist: %s +rev: %s +'todo' files: %s +""" % (self.name, + self.prjname, + self.dir, + '\n '.join(self.filenamelist), + self.rev, + self.todo) + + return r + + + +def findpacs(files): + pacs = [] + for f in files: + if f in exclude_stuff: + break + + p = filedir_to_pac(f) + known = None + for i in pacs: + if i.name == p.name: + known = i + break + if known: + i.merge(p) + else: + pacs.append(p) + return pacs + + +def read_filemeta(dir): + return ET.parse(os.path.join(dir, store, '_files')) + + +def read_tobedeleted(dir): + r = [] + fname = os.path.join(dir, store, '_to_be_deleted') + + if os.path.exists(fname): + + for i in open(fname, 'r').readlines(): + r.append(i.strip()) + + return r + + def parseargs(): if len(sys.argv) > 2: args = sys.argv[2:] @@ -39,12 +250,46 @@ def parseargs(): return args +def filedir_to_pac(f): + + if os.path.isdir(f): + wd = f + p = Package(wd) + + elif os.path.isfile(f): + wd = os.path.dirname(f) + if wd == '': + wd = os.curdir + p = Package(wd) + p.todo = [ os.path.basename(f) ] + + else: + wd = os.path.dirname(f) + if wd == '': + wd = os.curdir + p = Package(wd) + p.todo = [ os.path.basename(f) ] + + + #else: + # print + # print 'error: %s is neither a valid file or directory' % f + # sys.exit(1) + + return p + + +def statfrmt(statusletter, filename): + return '%s %s' % (statusletter, filename) + + def makeurl(l): """given a list of path compoments, construct a complete URL""" return urlunsplit((scheme, netloc, '/'.join(l), '', '')) def copy_file(src, dst): + # fixme: preserve mtime by default? s = open(src) d = open(dst, 'w') while 1: @@ -55,8 +300,65 @@ def copy_file(src, dst): d.close() +def readauth(): + """look for the credentials. If there aren't any, ask and store them""" + + # + # try .netrc first + # + + # the needed entry in .netrc looks like this: + # machine api.opensuse.org login your_login password your_pass + # but it is not able for credentials containing spaces + import netrc + global username, password + + try: + info = netrc.netrc() + username, account, password = info.authenticators(netloc) + return username, password + + except (IOError, TypeError): + pass + + # + # try .oscrc next + # + import ConfigParser + conffile = os.path.expanduser('~/.oscrc') + if os.path.exists(conffile): + config = ConfigParser.ConfigParser() + config.read(conffile) + username = config.get(netloc, 'user') + password = config.get(netloc, 'pass') + return username, password + + # + # create .oscrc + # + import getpass + print >>sys.stderr, \ +"""your user account / password are not configured yet. +You will be asked for them below, and they will be stored in +%s for later use. +""" % conffile + + username = raw_input('Username: ') + password = getpass.getpass() + + fd = open(conffile, 'w') + os.chmod(conffile, 0600) + print >>fd, '[%s]\nuser: %s\npass: %s' % (netloc, username, password) + fd.close() + + return username, password + + + def init_basicauth(): + username, password = readauth() + passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() # this creates a password manager passmgr.add_password(None, netloc, username, password) @@ -98,21 +400,30 @@ def init_package_dir(project, package, dir): return -def check_store_version(): +def check_store_version(dir): + versionfile = os.path.join(dir, store, '_osclib_version') try: - v = open(os.path.join(store, '_osclib_version')).read().strip() + v = open(versionfile).read().strip() except: v = '' + if v == '': + print 'error: "%s" is not an osc working copy' % dir + sys.exit(1) + if v != __version__: + if v == '0.2': + # 0.2 is fine, no migration needed + f = open(versionfile, 'w') + f.write(__version__ + '\n') + f.close() + return print - print 'the osc metadata of your working copy' - print ' %s' % os.getcwd() + print 'the osc metadata of your working copy "%s"' % dir print 'has the wrong version (%s), should be %s' % (v, __version__) print 'please do a fresh checkout' print sys.exit(1) - def meta_get_packagelist(prj): @@ -158,6 +469,7 @@ def localmeta_addfile(filename): o = open(os.path.join(store, '_files'), 'w') PrettyPrint(doc, stream=o) o.close() + def localmeta_removefile(filename): @@ -195,6 +507,7 @@ def get_slash_source(): r.sort() return r + def show_project_meta(prj): f = urllib2.urlopen(makeurl(['source', prj, '_meta'])) return f.readlines() @@ -204,22 +517,49 @@ def show_package_meta(prj, pac): f = urllib2.urlopen(makeurl(['source', prj, pac, '_meta'])) return f.readlines() + def show_files_meta(prj, pac): f = urllib2.urlopen(makeurl(['source', prj, pac])) return f.readlines() + +def read_meta_from_spec(specfile): + """read Name, Summary and %description from spec file""" + in_descr = False + descr = [] + + if not os.path.isfile(specfile): + print 'file \'%s\' is not a readable file' % specfile + return None + + for line in open(specfile, 'r'): + if line.startswith('Name:'): + name = line.split(':')[1].strip() + if line.startswith('Summary:'): + summary = line.split(':')[1].strip() + if line.startswith('%description'): + in_descr = True + continue + if in_descr and line.startswith('%'): + break + if in_descr: + descr.append(line) + + return name, summary, descr + + def get_user_id(user): u = makeurl(['person', user]) f = urllib2.urlopen(u) return f.readlines() -def get_source_file(prj, package, filename): +def get_source_file(prj, package, filename, targetfilename=None): u = makeurl(['source', prj, package, filename]) #print 'checking out', u f = urllib2.urlopen(u) - o = open(filename, 'w') + o = open(targetfilename or filename, 'w') while 1: buf = f.read(BUFSIZE) if not buf: break @@ -227,84 +567,22 @@ def get_source_file(prj, package, filename): o.close() - def dgst(file): - if not os.path.exists(file): - return None + #if not os.path.exists(file): + #return None - import sha - s = sha.new() + import md5 + s = md5.new() f = open(file, 'r') while 1: buf = f.read(BUFSIZE) if not buf: break s.update(buf) - return s.digest() + return s.hexdigest() -def get_file_status(prj, package, filename, filelist=None): - """ - status can be: - - file storefile file present STATUS - exists exists in _files - - x x - 'D' - x x x 'M', if digest differs, else ' ' - x - - '?' - x - x 'A' - - x x '!' - - x - 'D' (when file in working copy is already deleted) - - - x NOT DEFINED - - - - NEVER REACHED - - """ - known_by_meta = False - exists = False - exists_in_store = False - - if not filelist: - filelist = localmeta_get_filelist() - - if filename in filelist: - known_by_meta = True - - if os.path.exists(filename): - exists = True - - if os.path.exists(os.path.join(store, filename)): - exists_in_store = True - - if exists and exists_in_store and not known_by_meta: - state = 'D' - elif exists and exists_in_store and known_by_meta: - if dgst(filename) != dgst(os.path.join(store, filename)): - state = 'M' - else: - state = ' ' - elif exists and not exists_in_store and not known_by_meta: - state = '?' - elif exists and not exists_in_store and known_by_meta: - state = 'A' - elif not exists and exists_in_store and known_by_meta: - state = '!' - elif not exists and not exists_in_store and known_by_meta: - print '%s: not exists and not exists_in_store and known_by_meta' % filename - print 'this state is undefined!' - sys.exit(1) - elif not exists and exists_in_store and not known_by_meta: - state = 'D' - elif not exists and not exists_in_store and not known_by_meta: - print '%s: not exists and not exists_in_store and not nown_by_meta' % filename - print 'this code path should never be reached!' - sys.exit(1) - - - return '%s %s' % (state, filename) - - -def get_source_file_diff(prj, package, filename): +def get_source_file_diff_upstream(prj, package, filename): url = makeurl(['source', prj, package, filename]) f = urllib2.urlopen(url) @@ -319,6 +597,26 @@ def get_source_file_diff(prj, package, filename): return ''.join(d) +def get_source_file_diff(dir, filename, rev): + import difflib + + file1 = os.path.join(dir, filename) + file2 = os.path.join(dir, store, filename) + + f1 = open(file1, 'r') + f2 = open(file2, 'r') + + d = difflib.unified_diff(\ + f1.readlines(), \ + f2.readlines(), \ + fromfile = '%s (revision %s)' % (filename, rev), \ + tofile = '%s (working copy)' % filename) + + f1.close() + f2.close() + + return ''.join(d) + def put_source_file(prj, package, filename): import othermethods @@ -341,13 +639,17 @@ def del_source_file(prj, package, filename): def make_dir(project, package): #print "creating directory '%s'" % project - print 'A %s' % project + print statfrmt('A', project) if not os.path.exists(project): os.mkdir(project) os.mkdir(os.path.join(project, store)) + f = open(os.path.join(project, store, '_project'), 'w') + f.write(project + '\n') + f.close() + #print "creating directory '%s/%s'" % (project, package) - print 'A %s/%s' % (project, package) + print statfrmt('A', '%s/%s' % (project, package)) if not os.path.exists(os.path.join(project, package)): os.mkdir(os.path.join(project, package)) os.mkdir(os.path.join(project, package, store)) @@ -435,6 +737,15 @@ def get_log(prj, package, platform, arch): return f.readlines() +def get_history(prj, package): + # http://api.opensuse.org/rpm/Apache/factory/i586/apache2/history ? + # http://api.opensuse.org/package/Apache/apache2/history ? + u = makeurl(['package', prj, package, 'history']) + print u + f = urllib2.urlopen(u) + return f.readlines() + + def store_read_project(dir): p = open(os.path.join(dir, store, '_project')).readlines()[0].strip() return p diff --git a/setup.py b/setup.py index c54ff857..3189e815 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from distutils.core import setup setup(name='osc', - version='0.2', + version='0.3', description='opensuse commander', author='Peter Poeml', author_email='poeml@suse.de',