1
0
mirror of https://github.com/openSUSE/osc.git synced 2024-12-25 17:36:13 +01:00

All in one go.

0.121.jw02
 - made rresults an alias for results. python decorators are a strange concept...
 - asserting that ~/.oscrc remains mode 0600
 - no more plain text passwords in ~/.oscrc, we store now as bz2+base64
 - added verbosity control -v -q. To be used in guess_proj_pack()
 - added 'll' and 'ls -l' as shorthand to 'list -v'
 - started to change to explicit dual license GPLv2 or GPLv3 to conform to Novell policy.
 - added revision parameter to show_upstream_srcmd5(), so that it can be used in do_cat later.
 - allowed both integer and srcmd5 revisions in meta_get_filelist()
 - added 'lL', 'LL': allowed -e and -v together in do_list(). Was an internal error before.
 - added cat -e, to cat a file through a link.
   'cat -e -r 3' expands through the third revision of the _link.
 - added subcmd bco as alias for branch -c
 - added default project to branch subcommand. .oscrc:branch_project = OpenSUSE:Factory
 - added primitive experimental support for .oscrc:checkout_no_colon = 1
 - suggest using svn when .svn found.
 - alias submitpac submitrequest
 - osc bco now continues to checkout after branch target exists error.
 - added .oscrc:plaintext_passwd=1 for backwards compatibility
 - moved core.py:exclude_stuff to .oscrc:exclude_glob and expand it to catch *.orig etc.
 - allowed req as alias for request.
 - bugfix get_request_list: use 'or' with multiple states, not 'and'.
 - added osc req list -s all; a shorthand for enumerating all states
 - osc req list no longer confuses creator with approver.
 - osc req list -D nnn limit to requests nnn days old.
 - osc req list now also shows requests from the the given package, not only to.
 - improved help texts with repairlink to point to osc resolved.
 - improved passx code when creating oscrc.
 - osc sr -l is now a shortcut for 'osc req list -M -a -t submit -D 0'
This commit is contained in:
Jürgen Weigert 2009-08-20 19:28:05 +00:00
parent 090e79964c
commit 83b1b5ca59
6 changed files with 428 additions and 83 deletions

View File

@ -19,4 +19,5 @@ Susanne Oberhauser
Tom Patzig
Werner Fink
Will Stephenson
Juergen Weigert
Jan-Simon Möller

31
NEWS
View File

@ -1,3 +1,34 @@
0.121.jw02
- made rresults an alias for results. python decorators are a strange concept...
- asserting that ~/.oscrc remains mode 0600
- no more plain text passwords in ~/.oscrc, we store now as bz2+base64
- added verbosity control -v -q. To be used in guess_proj_pack()
- added 'll' and 'ls -l' as shorthand to 'list -v'
- started to change to explicit dual license GPLv2 or GPLv3 to conform to Novell policy.
- added revision parameter to show_upstream_srcmd5(), so that it can be used in do_cat later.
- allowed both integer and srcmd5 revisions in meta_get_filelist()
- added 'lL', 'LL': allowed -e and -v together in do_list(). Was an internal error before.
- added cat -e, to cat a file through a link.
'cat -e -r 3' expands through the third revision of the _link.
- added subcmd bco as alias for branch -c
- added default project to branch subcommand. .oscrc:branch_project = OpenSUSE:Factory
- added primitive experimental support for .oscrc:checkout_no_colon = 1
- suggest using svn when .svn found.
- alias submitpac submitrequest
- osc bco now continues to checkout after branch target exists error.
- added .oscrc:plaintext_passwd=1 for backwards compatibility
- moved core.py:exclude_stuff to .oscrc:exclude_glob and expand it to catch *.orig etc.
- allowed req as alias for request.
- bugfix get_request_list: use 'or' with multiple states, not 'and'.
- added osc req list -s all; a shorthand for enumerating all states
- osc req list no longer confuses creator with approver.
- osc req list -D nnn limit to requests nnn days old.
- osc req list now also shows requests from the the given package, not only to.
- improved help texts with repairlink to point to osc resolved.
- improved passx code when creating oscrc.
- osc sr -l is now a shortcut for 'osc req list -M -a -t submit -D 0'
0.121:
- osc metafromspec allows editing before send
- allow handling of other roles than "maintainer" with maintainer command

58
TODO
View File

