diff --git a/CREDITS b/CREDITS new file mode 100644 index 00000000..b3f96fac --- /dev/null +++ b/CREDITS @@ -0,0 +1,5 @@ +There is a number of people who have helped: + +Marcus Rueckert - help and countless suggestions +Christoph Thiel - patch enabling build log following. +Adrian Schroeter - one-liner showing how to handle an error ;-) diff --git a/TODO b/TODO index 3cd58acc..70c60347 100644 --- a/TODO +++ b/TODO @@ -2,17 +2,10 @@ - implement 'info' command - implement 'mv' command -- editing of user data - - - - - editmeta: the API will return a 500 if the xml is broken... the document could be presented for editing again in that case - updatepacmetafromspec -- is that useful? In which form would this be integrated best? -- clean up the way how a .oscrc template is being added: at the moment, there - are two places doing this checkin: diff --git a/osc/build.py b/osc/build.py index cfe64b29..4c68d718 100644 --- a/osc/build.py +++ b/osc/build.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. diff --git a/osc/commandline.py b/osc/commandline.py index 980c52d2..e9147d2e 100755 --- a/osc/commandline.py +++ b/osc/commandline.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. @@ -134,6 +134,20 @@ usage: osc editmeta FooPrj # edit meta of project 'FooPrj' edit_meta(project, None) +def edituser(args): + """edituser: Edit user meta information +usage: osc edituser + +If the named user id does not exist, it will be created. + """ + + if not args or len(args) != 1: + user = conf.config['user'] + else: + user = args[0] + edit_user_meta(user) + + def linkpac(args): """"Link" a package to another package -- possibly cross-project. @@ -473,6 +487,8 @@ usage: osc ci # current dir for filename in p.todo_delete: p.delete_source_file(filename) p.to_be_deleted.remove(filename) + if conf.config['do_commits'] == '1': + p.commit(msg='MESSAGE') p.update_filesmeta() p.write_deletelist() @@ -606,17 +622,17 @@ usage: osc resolved p.clear_from_conflictlist(filename) -def userid(args): - """id: show metadata about user +def usermeta(args): + """usermeta: show metadata about user -usage: osc id +usage: osc usermeta """ if not args: print 'this command requires at least one argument' sys.exit(1) - r = get_user_id(args[0]) + r = get_user_meta(args[0]) if r: print ''.join(r) @@ -842,6 +858,9 @@ BUILD_ROOT or OSC_BUILD_ROOT overrides the build-root. import osc.build + if not os.path.exists('/usr/lib/build/debsort'): + sys.exit('Error: you need build.rpm with version 2006.6.14 or newer.\nSee http://software.opensuse.org/download/openSUSE:/Tools/') + builddist = os.getenv('BUILD_DIST') if builddist: #sys.argv[4] = sys.argv[1] @@ -981,7 +1000,8 @@ cmd_dict = { help: ['help'], buildhistory: ['buildhistory', 'buildhist'], linkpac: ['linkpac'], - userid: ['id'], # <- small difference here + usermeta: ['usermeta'], + edituser: ['edituser'], init: ['init'], # depracated log: ['log'], ls: ['ls', 'list'], diff --git a/osc/conf.py b/osc/conf.py index fd824482..29d49891 100644 --- a/osc/conf.py +++ b/osc/conf.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. @@ -56,6 +56,9 @@ DEFAULTS = { 'apisrv': 'api.opensuse.org', # direct access to "full" tree 'http://api.opensuse.org/rpm/%(project)s/%(repository)s/_repository/%(buildarch)s/%(name)s', ], + + # switched off for now (testing) + 'do_commits': '0', } new_conf_template = """ diff --git a/osc/core.py b/osc/core.py index b3e11a05..2b3c05b9 100755 --- a/osc/core.py +++ b/osc/core.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. @@ -97,6 +97,24 @@ HERE """ +new_user_template = """\ + + %(user)s + PUT_EMAIL_ADDRESS_HERE + PUT_REAL_NAME_HERE + + + + + + + + + + + + +""" buildstatus_symbols = {'succeeded': '.', 'disabled': ' ', @@ -271,10 +289,22 @@ class Package: # escaping '+' in the URL path (note: not in the URL query string) is # only a workaround for ruby on rails, which swallows it otherwise u = makeurl(['source', self.prjname, self.name, pathname2url(n)]) + if conf.config['do_commits'] == '1': + u += '?rev=upload' othermethods.putfile(u, conf.config['user'], conf.config['pass'], file = os.path.join(self.dir, n)) shutil.copy2(os.path.join(self.dir, n), os.path.join(self.storedir, n)) + def commit(self, msg=''): + import othermethods + + u = makeurl(['source', self.prjname, self.name]) + u += '?cmd=commit&rev=upload' + u += '&user=%s' % conf.config['user'] + u += '&comment=%s' % quote_plus(msg) + #print u + f = urlopen(u, data='') + #print f.read() def write_conflictlist(self): if len(self.in_conflict) == 0: @@ -721,7 +751,7 @@ def edit_meta(prj, pac, template=new_package_templ, change_is_required=True): m = urllib2.urlopen(u).readlines() except urllib2.HTTPError, e: if e.code == 404: - m = template % (pac, conf.config['username']) + m = template % (pac, conf.config['user']) else: print 'error getting package meta for project \'%s\' package \'%s\':' % (prj, pac) print e @@ -734,7 +764,7 @@ def edit_meta(prj, pac, template=new_package_templ, change_is_required=True): m = urllib2.urlopen(u).readlines() except urllib2.HTTPError, e: if e.code == 404: - m = new_project_templ % (prj, conf.config['username']) + m = new_project_templ % (prj, conf.config['user']) else: print 'error getting package meta for project \'%s\':' % prj print e @@ -760,6 +790,42 @@ def edit_meta(prj, pac, template=new_package_templ, change_is_required=True): print 'Done.' +def edit_user_meta(user, change_is_required=True): + import othermethods + import tempfile + + u = makeurl(['person', quote_plus(user)]) + + try: + m = urllib2.urlopen(u).readlines() + except urllib2.HTTPError, e: + if e.code == 404: + m = new_user_template % { 'user': user } + else: + print 'error getting metadata for user \'%s\':' % user + print e + sys.exit(1) + + (fd, filename) = tempfile.mkstemp(prefix = 'osc_edituser.', suffix = '.xml', dir = '/tmp') + f = os.fdopen(fd, 'w') + f.write(''.join(m)) + f.close() + timestamp = os.path.getmtime(filename) + + editor = os.getenv('EDITOR', default='vim') + os.system('%s %s' % (editor, filename)) + + if change_is_required == True and os.path.getmtime(filename) == timestamp: + print 'File unchanged. Not saving.' + os.unlink(filename) + + else: + print 'Sending meta data...', + othermethods.putfile(u, conf.config['user'], conf.config['pass'], file=filename) + os.unlink(filename) + print 'Done.' + + def show_files_meta(prj, pac): f = urlopen(makeurl(['source', prj, pac])) return f.readlines() @@ -799,7 +865,7 @@ def read_meta_from_spec(specfile): return name, summary, descr -def get_user_id(user): +def get_user_meta(user): u = makeurl(['person', quote_plus(user)]) try: f = urllib2.urlopen(u) @@ -1001,14 +1067,14 @@ def delete_package(prj, pac): import othermethods u = makeurl(['source', prj, pac]) - othermethods.delfile(u, pac, conf.config['username'], conf.config['pass']) + othermethods.delfile(u, pac, conf.config['user'], conf.config['pass']) def delete_project(prj): import othermethods u = makeurl(['source', prj]) - othermethods.delfile(u, prj, conf.config['username'], conf.config['pass']) + othermethods.delfile(u, prj, conf.config['user'], conf.config['pass']) def get_platforms(): diff --git a/osc/fetch.py b/osc/fetch.py index db5ab4cb..fd7c0eae 100644 --- a/osc/fetch.py +++ b/osc/fetch.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. diff --git a/osc/othermethods.py b/osc/othermethods.py index daa0c44b..9759dc6c 100755 --- a/osc/othermethods.py +++ b/osc/othermethods.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2006 Peter Poeml. All rights reserved. +# Copyright (C) 2006 Peter Poeml / Novell Inc. All rights reserved. # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. @@ -16,100 +16,81 @@ import httplib import base64 import os import urlparse +from osc.core import __version__ BLOCKSIZE=4096 -def delfile(url, file, username, password): +def request(method, url, username, password, file=None, strbuf=None): + """call with method = (PUT|DELETE)""" - auth_string = base64.encodestring('%s:%s' % (username, password)).strip() - u = urlparse.urlparse(url) - host = u[1] - path = u[2] + if method == 'PUT': + if file == None and strbuf == None: + print >>sys.stderr, 'putting a file requires either a filename or a string buffer' + sys.exit(1) + if strbuf: + size = len(strbuf) + else: + size = os.path.getsize(file) - conn = httplib.HTTP(host) - conn.putrequest('DELETE', '%s' % path) + scheme, host, path, params, query, fragment = urlparse.urlparse(url) + if query: + path += '?' + query + + if scheme == 'https': + conn = httplib.HTTPS(host) + elif scheme == 'http': + conn = httplib.HTTP(host) + else: + sys.exit('unknown scheme %s' % scheme) + + # Headers + conn.putrequest(method, '%s' % path) conn.putheader('Host', host) + conn.putheader('User-agent', 'osc/%s' % __version__) + auth_string = base64.encodestring('%s:%s' % (username, password)).strip() conn.putheader('Authorization', 'Basic %s' % auth_string) + if method == 'PUT': + conn.putheader('Content-Type', 'text/plain') + conn.putheader('Content-Length', str(size)) conn.endheaders() + # Body + if method == 'PUT': + if strbuf: + conn.send(strbuf) + else: + fp = open(file, 'rb') + #n = 0 + while 1: + buf = fp.read(BLOCKSIZE) + #n+=1 + #if n % 10 == 0: + # print 'upload-sending blocknum=', n + # print '.', + + if not buf: break + + try: + conn.send(buf) + except: + sys.exit('ERROR uploading %s' % file) + fp.close() reply, msg, headers = conn.getreply() if reply != 200: - print 'error deleting %s' % file - print 'upload-DELETE reply=', reply, ' msg=', msg, 'headers=', headers + print >>sys.stderr, 'Error: can\'t %s \'%s\'' % (method, url) + print >>sys.stderr, 'reply:', reply + print >>sys.stderr, '\nDebugging output follows.\nurl:\n%s\nheaders:\n%s\nresponse:\n%s' % (url, headers, msg) + + + +def delfile(url, file, username, password): + return request('DELETE', url, username, password, file=file) def putfile(url, username, password, file=None, strbuf=None): - - if file == None and strbuf == None: - print >>sys.stderr, 'putfile requires either a filename or a string buffer' - sys.exit(1) - - if strbuf: - size = len(strbuf) - else: - size = os.stat(file)[6] - - auth_string = base64.encodestring('%s:%s' % (username, password)).strip() - - u = urlparse.urlparse(url) - host = u[1] - path = u[2] - - conn = httplib.HTTP(host) - conn.putrequest('PUT', '%s' % path) - conn.putheader('Host', host) - conn.putheader('Content-Type', 'text/plain') - conn.putheader('Content-Length', str(size)) - conn.putheader('Authorization', 'Basic %s' % auth_string) - conn.endheaders() - - if strbuf: - conn.send(strbuf) - else: - fp = open(file, 'rb') - n = 0 - while 1: - buf = fp.read(BLOCKSIZE) - n+=1 - if n % 10 == 0: - #print 'upload-sending blocknum=', n - #print '.', - pass - - if not buf: break - - try: - conn.send(buf) - except: - print - print 'ERROR uploading %s' % file - print - os._exit(1) - - fp.close() - - reply, msg, headers = conn.getreply() - - if reply != 200: - print 'error uploading %s' % file - print 'upload-PUT reply=', reply, ' msg=', msg, 'headers=', headers + return request('PUT', url, username, password, file=file, strbuf=strbuf) -def main(): - import sys - - username = 'yourusername' - password = 'yourpassword' - file = sys.argv[1] - url = 'http://api.opensuse.org/source/exim/exim/%s' % os.path.basename(file) - - putfile(url, file, username, password) - - delfile(url, file, username, password) - - -if __name__ == '__main__': - main() diff --git a/tests.py b/tests.py index df476ebd..755e897c 100755 --- a/tests.py +++ b/tests.py @@ -4,7 +4,6 @@ import os, sys, time import unittest import shutil -from osc.core import init_basicauth from osc import commandline PRJ = 'home:poeml' @@ -25,11 +24,11 @@ class TestOsc(unittest.TestCase): ##################################################################### - def testId(self): + def testUsermeta(self): expect = """ poeml poeml@suse.de - Dr. Peter Peoml + Dr. Peter Poeml @@ -50,12 +49,14 @@ class TestOsc(unittest.TestCase): + + """ - self.out, self.err = runosc('id poeml') + self.out, self.err = runosc('usermeta poeml') self.assertEqual(self.err, '') self.assertEqual(self.out, expect) @@ -91,7 +92,7 @@ class TestOsc(unittest.TestCase): def testMetaPac(self): self.out, self.err = runosc('meta Apache apache2') self.assertEqual(self.err, '') - self.assert_('' in self.out) + self.assert_('' in self.out) ##################################################################### @@ -251,8 +252,6 @@ def touch(filename): if __name__ == '__main__': - init_basicauth() - #unittest.main() oldpwd = os.getcwd() suite = unittest.makeSuite(TestOsc)