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

Merge branch 'master' of gitorious.org:opensuse/osc

This commit is contained in:
Juergen Weigert 2011-02-11 14:42:17 +01:00
commit 1685ff801b
17 changed files with 153 additions and 124 deletions

1
NEWS
View File

@ -17,6 +17,7 @@
- support for new source service modes: disabled, trylocal and localonly
- support project wide source services
- support for armv7hl architecuture. used to denote armv7 + hardfloat binaries
- add force option to accept requests in review state.
0.130
- new "revert" command to restore the original working copy file (without

View File

@ -9,6 +9,7 @@ import cmdln
import conf
import oscerr
import sys
from util import safewriter
from optparse import SUPPRESS_HELP
MAN_HEADER = r""".TH %(ucname)s "1" "%(date)s" "%(name)s %(version)s" "User Commands"
@ -63,6 +64,8 @@ class Osc(cmdln.Cmdln):
def __init__(self, *args, **kwargs):
cmdln.Cmdln.__init__(self, *args, **kwargs)
cmdln.Cmdln.do_help.aliases.append('h')
sys.stderr = safewriter.SafeWriter(sys.stderr)
sys.stdout = safewriter.SafeWriter(sys.stdout)
def get_version(self):
return get_osc_version()
@ -349,10 +352,7 @@ class Osc(cmdln.Cmdln):
elif not opts.binaries:
if not args:
for prj in meta_get_project_list(apiurl, opts.deleted):
try:
print prj
except UnicodeEncodeError:
print prj.encode('unicode_escape')
print prj
elif len(args) == 1:
if opts.verbose:
@ -361,10 +361,7 @@ class Osc(cmdln.Cmdln):
if opts.expand:
raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
for pkg in meta_get_packagelist(apiurl, project, opts.deleted):
try:
print pkg
except UnicodeEncodeError:
print pkg.encode('unicode_escape')
print pkg
elif len(args) == 2 or len(args) == 3:
link_seen = False
@ -1198,7 +1195,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
'superseded by %s' % result, result)
if opts.supersede:
change_request_state(apiurl, opts.supersede, 'superseded', '', result)
change_request_state(apiurl, opts.supersede, 'superseded', '', result)
#print 'created request id', result
return actionxml
@ -1605,6 +1602,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
help='limit to requests which contain a given action type (submit/delete/change_devel)')
@cmdln.option('-a', '--all', action='store_true',
help='all states. Same as\'-s all\'')
@cmdln.option('-f', '--force', action='store_true',
help='enforce state change, can be used to ignore open reviews')
@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/revoked/declined) or "all" [default=new, or all, if no args given]')
@cmdln.option('-D', '--days', metavar='DAYS',
@ -1873,7 +1872,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
for result in results:
print result.reqid, ": ",
r = change_request_state(apiurl,
result.reqid, 'accepted', opts.message or '')
result.reqid, 'accepted', opts.message or '', force=opts.force)
print 'Result of change request state: %s' % r
else:
print >>sys.stderr, 'Aborted...'
@ -1955,7 +1954,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
tmpl = change_request_state_template(rq, state_map[cmd])
opts.message = edit_message(template=tmpl)
r = change_request_state(apiurl,
reqid, state_map[cmd], opts.message or '')
reqid, state_map[cmd], opts.message or '', force=opts.force)
print 'Result of change request state: %s' % r
# editmeta and its aliases are all depracated
@ -2407,11 +2406,6 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if len(args) >= 4:
tpackage = args[3]
if not opts.message:
footer='please specify the purpose of your branch'
template='This package was branched from %s in order to ...\n' % args[0]
opts.message = edit_message(footer, template)
exists, targetprj, targetpkg, srcprj, srcpkg = \
branch_pkg(apiurl, args[0], args[1],
nodevelproject=opts.nodevelproject, rev=opts.revision,
@ -2422,18 +2416,23 @@ Please submit there instead, or use --nodevelproject to force direct submission.
print >>sys.stderr, 'Using existing branch project: %s' % targetprj
devloc = None
if not exists and (srcprj is not None and srcprj != args[0] or \
srcprj is None and targetprj != expected):
devloc = srcprj or targetprj
if not srcprj and 'branches:' in targetprj:
devloc = targetprj.split('branches:')[1]
print '\nNote: The branch has been created of a different project,\n' \
' %s,\n' \
' which is the primary location of where development for\n' \
' that package takes place.\n' \
' That\'s also where you would normally make changes against.\n' \
' A direct branch of the specified package can be forced\n' \
' with the --nodevelproject option.\n' % devloc
if not exists and (srcprj != args[0] or srcpkg != args[1]):
try:
root = ET.fromstring(''.join(show_attribute_meta(apiurl, args[0], None, None,
conf.config['maintained_update_project_attribute'], False, False)))
# this might raise an AttributeError
uproject = root.find('attribute').find('value').text
print '\nNote: The branch has been created from the configured update project: %s' \
% uproject
except (AttributeError, urllib2.HTTPError), e:
devloc = srcprj
print '\nNote: The branch has been created of a different project,\n' \
' %s,\n' \
' which is the primary location of where development for\n' \
' that package takes place.\n' \
' That\'s also where you would normally make changes against.\n' \
' A direct branch of the specified package can be forced\n' \
' with the --nodevelproject option.\n' % devloc
package = tpackage or args[1]
if opts.checkout:
@ -2450,7 +2449,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
% (apiopt, targetprj, package)
print_request_list(apiurl, args[0], args[1])
if devloc:
print_request_list(apiurl, devloc, args[1])
print_request_list(apiurl, devloc, srcpkg)
def do_undelete(self, subcmd, opts, *args):
@ -5042,7 +5041,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
what = {'project': ''}
elif type in args_sr:
requests = get_request_list(apiurl, req_who=user, exclude_target_projects=exclude_projects)
for r in requests:
for r in sorted(requests):
print r.list_view(), '\n'
return
elif not type in args_pkg:
@ -5061,19 +5060,25 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if list_requests:
# try api side search as supported since OBS 2.2
try:
u = makeurl(apiurl, ['request'], ['view=collection&user=%s' % quote_plus(user)])
res = http_GET(u)
requests = []
for root in res['request'].findall('request'):
r = Request()
r.read(root)
requests.append(r)
for r in requests:
print r.list_view(), '\n'
return
except:
# skip it ... try again with old style below
pass
u = makeurl(apiurl, ['request'], {
'view' : 'collection',
'state': 'pending',
'user' : user,
})
f = http_GET(u)
root = ET.parse(f).getroot()
requests = []
for node in root.findall('request'):
r = Request()
r.read(node)
requests.append(r)
for r in sorted(requests):
print r.list_view(), '\n'
return
except urllib2.HTTPError, e:
if e.code == 400:
# skip it ... try again with old style below
pass
res = get_user_projpkgs(apiurl, user, role_filter, exclude_projects,
what.has_key('project'), what.has_key('package'),
@ -5100,7 +5105,7 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if list_requests:
# old style, only for OBS 2.1 and before. Should not be used, since it is slow and incomplete
requests = get_user_projpkgs_request_list(apiurl, user, projpkgs=request_todo)
for r in requests:
for r in sorted(requests):
print r.list_view(), '\n'
else:
for i in sorted(roles.keys()):

View File

@ -108,9 +108,7 @@ DEFAULTS = { 'apiurl': 'https://api.opensuse.org',
'checkout_rooted': '0',
# local files to ignore with status, addremove, ....
'exclude_glob': '.osc CVS .svn .* _linkerror *~ #*# *.orig *.bak *.changes.vctmp.*',
# 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.
# whether to keep passwords in plaintext.
'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'
@ -224,11 +222,10 @@ apiurl = %(apiurl)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.
# keep passwords in plaintext.
# Set to 0 to obfuscate passwords. It's no real security, just
# prevents most people from remembering your password if they watch
# you editing this file.
#plaintext_passwd = %(plaintext_passwd)s
# limit the age of requests shown with 'osc req list'.
@ -285,7 +282,7 @@ apiurl = %(apiurl)s
[%(apiurl)s]
user = %(user)s
passx = %(passx)s
pass = %(pass)s
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used
@ -601,7 +598,6 @@ def write_initial_config(conffile, entries, custom_template = ''):
conf_template = custom_template or new_conf_template
config = DEFAULTS.copy()
config.update(entries)
config['passx'] = base64.b64encode(config['pass'].encode('bz2'))
# at this point use_keyring and gnome_keyring are str objects
if config['use_keyring'] == '1' and GENERIC_KEYRING:
protocol, host = \
@ -622,6 +618,9 @@ def write_initial_config(conffile, entries, custom_template = ''):
config['passx'] = ''
if not config['plaintext_passwd']:
config['pass'] = ''
else:
config['passx'] = base64.b64encode(config['pass'].encode('bz2'))
sio = StringIO.StringIO(conf_template.strip() % config)
cp = OscConfigParser.OscConfigParser(DEFAULTS)
cp.readfp(sio)
@ -675,7 +674,11 @@ def add_section(filename, url, user, passwd):
cp.set(url, 'user', user)
if not config['plaintext_passwd']:
cp.remove_option(url, 'pass')
cp.set(url, 'passx', base64.b64encode(passwd.encode('bz2')))
cp.set(url, 'passx', base64.b64encode(passwd.encode('bz2')))
else:
cp.remove_option(url, 'passx')
cp.set(url, 'pass', passwd)
file = open(filename, 'w')
cp.write(file, True)
if file: file.close()
@ -786,23 +789,29 @@ 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!
try:
passwordx = cp.get(url, 'passx', raw=True).decode('base64').decode('bz2') # especially on password!
except:
passwordx = ''
if password == None or password == 'your_password':
password = ''
if user is None or user == '':
raise oscerr.ConfigError('user is blank for %s' % apiurl, config['conffile'])
if password is None or password == 'your_password':
try:
password = passwordx.decode('base64').decode('bz2')
except:
print >>sys.stderr, "%s: no credentials known" % url
password = 'your_password'
elif password != passwordx.decode('base64').decode('bz2'):
if not passwordx:
# passx not present
print >>sys.stderr, '%s: rewriting from plain pass to encoded pass\n' % url
if config['plaintext_passwd'] and passwordx or not config['plaintext_passwd'] and password:
if not config['plaintext_passwd']:
if password != passwordx:
print >>sys.stderr, '%s: rewriting from plain pass to encoded pass' % url
add_section(conffile, url, user, password)
else:
print >>sys.stderr, '%s: pass and passx mismatch (rewriting from plain pass to encoded pass)\n' % url
add_section(conffile, url, user, password)
if password != passwordx:
print >>sys.stderr, '%s: rewriting from encoded pass to plain pass' % url
add_section(conffile, url, user, passwordx)
if not config['plaintext_passwd']:
password = passwordx
if cp.has_option(url, 'http_headers'):
http_headers = cp.get(url, 'http_headers')

View File

@ -2500,7 +2500,7 @@ class Request:
def list_view(self):
"""return "list view" format"""
import textwrap, locale
import textwrap
lines = ['%6s State:%-10s By:%-12s When:%-19s' % (self.reqid, self.state.name, self.state.who, self.state.when)]
tmpl = ' %(type)-16s %(source)-50s %(target)s'
for action in self.actions:
@ -2512,14 +2512,12 @@ class Request:
if history:
lines.append(' From: %s' % ' -> '.join(history))
if self.description:
descr = self.description.encode(locale.getpreferredencoding(), 'replace')
lines.append(textwrap.fill(descr, width=80, initial_indent=' Descr: ',
lines.append(textwrap.fill(self.description, width=80, initial_indent=' Descr: ',
subsequent_indent=' '))
return '\n'.join(lines)
def __str__(self):
"""return "detailed" format"""
import locale
lines = ['Request: #%s\n' % self.reqid]
for action in self.actions:
tmpl = ' %(type)-13s %(source)s %(target)s'
@ -2529,7 +2527,7 @@ class Request:
lines.append(tmpl % Request.format_action(action, show_srcupdate=True))
lines.append('\n\nMessage:')
if self.description:
lines.append(self.description.encode(locale.getpreferredencoding(), 'replace'))
lines.append(self.description)
else:
lines.append('<no message>')
if self.state:
@ -2568,7 +2566,7 @@ class Request:
return '\n'.join(lines)
def __cmp__(self, other):
return cmp(self.reqid, other.reqid)
return cmp(int(self.reqid), int(other.reqid))
def create(self, apiurl):
"""create a new request"""
@ -3522,10 +3520,12 @@ def change_review_state(apiurl, reqid, newstate, by_user='', by_group='', by_pro
root = ET.parse(f).getroot()
return root.attrib['code']
def change_request_state(apiurl, reqid, newstate, message='', supersed=None):
def change_request_state(apiurl, reqid, newstate, message='', supersed=None, force=False):
query={'cmd': 'changestate', 'newstate': newstate }
if supersed:
query['superseded_by'] = supersed
if force:
query['force'] = "1"
u = makeurl(apiurl,
['request', reqid], query=query)
f = http_POST(u, data=message)
@ -3595,7 +3595,7 @@ def get_review_list(apiurl, project='', package='', byuser='', bygroup='', bypro
requests.append(r)
return requests
def get_request_list(apiurl, project='', package='', req_who='', req_state=('new',), req_type=None, exclude_target_projects=[]):
def get_request_list(apiurl, project='', package='', req_who='', req_state=('new','review',), req_type=None, exclude_target_projects=[]):
xpath = ''
if not 'all' in req_state:
for state in req_state:
@ -3633,7 +3633,7 @@ def get_request_list(apiurl, project='', package='', req_who='', req_state=('new
return requests
# old style search, this is to be removed
def get_user_projpkgs_request_list(apiurl, user, req_state=('new',), req_type=None, exclude_projects=[], projpkgs={}):
def get_user_projpkgs_request_list(apiurl, user, req_state=('new','review',), req_type=None, exclude_projects=[], projpkgs={}):
"""OBSOLETE: user involved request search is supported by OBS 2.2 server side in a better way
Return all running requests for all projects/packages where is user is involved"""
if not projpkgs:
@ -4330,12 +4330,13 @@ def branch_pkg(apiurl, src_project, src_package, nodevelproject=False, rev=None,
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+)/(\S+)", msg)
root = ET.fromstring(e.read())
summary = root.find('summary')
if summary is None:
raise oscerr.APIError('unexpected response:\n%s' % ET.tostring(root))
m = re.match(r"branch target package already exists: (\S+)/(\S+)", summary.text)
if not m:
e.msg += '\n' + msg
e.msg += '\n' + summary.text
raise
return (True, m.group(1), m.group(2), None, None)
@ -5889,13 +5890,8 @@ def request_interactive_review(apiurl, request, initial_cmd=''):
tmpfile = None
def print_request(request):
try:
# FIXME: print can fail with unicode chars in the string.
# Here we fix the symptoms, not the cause.
# UnicodeEncodeError: 'ascii' codec can't encode character u'\u2002' in position 309: ordinal not in range(128)
print request
except:
print request.__str__().encode('ascii', 'xmlcharrefreplace')
print request
print_request(request)
try:
prompt = '(a)ccept/(d)ecline/(r)evoke/c(l)one/(s)kip/(c)ancel > '

View File

@ -1 +1 @@
__all__ = ['ar', 'cpio', 'debquery', 'packagequery', 'rpmquery']
__all__ = ['ar', 'cpio', 'debquery', 'packagequery', 'rpmquery', 'safewriter']

29
osc/util/safewriter.py Normal file
View File

@ -0,0 +1,29 @@
# be careful when debugging this code:
# don't add print statements when setting sys.stdout = SafeWriter(sys.stdout)...
class SafeWriter:
"""
Safely write an (unicode) str. In case of an "UnicodeEncodeError" the
the str is encoded with the "encoding" encoding.
All getattr, setattr calls are passed through to the "writer" instance.
"""
def __init__(self, writer, encoding='unicode_escape'):
self.__dict__['writer'] = writer
self.__dict__['encoding'] = encoding
def __get_writer(self):
return self.__dict__['writer']
def __get_encoding(self):
return self.__dict__['encoding']
def write(self, s):
try:
self.__get_writer().write(s)
except UnicodeEncodeError, e:
self.__get_writer().write(s.encode(self.__get_encoding()))
def __getattr__(self, name):
return getattr(self.__get_writer(), name)
def __setattr__(self, name, value):
setattr(self.__get_writer(), name, value)

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used

View File

@ -90,9 +90,8 @@ apiurl = http://localhost
#source_validator_directory = /usr/lib/osc/source_validators
[http://localhost]
user = Admin
pass = opensuse
passx = QlpoOTFBWSZTWeTSblkAAAGBgAIBygAgADDACGNEHxaYXckU4UJDk0m5ZA==
user=Admin
pass=opensuse
# set aliases for this apiurl
# aliases = foo, bar
# email used in .changes, unless the one from osc meta prj <user> will be used