@ -1,3 +1,34 @@
CRITICAL:
- there is no way to list my old requests, regardless of their state,
the online help is misleading about comma seperated lists.
osc request list -s revoked,revoked openSUSE:Tools -> lists 37
osc request list -s accecpted openSUSE:Tools -> lists 113
osc request list -s accecpted,revoked openSUSE:Tools -> lists 0
- hitting the wrong build service is a legal risk.
osc has no guard against forgetting -A. Add automatic pickup from current path.
- webpage can create a _link in a fully populated package.
Need to prevent his somehow.
- cat -E <srcmd5> -r 3 <file> should expand through the third revision of the link to a
named md5sum revision of the file. Or is this nonsense???
- canonical option parser.
-A, -e, -u, -E <n>, should be univeral to all subconmmands that work on prj/pkg objects.
With all subcommands that work on prj/pkg, the following should all be synonyms:
-A apiurl prj pkg
-A apiurl --project prj --package=pkg
-A apiurl prj/pkg
-A apiurl prj:pkg
apiurl/source/prj/pkg
The current working directory or its descendants should provide defaults
for apiurl, prj and/or pkg.
- checkout working directories should be real directories. No more colons.
A prj / package nameing collision shall be resolved by renaming the project to
have a .prj suffix
MAJOR:
@ -21,11 +52,6 @@ NORMAL:
and put it into the spec file... otherwise clients won't regard the copied package as newer
- commit: check if errors during PUT are handled sensibly, so the change is
not committed to localmeta
- store password base64 hashed, so it is not directly visible in plaintext
when opening .oscrc and someone is looking over the shoulder
- use http://code.google.com/p/iniparse/ instead of ConfigParser, with
write capability, for the change
- or implement Juergen Weigert's idea of a on-demand password agent
- add switch to commit to change repository options, like to e.g. disable publishing?
- implement optional logging to .osc/log, which could be useful for debugging bugs like
the one where api.opensuse.org sends empty replies (a hard-to-catch one)
@ -47,13 +73,9 @@ MINOR:
this can peruse the new get_binarylist() and get_binary_file() functions
- adjust zsh completion to work with cmdln.py implementation
- add support for adding tags to packages?
- evaluate password storage alternatives
- use http://code.google.com/p/iniparse/ instead of ConfigParser, with
write capability, for the change
@ -67,3 +89,15 @@ MINOR:
15:20 < DuDE> mt: das ist gut, dann sollte osc kein "try again" anbieten, sondern die Meldung rueberbringen
JW:
FIXME: osc co overwrites local changes without warning.
FIXME: when branching, the user should be added to bugowner, for the branch project.
FIXME: 'osc req' shall default to 'osc req list -M -B -s all',
where -B shows requests related to packages where I am the bugowner.
FIXME: 'osc log openSUSE:Factory PKG' should also point to the bsdevelproject
osc addrepo - obsolete zypper ar
osc install - obsolete zypper in
-

View File

@ -3,7 +3,7 @@
# Copyright (C) 2006 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.
# either version 2, or version 3 (at your option).
from core import *
@ -58,6 +58,11 @@ class Osc(cmdln.Cmdln):
name = 'osc'
conf = None
## FIXME: not sure, if it is possible in python to
## fetch conf.config['request_list_days'] from
## oscrc instead of an ugly pointer string hre.
request_list_days = 'oscrc:request_list_days'
man_header = MAN_HEADER
man_footer = MAN_FOOTER
@ -90,6 +95,10 @@ class Osc(cmdln.Cmdln):
help='specify alternate configuration file')
optparser.add_option('--no-gnome-keyring', action='store_true',
help='disable usage of GNOME Keyring')
optparser.add_option('-v', '--verbose', dest='verbose', action='count', default=0,
help='increase verbosity')
optparser.add_option('-q', '--quiet', dest='verbose', action='store_const', const=-1,
help='be quiet, not verbose')
return optparser
@ -102,7 +111,8 @@ class Osc(cmdln.Cmdln):
override_http_debug = self.options.http_debug,
override_traceback = self.options.traceback,
override_post_mortem = self.options.post_mortem,
override_no_gnome_keyring = self.options.no_gnome_keyring)
override_no_gnome_keyring = self.options.no_gnome_keyring,
override_verbose = self.options.verbose)
except oscerr.NoConfigfile, e:
print >>sys.stderr, e.msg
print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
@ -125,6 +135,11 @@ class Osc(cmdln.Cmdln):
if try_again: self.postoptparse(try_again = False)
self.conf = conf
self.options.verbose = conf.config['verbose']
conf.exclude_glob = conf.config['exclude_glob'].split()
#if conf.config['version']:
# print "osc-%s" % __version__
# exit
def get_cmd_help(self, cmdname):
@ -166,6 +181,9 @@ class Osc(cmdln.Cmdln):
@cmdln.alias('ls')
@cmdln.alias('ll')
@cmdln.alias('lL')
@cmdln.alias('LL')
@cmdln.option('-a', '--arch', metavar='ARCH',
help='specify architecture')
@cmdln.option('-R', '--revision', metavar='REVISION',
@ -176,6 +194,8 @@ class Osc(cmdln.Cmdln):
help='list built binaries, instead of sources')
@cmdln.option('-v', '--verbose', action='store_true',
help='print extra information')
@cmdln.option('-l', '--long', action='store_true', dest='verbose',
help='print extra information')
@cmdln.option('-e', '--expand', action='store_true',
help='expand linked package')
def do_list(self, subcmd, opts, *args):
@ -192,6 +212,9 @@ class Osc(cmdln.Cmdln):
ls Apache apache2 # list source files of package of a project
ls Apache apache2 <file> # list <file> if this file exists
ls -v Apache apache2 # verbosely list source files of package
ls -l Apache apache2 # verbosely list source files of package
ll Apache apache2 # verbosely list source files of package
LL Apache apache2 # verbosely list source files of expanded link
With --verbose, the following fields will be shown for each item:
MD5 hash of file
@ -204,6 +227,8 @@ class Osc(cmdln.Cmdln):
"""
args = slash_split(args)
if (subcmd == 'll'): opts.verbose = True;
if (subcmd == 'lL' or subcmd == 'LL'): opts.verbose = True; opts.expand = True;
if len(args) == 1:
project = args[0]
@ -246,7 +271,8 @@ class Osc(cmdln.Cmdln):
elif len(args) == 1:
if opts.verbose:
raise oscerr.WrongOptions('Sorry, the --verbose option is not implemented for projects.')
if self.options.verbose:
print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
if opts.expand:
raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
@ -260,7 +286,7 @@ class Osc(cmdln.Cmdln):
expand=opts.expand,
revision=opts.revision)
if opts.verbose:
out = [ '%s %7d %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
for i in l if not fname or fname == i.name ]
if len(out) == 0:
if fname:
@ -475,8 +501,11 @@ class Osc(cmdln.Cmdln):
'(primary project where a package is developed)')
@cmdln.option('-d', '--diff', action='store_true',
help='show diff only instead of creating the actual request')
@cmdln.option('-l', '--list', action='store_true',
help='show submitrequests. Same as \'osc req list -M -s all -t submit -D 0\'')
@cmdln.alias("sr")
@cmdln.alias("submitreq")
@cmdln.alias("submitpac")
def do_submitrequest(self, subcmd, opts, *args):
"""${cmd_name}: Create request to submit source into another Project
@ -492,16 +521,26 @@ class Osc(cmdln.Cmdln):
${cmd_option_list}
"""
if opts.list:
opts.state = "all"
opts.user = ""
opts.all = True
opts.type = "submit"
opts.mine = True
return self.do_request('list', opts, *args)
args = slash_split(args)
# remove this block later again
oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
if args and args[0] in oldcmds:
print "****************************************************************"
print "* WARNING: It looks that you are using this command with a *"
print "* deprecated syntax (maybe) ! *"
print "* Please run \"osc sr --help\" to see the new syntax. *"
print "****************************************************************"
print "***********************************************************************"
print "* WARNING: It looks that you are using this command with a *"
print "* deprecated syntax (maybe) ! *"
print "* Please run \"osc sr --help\" and \"osc req --help\" *"
print "* to see the new syntax. *"
print "* E.g. \"osc sr -l\" is shortcut for \"osc req list -M -s all -t submit\" *"
print "***********************************************************************"
if args[0] == 'create':
args.pop(0)
else:
@ -671,7 +710,6 @@ Please submit there instead, or use --nodevelproject to force direct submission.
print result
@cmdln.option('-d', '--diff', action='store_true',
help='generate a diff')
@cmdln.option('-u', '--unified', action='store_true',
@ -681,16 +719,19 @@ Please submit there instead, or use --nodevelproject to force direct submission.
@cmdln.option('-t', '--type', metavar='TEXT',
help='limit to requests which contain a given action type (submit/delete/change_devel)')
@cmdln.option('-a', '--all', action='store_true',
help='all states')
@cmdln.option('-s', '--state', default='new',
help='only list requests in one of the comma separated given states (new/accepted/rejected/revoked/declined) [default=new]')
help='all states. Same as\'-s all\'')
@cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
help='only list requests in one of the comma separated given states (new/accepted/rejected/revoked/declined) or "all" [default=new, or all, if no args given]')
@cmdln.option('-D', '--days', default='%s' % request_list_days, metavar='DAYS',
help='only list requests created or changed in the last DAYS. [default=%s]' % request_list_days)
@cmdln.option('-U', '--user', metavar='USER',
help='same as -M, but for the specified USER')
@cmdln.option('-b', '--brief', action='store_true', default=False,
help='print output in list view as list subcommand')
@cmdln.option('-M', '--mine', action='store_true',
help='only show requests created by yourself')
@cmdln.alias("rq")
# may support it later, but lets fail for people used the api call before
# @cmdln.alias("req")
@cmdln.alias("req")
def do_request(self, subcmd, opts, *args):
"""${cmd_name}: Show and modify requests
@ -705,7 +746,9 @@ Please submit there instead, or use --nodevelproject to force direct submission.
This command has the following sub commands:
"list" lists open requests attached to a project or package.
"list" lists open requests attached to a project or package or person.
Uses the project/package of the current directory if none of
-M, -U USER, project/package are given.
"log" will show the history of the given ID
@ -726,7 +769,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
usage:
osc request list [-M] [-s state] [-t type] [PRJ [PKG]]
osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [PRJ [PKG]]
osc request log ID
osc request show [-d] [-b] ID
osc request accept [-m TEXT] ID
@ -738,8 +781,20 @@ Please submit there instead, or use --nodevelproject to force direct submission.
args = slash_split(args)
# 'req' defaults to 'req list -M -s all'
if not args:
args = [ 'list' ]
opts.mine = 1
if opts.state == '':
opts.state = 'all'
if opts.state == '':
opts.state = 'new'
cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke']
if not args or args[0] not in cmds:
if subcmd == 'req':
print >>sys.stderr, 'You may want to try "osc api" instead of "osc req".'
raise oscerr.WrongArgs('Unknown request action. Choose one of %s.' \
% ', '.join(cmds))
@ -780,20 +835,50 @@ Please submit there instead, or use --nodevelproject to force direct submission.
# list
if cmd == 'list':
states = ('new', 'accepted', 'rejected', 'revoked', 'declined')
state_list = opts.state.split(',')
if opts.state == 'all':
state_list = []
for s in state_list:
if not s in states:
print >>sys.stderr, "Unknown state %s, try one of %s" % (s, ','.join(states))
who = ''
if opts.mine:
who = conf.get_apiurl_usr(apiurl)
if opts.user:
who = opts.user
if opts.all:
state_list = ('new', 'accepted', 'declined', 'revoked', 'rejected')
state_list = []
results = get_request_list(apiurl,
project, package, who, state_list, opts.type)
results.sort(reverse=True)
import time
try:
## ugly hack needed, as as the -D decorator constructs its default before osrcrc is loaded.
if opts.days == self.request_list_days:
opts.days = conf.config['request_list_days']
int(opts.days)
except:
opts.days = '0'
if int(opts.days) > 0:
since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-int(opts.days)*24*3600))
skipped = 0
## bs has received 2009-09-20 a new xquery compare() function
## which allows us to limit the list inside of get_request_list
## That would be much faster for coolo. But counting the remainder
## is not possible with current xquery implementation.
for result in results:
print result.list_view()
if int(opts.days) == 0 or result.state.when > since:
print result.list_view()
else:
skipped += 1
if skipped:
print "There are %d requests older than %s days.\n" % (skipped,opts.days)
elif cmd == 'log':
for l in get_request_log(conf.config['apiurl'], reqid):
@ -1031,7 +1116,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
@cmdln.option('-d', '--keep-develproject', action='store_true',
help='keep develproject tag in the package metadata')
@cmdln.option('-r', '--revision', metavar='rev',
help='link the specified revision.')
help='link the specified revision.')
@cmdln.option('-t', '--to-apiurl', metavar='URL',
help='URL of destination api server. Default is the source api server.')
@cmdln.option('-m', '--message', metavar='TEXT',
@ -1090,7 +1175,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if opts.message:
comment = opts.message
else:
if not rev:
if not rev:
rev = show_upstream_rev(src_apiurl, src_project, src_package);
comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
@ -1105,11 +1190,16 @@ Please submit there instead, or use --nodevelproject to force direct submission.
print r
@cmdln.alias('branch_co')
@cmdln.alias('branchco')
@cmdln.alias('bco')
@cmdln.alias('getpac')
@cmdln.option('--nodevelproject', action='store_true',
help='do not follow a defined devel project ' \
'(primary project where a package is developed)')
@cmdln.option('-c', '--checkout', action='store_true',
help='Checkout branched package afterwards ' )
help='Checkout branched package afterwards ' \
'(\'osc bco\' is a shorthand for this option)' )
@cmdln.option('-r', '--revision', metavar='rev',
help='branch against a specific revision')
def do_branch(self, subcmd, opts, *args):
@ -1125,15 +1215,31 @@ Please submit there instead, or use --nodevelproject to force direct submission.
home:USERNAME:branches:PROJECT/PACKAGE
if nothing else specified.
The branched package will come from
openSUSE:Factory
if nothing else specified.
usage:
osc branch SOURCEPACKAGE
osc branch SOURCEPROJECT SOURCEPACKAGE
osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
osc bco ...
${cmd_option_list}
"""
# FIXME: how can we interpolate conf.config['branch_project'] in the above message?
if (subcmd == 'branch_co' or subcmd == 'branchco' or subcmd == 'bco'): opts.checkout = True
args = slash_split(args)
tproject = tpackage = None
if (len(args) == 1):
print >>sys.stderr, "defaulting to %s/%s" % (conf.config['branch_project'],args[0])
# python has no args.unshift ???
args = [ conf.config['branch_project'] , args[0] ]
if not (len(args) >= 2 and len(args) <= 4):
raise oscerr.WrongArgs('Wrong number of arguments.')
if len(args) >= 3:
@ -1142,9 +1248,16 @@ Please submit there instead, or use --nodevelproject to force direct submission.
tpackage = args[3]
r = branch_pkg(conf.config['apiurl'], args[0], args[1],
nodevelproject=opts.nodevelproject, rev=opts.revision, target_project=tproject, target_package=tpackage)
nodevelproject=opts.nodevelproject, rev=opts.revision,
target_project=tproject, target_package=tpackage,
return_existing=opts.checkout)
expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
if r[0] is None:
r = expected = r[1]
print >>sys.stderr, 'Using existing branch project:', r, '\n'
if r != expected:
devloc = r
if 'branches:' in r:
@ -1163,6 +1276,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if opts.checkout:
checkout_package(conf.config['apiurl'], r, package,
expand_link=True, prj_dir=r)
if conf.config['verbose']:
print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
else:
apiopt = ''
if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
@ -1175,7 +1290,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
@cmdln.option('-f', '--force', action='store_true',
help='deletes a project and its packages')
help='deletes a package or an empty project')
def do_rdelete(self, subcmd, opts, *args):
"""${cmd_name}: Delete a project or packages on the server.
@ -1341,6 +1456,19 @@ Please submit there instead, or use --nodevelproject to force direct submission.
print rdiff
def do_install(self, subcmd, opts, *args):
"""${cmd_name}: install a package after build via zypper in -r
Not implemented yet. Use osc repourls,
select the url you best like (standard),
chop off after the last /, this should work with zypper.
${cmd_usage}
${cmd_option_list}
"""
print self.do_install.__doc__
def do_repourls(self, subcmd, opts, *args):
"""${cmd_name}: Shows URLs of .repo files
@ -1621,14 +1749,17 @@ Please submit there instead, or use --nodevelproject to force direct submission.
p.todo = p.filenamelist + p.filenamelist_unvers
for filename in p.todo:
if os.path.isdir(filename):
continue
# ignore foo.rXX, foo.mine for files which are in 'C' state
if os.path.splitext(filename)[0] in p.in_conflict:
continue
continue
state = p.status(filename)
if state == '?':
# TODO: should ignore typical backup files suffix ~ or .orig
p.addfile(filename)
print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
elif state == '!':
@ -1962,8 +2093,17 @@ Please submit there instead, or use --nodevelproject to force direct submission.
sys.exit(1)
@cmdln.hide(1)
@cmdln.option('-l', '--last-build', action='store_true',
help='show last build results (succeeded/failed/unknown)')
@cmdln.option('-r', '--repo', action='append', default = [],
help='Show results only for specified repo(s)')
@cmdln.option('-a', '--arch', action='append', default = [],
help='Show results only for specified architecture(s)')
@cmdln.option('', '--xml', action='store_true',
help='generate output in XML (former results_meta)')
def do_rresults(self, subcmd, opts, *args):
print "Command rresults is obsolete. Please use 'osc results'"
print "Command rresults is obsolete. Running 'osc results' instead"
self.do_results('results', opts, *args)
sys.exit(1)
@ -2004,10 +2144,10 @@ Please submit there instead, or use --nodevelproject to force direct submission.
else:
raise e
@cmdln.hide(1)
def do_req(self, subcmd, opts, *args):
print "Command req is obsolete. Please use 'osc api'"
sys.exit(1)
# @cmdln.hide(1)
# def do_req(self, subcmd, opts, *args):
# print "Command req is obsolete. Please use 'osc api'"
# sys.exit(1)
@cmdln.alias('r')
@cmdln.option('-l', '--last-build', action='store_true',
@ -2029,6 +2169,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
"""
args = slash_split(args)
apiurl = conf.config['apiurl']
if len(args) == 0:
wd = os.curdir
@ -2786,6 +2927,9 @@ Please submit there instead, or use --nodevelproject to force direct submission.
usage:
osc search \'search term\' <options>
${cmd_option_list}
osc search does not find binary rpm names. Use
http://software.opensuse.org/search?q=binaryname
"""
search_term = None
@ -3144,6 +3288,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
@cmdln.option('-r', '--revision', metavar='rev',
help='print out the specified revision')
@cmdln.option('-e', '--expand', action='store_true',
help='expand linked package')
def do_cat(self, subcmd, opts, *args):
"""${cmd_name}: Output the content of a file to standard output
@ -3160,9 +3306,11 @@ Please submit there instead, or use --nodevelproject to force direct submission.
raise oscerr.WrongArgs('Wrong number of arguments.')
rev, dummy = parseRevisionOption(opts.revision)
query = ''
query = { }
if opts.revision:
query = 'rev=%s' % opts.revision
query['rev'] = opts.revision
if opts.expand:
query['rev'] = show_upstream_srcmd5(conf.config['apiurl'], args[0], args[1], expand=True, revision=opts.revision)
u = makeurl(conf.config['apiurl'], ['source', args[0], args[1], args[2]], query=query)
for data in streamfile(u):
sys.stdout.write(data)
@ -3186,7 +3334,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
This command checks out a package with merged source changes. It uses
a 3-way merge to resolve file conflicts. After reviewing/repairing
the merge, 'osc ci' will re-create a working source link.
the merge, use 'osc resolved ...' and 'osc ci' to re-create a
working source link.
usage:
* For merging conflicting changes of a checkout package:
@ -3379,7 +3528,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
pac.write_conflictlist()
print
print 'Please change into the \'%s\' directory,' % destdir
print 'fix the conflicts, and commit the changes.'
print 'fix the conflicts (files marked with \'C\' above),'
print 'run \'osc resolved ...\', and commit the changes.'
@cmdln.option('-m', '--message',
help='add MESSAGE to changes (not open an editor)')

View File

@ -1,12 +1,17 @@
# Copyright (C) 2006 Novell Inc. All rights reserved.
# Copyright (C) 2006-2009 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.
# either version 2, or version 3 (at your option).
"""Read osc configuration and store it in a dictionary
This module reads and parses ~/.oscrc. The resulting configuration is stored
for later usage in a dictionary named 'config'.
The .oscrc is kept mode 0600, so that it is not publically readable.
This gives no real security for storing passwords.
If in doubt, use your favourite keyring.
Password is stored on ~/.oscrc as bz2 compressed and base64 encoded, so that is fairly
large and not to be recognized or remembered easily by an occasional spectator.
If information is missing, it asks the user questions.
@ -28,7 +33,6 @@ The configuration dictionary could look like this:
}
"""
import OscConfigParser
from osc import oscerr
@ -47,6 +51,7 @@ config = { }
DEFAULTS = { 'apiurl': 'https://api.opensuse.org',
'user': 'your_username',
'pass': 'your_password',
'passx': '',
'packagecachedir': '/var/tmp/osbuild-packagecache',
'su-wrapper': 'su -c',
@ -70,6 +75,20 @@ DEFAULTS = { 'apiurl': 'https://api.opensuse.org',
'extra-pkgs': 'vim gdb strace',
# default platform
'build_platform': 'openSUSE_Factory',
# default project for branch or bco
'branch_project': 'openSUSE:Factory',
# alternate filesystem layout: have multiple subdirs, where colons were.
'checkout_no_colon': 0,
# local files to ignore with status, addremove, ....
'exclude_glob': '.osc CVS .svn .* _linkerror *~ #*# *.orig *.bak',
# keep passwords in plaintext. If you see this comment, your osc
# already uses the encrypted password, and only keeps them in plain text
# for backwards compatibility. Default will change to 0 in future releases.
'plaintext_passwd': 1,
# limit the age of requests shown with 'osc req list'.
# this is a default only, can be overridden by 'osc req list -D NNN'
# Use 0 for unlimted.
'request_list_days': 30,
# check for unversioned/removed files before commit
'check_filelist': '1',
}
@ -101,20 +120,41 @@ apiurl = %(apiurl)s
#extra-pkgs = vim gdb strace
# build platform is used if the platform argument is omitted to osc build
#build_platform = openSUSE_Factory
#build_platform = %(build_platform)s
# show info useful for debugging
# default project for branch or bco
#branch_project = %(branch_project)s
# alternate filesystem layout: have multiple subdirs, where colons were.
#checkout_no_colon = %(checkout_no_colon)s
# local files to ignore with status, addremove, ....
#exclude_glob = %(exclude_glob)s
# keep passwords in plaintext. If you see this comment, your osc
# already uses the encrypted password, and only keeps them in plain text
# for backwards compatibility. Default will change to 0 in future releases.
# You can remove the plaintext password without harm, if you do not need
# backwards compatibility.
#plaintext_passwd = %(plaintext_passwd)s
# limit the age of requests shown with 'osc req list'.
# this is a default only, can be overridden by 'osc req list -D NNN'
# Use 0 for unlimted.
#request_list_days = %(request_list_days)s
# show info useful for debugging
#debug = 1
# show HTTP traffic useful for debugging
# show HTTP traffic useful for debugging
#http_debug = 1
# jump into the debugger in case of errors
#post_mortem = 1
# print call traces in case of errors
#traceback = 1
# use GNOME keyring for credentials if available
#gnome_keyring = 0
@ -124,10 +164,11 @@ apiurl = %(apiurl)s
[%(apiurl)s]
user = %(user)s
pass = %(pass)s
passx = %(passx)s
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used
# email =
# email =
# additional headers to pass to a request, e.g. for special authentication
#http_headers = Host: foofoobar,
# User: mumblegack
@ -281,6 +322,7 @@ def write_initial_config(conffile, entries, custom_template = ''):
conf_template = custom_template or new_conf_template
config = DEFAULTS.copy()
config.update(entries)
config['passx'] = config['pass'].encode('bz2').encode('base64')
if config['gnome_keyring'] and GNOME_KEYRING:
protocol, host = \
parse_apisrv_url(None, config['apiurl'])
@ -291,6 +333,9 @@ def write_initial_config(conffile, entries, custom_template = ''):
server = host)
config['user'] = ''
config['pass'] = ''
config['passx'] = ''
if not config['plaintext_passwd']:
config['pass'] = ''
sio = StringIO.StringIO(conf_template.strip() % config)
cp = OscConfigParser.OscConfigParser(DEFAULTS)
cp.readfp(sio)
@ -331,7 +376,9 @@ def add_section(filename, url, user, passwd):
cp.set(url, 'keyring', '1')
else:
cp.set(url, 'user', user)
cp.set(url, 'pass', passwd)
if not config['plaintext_passwd']:
cp.remove_option(url, 'pass')
cp.set(url, 'passx', passwd.encode('bz2').encode('base64'))
file = open(filename, 'w')
cp.write(file, True)
if file: file.close()
@ -343,7 +390,8 @@ def get_config(override_conffile = None,
override_http_debug = None,
override_traceback = None,
override_post_mortem = None,
override_no_gnome_keyring = None):
override_no_gnome_keyring = None,
override_verbose = None):
"""do the actual work (see module documentation)"""
import os
import sys
@ -359,6 +407,9 @@ def get_config(override_conffile = None,
# okay, we made sure that .oscrc exists
# make sure it is not world readable, it may contain a password.
os.chmod(conffile, 0600)
cp = get_configParser(conffile)
if not cp.has_section('general'):
@ -405,7 +456,7 @@ def get_config(override_conffile = None,
# override values which we were called with
# This needs to be done before processing API sections as it might be already used there
if override_no_gnome_keyring:
if override_no_gnome_keyring:
config['gnome_keyring'] = False
aliases = {}
@ -432,6 +483,17 @@ def get_config(override_conffile = None,
#from the general section.
user = cp.get(url, 'user', raw=True) # need to set raw to prevent '%' expansion
password = cp.get(url, 'pass', raw=True) # especially on password!
passwordx = cp.get(url, 'passx', raw=True) # especially on password!
if password is None or password == 'your_password':
try:
password = passwordx.decode('base64').decode('bz2');
except:
print "%s: no credentials known" % url
password = 'your_password'
else:
if not passwordx:
print "%s: rewriting from plain pass to encoded pass\n" % url
add_section(conffile, url, user, password)
if cp.has_option(url, 'keyring') and cp.get(url, 'keyring'):
# This APIURL was configured to use keyring by
continue
@ -464,7 +526,11 @@ def get_config(override_conffile = None,
config['api_host_options'] = api_host_options
# override values which we were called with
if override_debug:
if override_verbose:
config['verbose'] = override_verbose + 1
if not config.has_key('verbose') : config['verbose'] = 1;
if override_debug:
config['debug'] = override_debug
if override_http_debug:
config['http_debug'] = override_http_debug
@ -478,7 +544,7 @@ def get_config(override_conffile = None,
parse_apisrv_url(None, apiurl)
config['apiurl'] = apiurl
# XXX unless config['user'] goes away (and is replaced with a handy function, or
# XXX unless config['user'] goes away (and is replaced with a handy function, or
# config becomes an object, even better), set the global 'user' here as well,
# provided that there _are_ credentials for the chosen apiurl:
try:

View File

@ -3,9 +3,9 @@
# Copyright (C) 2006 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.
# either version 2, or version 3 (at your option).
__version__ = '0.120.90'
__version__ = '0.121.jw02'
# __store_version__ is to be incremented when the format of the working copy
# "store" changes in an incompatible way. Please add any needed migration
# functionality to check_store_version().
@ -15,7 +15,7 @@ import os
import os.path
import sys
import urllib2
from urllib import pathname2url, quote_plus, urlencode
from urllib import pathname2url, quote_plus, urlencode, unquote
from urlparse import urlsplit, urlunsplit
from cStringIO import StringIO
import shutil
@ -33,8 +33,6 @@ except ImportError:
BUFSIZE = 1024*1024
store = '.osc'
exclude_stuff = [store, 'CVS', '*~', '#*#', '.*', '_linkerror']
new_project_templ = """\
<project name="%(name)s">
@ -286,7 +284,7 @@ class Project:
self.pac_root = self.read_packages().getroot()
self.pacs_have = [ pac.get('name') for pac in self.pac_root.findall('package') ]
self.pacs_excluded = [ i for i in os.listdir(self.dir)
for j in exclude_stuff
for j in conf.exclude_glob
if fnmatch.fnmatch(i, j) ]
self.pacs_unvers = [ i for i in os.listdir(self.dir) if i not in self.pacs_have and i not in self.pacs_excluded ]
# store all broken packages (e.g. packages which where removed by a non-osc cmd)
@ -365,7 +363,7 @@ class Project:
def addPackage(self, pac):
import fnmatch
for i in exclude_stuff:
for i in conf.exclude_glob:
if fnmatch.fnmatch(pac, i):
return False
state = self.get_state(pac)
@ -957,7 +955,7 @@ class Package:
# gather unversioned files, but ignore some stuff
self.excluded = [ i for i in os.listdir(self.dir)
for j in exclude_stuff
for j in conf.exclude_glob
if fnmatch.fnmatch(i, j) ]
self.filenamelist_unvers = [ i for i in os.listdir(self.dir)
if i not in self.excluded
@ -1431,7 +1429,7 @@ class Request:
)
def list_view(self):
ret = '%6d State:%-7s Creator:%-12s When:%-12s' % (self.reqid, self.state.name, self.state.who, self.state.when)
ret = '%6d State:%-7s By:%-12s When:%-12s' % (self.reqid, self.state.name, self.state.who, self.state.when)
for a in self.actions:
dst = "%s/%s" % (a.dst_project, a.dst_package)
@ -1448,8 +1446,14 @@ class Request:
ret += '\n %s: %-50s %-20s ' % \
(a.type, sr_source, dst)
if self.statehistory and self.statehistory[0]:
who = []
for h in self.statehistory:
who.append("%s(%s)" % (h.who,h.name))
who.reverse()
ret += "\n From: %s" % (' -> '.join(who))
if self.descr:
ret += "\n Comment: %s" % (repr(self.descr))
ret += "\n Descr: %s" % (repr(self.descr))
ret += "\n"
return ret
@ -1733,10 +1737,14 @@ def http_DELETE(*args, **kwargs): return http_request('DELETE', *args, **kwargs)
def init_project_dir(apiurl, dir, project):
if not os.path.exists(dir):
os.mkdir(dir)
if conf.config['checkout_no_colon']:
os.makedirs(dir) # helpful with checkout_no_colon
else:
os.mkdir(dir)
if not os.path.exists(os.path.join(dir, store)):
os.mkdir(os.path.join(dir, store))
# print 'project=',project,' dir=',dir
store_write_project(dir, project)
store_write_apiurl(dir, apiurl)
if conf.config['do_package_tracking']:
@ -1780,6 +1788,8 @@ def check_store_version(dir):
if v == '':
msg = 'Error: "%s" is not an osc working copy.' % os.path.abspath(dir)
if os.path.exists(os.path.join(dir, '.svn')):
msg = msg + '\nTry svn instead of osc.'
raise oscerr.NoWorkingCopy(msg)
if v != __store_version__:
@ -1824,7 +1834,8 @@ def meta_get_filelist(apiurl, prj, package, verbose=False, expand=False, revisio
else:
l = []
rev = int(root.get('rev'))
# rev = int(root.get('rev')) # don't force int. also allow srcmd5 here.
rev = root.get('rev')
for node in root.findall('entry'):
f = File(node.get('name'),
node.get('md5'),
@ -2069,8 +2080,8 @@ def show_files_meta(apiurl, prj, pac, revision=None, expand=False, linkrev=None,
return f.readlines()
def show_upstream_srcmd5(apiurl, prj, pac, expand=False):
m = show_files_meta(apiurl, prj, pac, expand=expand)
def show_upstream_srcmd5(apiurl, prj, pac, expand=False, revision=None):
m = show_files_meta(apiurl, prj, pac, expand=expand, revision=revision)
return ET.parse(StringIO(''.join(m))).getroot().get('srcmd5')
@ -2323,10 +2334,12 @@ def get_request_list(apiurl, project, package, req_who='', req_state=('new',), r
m = match
if project:
if len(m): m += '%20and%20'
m += '%s/target/@project=\'%s\'' % (what, quote_plus(project))
m += '(%s/target/@project=\'%s\'%%20or%%20' % (what, quote_plus(project))
m += '%s/source/@project=\'%s\')' % (what, quote_plus(project))
if package:
if len(m): m += '%20and%20'
m += '%s/target/@package=\'%s\'' % (what, quote_plus(package))
m += '(%s/target/@package=\'%s\'%%20or%%20' % (what, quote_plus(package))
m += '%s/source/@package=\'%s\')' % (what, quote_plus(package))
if req_type:
if len(m): m += '%20and%20'
m += '%s/@type=\'%s\'' % (what, quote_plus(req_type))
@ -2336,6 +2349,8 @@ def get_request_list(apiurl, project, package, req_who='', req_state=('new',), r
matches.append(match)
for match in matches:
if conf.config['verbose'] > 1:
print "[",match,"]"
u = makeurl(apiurl, ['search', 'request'], ['match=%s' % match])
f = http_GET(u)
collection = ET.parse(f).getroot()
@ -2696,10 +2711,31 @@ def make_dir(apiurl, project, package, pathname=None, prj_dir=None):
The optional 'prj_dir' parameter specifies the path to the project dir (default: 'project').
"""
prj_dir = prj_dir or project
# FIXME: carefully test each patch component of prj_dir,
# if we have a .osc/_files entry at that level.
# -> if so, we have a package/project clash,
# and should rename this path component by appending '.proj'
# and give user a warning message, to discourage such clashes
pathname = pathname or getTransActPath(os.path.join(prj_dir, package))
if not os.path.exists(prj_dir):
if os.path.exists(os.path.join(prj_dir,store,'_files')):
# we want this to become a project directory,
# but it already is a package directory.
e.osc_msg = 'checkout_package: package/project clash. Moving myself away not implemented'
raise
if not os.path.exists(os.path.join(prj_dir,store,'_packages')):
# this directory could exist as a parent direory for one of our earlier
# checked out sub-projects. in this case, we still need to initialize it.
print statfrmt('A', prj_dir)
init_project_dir(apiurl, prj_dir, project)
if os.path.exists(os.path.join(prj_dir, package, store, '_packages')):
# the thing exists, but is a project directory and not a package directory
# FIXME: this should be a warning message to discourage package/project clashes
e.osc_msg = 'checkout_package: package/project clash. Moving project away not implemented'
raise
if not os.path.exists(os.path.join(prj_dir, package)):
print statfrmt('A', pathname)
os.mkdir(os.path.join(prj_dir, package))
@ -2724,6 +2760,9 @@ def checkout_package(apiurl, project, package,
else:
if sys.platform[:3] == 'win':
prj_dir = prj_dir[:2] + prj_dir[2:].replace(':', ';')
else:
if conf.config['checkout_no_colon']:
prj_dir = prj_dir.replace(':', '/')
if not pathname:
pathname = getTransActPath(os.path.join(prj_dir, package))
@ -2873,7 +2912,7 @@ def aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map =
http_PUT(u, data=aggregate_template)
print 'Done.'
def branch_pkg(apiurl, src_project, src_package, nodevelproject=False, rev=None, target_project=None, target_package=None):
def branch_pkg(apiurl, src_project, src_package, nodevelproject=False, rev=None, target_project=None, target_package=None, return_existing=False):
"""
Branch a package (via API call)
"""
@ -2887,7 +2926,20 @@ def branch_pkg(apiurl, src_project, src_package, nodevelproject=False, rev=None,
if target_package:
query['target_package'] = target_package
u = makeurl(apiurl, ['source', src_project, src_package], query=query)
f = http_POST(u)
try:
f = http_POST(u)
except urllib2.HTTPError, e:
if not return_existing:
raise
msg = ''.join(e.readlines())
msg = msg.split('<summary>')[1]
msg = msg.split('</summary>')[0]
m = re.match(r"branch target package already exists: (\S+)/", msg)
if not m:
e.msg += '\n' + msg
raise
return (None, m.group(1))
r = f.read()
r = r.split('targetproject">')[1]
r = r.split('</data>')[0]
@ -3387,8 +3439,12 @@ def store_read_project(dir):
try:
p = open(os.path.join(dir, store, '_project')).readlines()[0].strip()
except IOError:
if os.path.exists(os.path.join(dir, '.svn')):
svn = '\nTry svn instead of osc.'
else:
svn = ''
raise oscerr.NoWorkingCopy('Error: \'%s\' is not an osc project dir ' \
'or working copy.' % os.path.abspath(dir))
'or working copy.'+svn % os.path.abspath(dir))
return p
@ -3396,7 +3452,11 @@ def store_read_package(dir):
try:
p = open(os.path.join(dir, store, '_package')).readlines()[0].strip()
except IOError:
raise oscerr.NoWorkingCopy('error: \'%s\' is not an osc working copy' \
if os.path.exists(os.path.join(dir, '.svn')):
svn = '\nTry svn instead of osc.'
else:
svn = ''
raise oscerr.NoWorkingCopy('error: \'%s\' is not an osc working copy'+svn \
% os.path.abspath(dir))
return p
@ -3609,6 +3669,7 @@ def search(apiurl, search_list, kind, search_term, verbose = False, exact_matche
predicate = build_xpath_predicate(search_list, search_term, exact_matches)
u = makeurl(apiurl, ['search', kind], ['match=%s' % quote_plus(''.join(predicate))])
if conf.config['verbose']: print '>>', unquote(u), '<<'
f = http_GET(u)
root = ET.parse(f).getroot()
result = []
@ -3934,6 +3995,8 @@ def createPackageDir(pathname, prj_obj=None):
return False
else:
print '\'%s\' is not a working copy' % prj_dir
if os.path.exists(os.path.join(prj_dir, '.svn')):
print 'try svn instead of osc.'
return False