1 # Copyright (C) 2006 Novell Inc. All rights reserved.
2 # This program is free software; it may be used, copied, modified
3 # and distributed under the terms of the GNU General Public Licence,
4 # either version 2, or version 3 (at your option).
11 from optparse import SUPPRESS_HELP
13 MAN_HEADER = r""".TH %(ucname)s "1" "%(date)s" "%(name)s %(version)s" "User Commands"
15 %(name)s \- openSUSE build service command-line tool.
18 [\fIGLOBALOPTS\fR] \fISUBCOMMAND \fR[\fIOPTS\fR] [\fIARGS\fR...]
23 openSUSE build service command-line tool.
27 Type 'osc help <subcommand>' for more detailed help on a specific subcommand.
29 For additional information, see
30 * http://wiki.opensuse.org/openSUSE:Build_Service_Tutorial
31 * http://wiki.opensuse.org/openSUSE:OSC
33 You can modify osc commands, or roll you own, via the plugin API:
34 * http://wiki.opensuse.org/openSUSE:OSC_plugins
36 osc was written by several authors. This man page is automatically generated.
39 class Osc(cmdln.Cmdln):
40 """Usage: osc [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]
41 or: osc help SUBCOMMAND
43 openSUSE build service command-line tool.
44 Type 'osc help <subcommand>' for help on a specific subcommand.
49 For additional information, see
50 * http://wiki.opensuse.org/openSUSE:Build_Service_Tutorial
51 * http://wiki.opensuse.org/openSUSE:OSC
53 You can modify osc commands, or roll you own, via the plugin API:
54 * http://wiki.opensuse.org/openSUSE:OSC_plugins
59 man_header = MAN_HEADER
60 man_footer = MAN_FOOTER
62 def __init__(self, *args, **kwargs):
63 cmdln.Cmdln.__init__(self, *args, **kwargs)
64 cmdln.Cmdln.do_help.aliases.append('h')
66 def get_version(self):
67 return get_osc_version()
69 def get_optparser(self):
70 """this is the parser for "global" options (not specific to subcommand)"""
72 optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
73 optparser.add_option('--debugger', action='store_true',
74 help='jump into the debugger before executing anything')
75 optparser.add_option('--post-mortem', action='store_true',
76 help='jump into the debugger in case of errors')
77 optparser.add_option('-t', '--traceback', action='store_true',
78 help='print call trace in case of errors')
79 optparser.add_option('-H', '--http-debug', action='store_true',
80 help='debug HTTP traffic')
81 optparser.add_option('-d', '--debug', action='store_true',
82 help='print info useful for debugging')
83 optparser.add_option('-A', '--apiurl', dest='apiurl',
85 help='specify URL to access API server at or an alias')
86 optparser.add_option('-c', '--config', dest='conffile',
88 help='specify alternate configuration file')
89 optparser.add_option('--no-keyring', action='store_true',
90 help='disable usage of desktop keyring system')
91 optparser.add_option('--no-gnome-keyring', action='store_true',
92 help='disable usage of GNOME Keyring')
93 optparser.add_option('-v', '--verbose', dest='verbose', action='count', default=0,
94 help='increase verbosity')
95 optparser.add_option('-q', '--quiet', dest='verbose', action='store_const', const=-1,
96 help='be quiet, not verbose')
100 def postoptparse(self, try_again = True):
101 """merge commandline options into the config"""
103 conf.get_config(override_conffile = self.options.conffile,
104 override_apiurl = self.options.apiurl,
105 override_debug = self.options.debug,
106 override_http_debug = self.options.http_debug,
107 override_traceback = self.options.traceback,
108 override_post_mortem = self.options.post_mortem,
109 override_no_keyring = self.options.no_keyring,
110 override_no_gnome_keyring = self.options.no_gnome_keyring,
111 override_verbose = self.options.verbose)
112 except oscerr.NoConfigfile, e:
113 print >>sys.stderr, e.msg
114 print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
117 config['user'] = raw_input('Username: ')
118 config['pass'] = getpass.getpass()
119 if self.options.no_keyring:
120 config['use_keyring'] = '0'
121 if self.options.no_gnome_keyring:
122 config['gnome_keyring'] = '0'
123 if self.options.apiurl:
124 config['apiurl'] = self.options.apiurl
126 conf.write_initial_config(e.file, config)
127 print >>sys.stderr, 'done'
128 if try_again: self.postoptparse(try_again = False)
129 except oscerr.ConfigMissingApiurl, e:
130 print >>sys.stderr, e.msg
132 user = raw_input('Username: ')
133 passwd = getpass.getpass()
134 conf.add_section(e.file, e.url, user, passwd)
135 if try_again: self.postoptparse(try_again = False)
137 self.options.verbose = conf.config['verbose']
138 self.download_progress = None
139 if conf.config.get('show_download_progress', False):
140 from meter import TextMeter
141 self.download_progress = TextMeter(hide_finished=True)
144 def get_cmd_help(self, cmdname):
145 doc = self._get_cmd_handler(cmdname).__doc__
146 doc = self._help_reindent(doc)
147 doc = self._help_preprocess(doc, cmdname)
148 doc = doc.rstrip() + '\n' # trim down trailing space
149 return self._str(doc)
151 def get_api_url(self):
152 localdir = os.getcwd()
153 if (is_package_dir(localdir) or is_project_dir(localdir)) and not self.options.apiurl:
154 return store_read_apiurl(os.curdir)
156 return conf.config['apiurl']
158 # overridden from class Cmdln() to use config variables in help texts
159 def _help_preprocess(self, help, cmdname):
160 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
161 return help % conf.config
164 def do_init(self, subcmd, opts, project, package=None):
165 """${cmd_name}: Initialize a directory as working copy
167 Initialize an existing directory to be a working copy of an
168 (already existing) buildservice project/package.
170 (This is the same as checking out a package and then copying sources
171 into the directory. It does NOT create a new package. To create a
172 package, use 'osc meta pkg ... ...')
174 You wouldn't normally use this command.
176 To get a working copy of a package (e.g. for building it or working on
177 it, you would normally use the checkout command. Use "osc help
178 checkout" to get help for it.
187 init_project_dir(conf.config['apiurl'], os.curdir, project)
188 print 'Initializing %s (Project: %s)' % (os.curdir, project)
190 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
191 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
197 @cmdln.option('-a', '--arch', metavar='ARCH',
198 help='specify architecture (only for binaries)')
199 @cmdln.option('-r', '--repo', metavar='REPO',
200 help='specify repository (only for binaries)')
201 @cmdln.option('-b', '--binaries', action='store_true',
202 help='list built binaries instead of sources')
203 @cmdln.option('-R', '--revision', metavar='REVISION',
204 help='specify revision (only for sources)')
205 @cmdln.option('-e', '--expand', action='store_true',
206 help='expand linked package (only for sources)')
207 @cmdln.option('-u', '--unexpand', action='store_true',
208 help='always work with unexpanded (source) packages')
209 @cmdln.option('-v', '--verbose', action='store_true',
210 help='print extra information')
211 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
212 help='print extra information')
213 @cmdln.option('-D', '--deleted', action='store_true',
214 help='show only the former deleted projects or packages')
215 def do_list(self, subcmd, opts, *args):
216 """${cmd_name}: List sources or binaries on the server
218 Examples for listing sources:
219 ls # list all projects
220 ls PROJECT # list packages in a project
221 ls PROJECT PACKAGE # list source files of package of a project
222 ls PROJECT PACKAGE <file> # list <file> if this file exists
223 ls -v PROJECT PACKAGE # verbosely list source files of package
224 ls -l PROJECT PACKAGE # verbosely list source files of package
225 ll PROJECT PACKAGE # verbosely list source files of package
226 LL PROJECT PACKAGE # verbosely list source files of expanded link
228 With --verbose, the following fields will be shown for each item:
230 Revision number of the last commit
232 Date and time of the last commit
234 Examples for listing binaries:
235 ls -b PROJECT # list all binaries of a project
236 ls -b PROJECT -a ARCH # list ARCH binaries of a project
237 ls -b PROJECT -r REPO # list binaries in REPO
238 ls -b PROJECT PACKAGE REPO ARCH
241 ${cmd_name} [PROJECT [PACKAGE]]
242 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
246 apiurl = conf.config['apiurl']
247 args = slash_split(args)
250 if subcmd == 'lL' or subcmd == 'LL':
262 raise oscerr.WrongArgs("Too many arguments when listing deleted packages")
265 raise oscerr.WrongArgs("Too many arguments when listing deleted packages")
268 if opts.repo != args[2]:
269 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
276 if not opts.binaries:
277 raise oscerr.WrongArgs('Too many arguments')
279 if opts.arch != args[3]:
280 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
285 if opts.binaries and opts.expand:
286 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
290 # ls -b toplevel doesn't make sense, so use info from
291 # current dir if available
294 if is_project_dir(dir):
295 project = store_read_project(dir)
296 elif is_package_dir(dir):
297 project = store_read_project(dir)
298 package = store_read_package(dir)
300 apiurl = self.get_api_url()
303 raise oscerr.WrongArgs('There are no binaries to list above project level.')
305 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
309 if opts.repo and opts.arch:
310 repos.append(Repo(opts.repo, opts.arch))
311 elif opts.repo and not opts.arch:
312 repos = [repo for repo in get_repos_of_project(apiurl, project) if repo.name == opts.repo]
313 elif opts.arch and not opts.repo:
314 repos = [repo for repo in get_repos_of_project(apiurl, project) if repo.arch == opts.arch]
316 repos = get_repos_of_project(apiurl, project)
320 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
322 for result in results:
325 print '%s/%s' % (result[0].name, result[0].arch)
330 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
336 elif not opts.binaries:
338 print '\n'.join(meta_get_project_list(conf.config['apiurl'], opts.deleted))
342 if self.options.verbose:
343 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
345 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
347 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project, opts.deleted))
349 elif len(args) == 2 or len(args) == 3:
351 print_not_found = True
354 l = meta_get_filelist(conf.config['apiurl'],
357 verbose=opts.verbose,
360 link_seen = '_link' in l
362 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
363 for i in l if not fname or fname == i.name ]
365 print_not_found = False
370 print_not_found = False
373 if opts.expand or opts.unexpand or not link_seen: break
374 m = show_files_meta(conf.config['apiurl'], project, package)
376 li.read(ET.fromstring(''.join(m)).find('linkinfo'))
378 raise oscerr.LinkExpandError(project, package, li.error)
379 project, package, rev = li.project, li.package, li.rev
381 print '# -> %s %s (%s)' % (project, package, rev)
383 print '# -> %s %s (latest)' % (project, package)
385 if fname and print_not_found:
386 print 'file \'%s\' does not exist' % fname
389 @cmdln.option('-f', '--force', action='store_true',
390 help='force generation of new patchinfo file')
391 @cmdln.option('--force-update', action='store_true',
392 help='drops away collected packages from an already built patch and let it collect again')
393 def do_patchinfo(self, subcmd, opts, *args):
394 """${cmd_name}: Generate and edit a patchinfo file.
396 A patchinfo file describes the packages for an update and the kind of
401 osc patchinfo PATCH_NAME
405 project_dir = localdir = os.getcwd()
406 if is_project_dir(localdir):
407 project = store_read_project(localdir)
408 apiurl = self.get_api_url()
410 sys.exit('This command must be called in a checked out project.')
412 for p in meta_get_packagelist(apiurl, project):
413 if p.startswith("_patchinfo:"):
416 if opts.force or not patchinfo:
417 print "Creating initial patchinfo..."
418 query='cmd=createpatchinfo'
420 query += "&name=" + args[0]
421 url = makeurl(apiurl, ['source', project], query=query)
423 for p in meta_get_packagelist(apiurl, project):
424 if p.startswith("_patchinfo:"):
427 if not os.path.exists(project_dir + "/" + patchinfo):
428 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
430 filename = project_dir + "/" + patchinfo + "/_patchinfo"
434 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
435 help='affect only a given attribute')
436 @cmdln.option('--attribute-defaults', action='store_true',
437 help='include defined attribute defaults')
438 @cmdln.option('--attribute-project', action='store_true',
439 help='include project values, if missing in packages ')
440 @cmdln.option('-F', '--file', metavar='FILE',
441 help='read metadata from FILE, instead of opening an editor. '
442 '\'-\' denotes standard input. ')
443 @cmdln.option('-e', '--edit', action='store_true',
444 help='edit metadata')
445 @cmdln.option('-c', '--create', action='store_true',
446 help='create attribute without values')
447 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
448 help='set attribute values')
449 @cmdln.option('--delete', action='store_true',
450 help='delete a pattern or attribute')
451 def do_meta(self, subcmd, opts, *args):
452 """${cmd_name}: Show meta information, or edit it
454 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
456 This command displays metadata on buildservice objects like projects,
457 packages, or users. The type of metadata is specified by the word after
458 "meta", like e.g. "meta prj".
460 prj denotes metadata of a buildservice project.
461 prjconf denotes the (build) configuration of a project.
462 pkg denotes metadata of a buildservice package.
463 user denotes the metadata of a user.
464 pattern denotes installation patterns defined for a project.
466 To list patterns, use 'osc meta pattern PRJ'. An additional argument
467 will be the pattern file to view or edit.
469 With the --edit switch, the metadata can be edited. Per default, osc
470 opens the program specified by the environmental variable EDITOR with a
471 temporary file. Alternatively, content to be saved can be supplied via
472 the --file switch. If the argument is '-', input is taken from stdin:
473 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
475 When trying to edit a non-existing resource, it is created implicitly.
481 osc meta pkg PRJ PKG -e
482 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
485 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
486 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
487 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
488 osc meta pattern --delete PRJ PATTERN
492 args = slash_split(args)
494 if not args or args[0] not in metatypes.keys():
495 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
496 % ', '.join(metatypes))
501 apiurl = self.get_api_url()
504 min_args, max_args = 0, 2
505 elif cmd in ['pattern']:
506 min_args, max_args = 1, 2
507 elif cmd in ['attribute']:
508 min_args, max_args = 1, 3
509 elif cmd in ['prj', 'prjconf']:
510 min_args, max_args = 0, 1
512 min_args, max_args = 1, 1
514 if len(args) < min_args:
515 raise oscerr.WrongArgs('Too few arguments.')
516 if len(args) > max_args:
517 raise oscerr.WrongArgs('Too many arguments.')
521 if cmd in ['pkg', 'prj', 'prjconf' ]:
523 project = store_read_project(os.curdir)
529 package = store_read_package(os.curdir)
533 elif cmd == 'attribute':
539 if opts.attribute_project:
540 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
545 attributepath.append('source')
546 attributepath.append(project)
548 attributepath.append(package)
550 attributepath.append(subpackage)
551 attributepath.append('_attribute')
554 elif cmd == 'pattern':
560 # enforce pattern argument if needed
561 if opts.edit or opts.file:
562 raise oscerr.WrongArgs('A pattern file argument is required.')
565 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
567 sys.stdout.write(''.join(show_project_meta(apiurl, project)))
569 sys.stdout.write(''.join(show_package_meta(apiurl, project, package)))
570 elif cmd == 'attribute':
571 sys.stdout.write(''.join(show_attribute_meta(apiurl, project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
572 elif cmd == 'prjconf':
573 sys.stdout.write(''.join(show_project_conf(apiurl, project)))
575 r = get_user_meta(apiurl, user)
577 sys.stdout.write(''.join(r))
578 elif cmd == 'pattern':
580 r = show_pattern_meta(apiurl, project, pattern)
582 sys.stdout.write(''.join(r))
584 r = show_pattern_metalist(apiurl, project)
586 sys.stdout.write('\n'.join(r) + '\n')
589 if opts.edit and not opts.file:
591 edit_meta(metatype='prj',
593 path_args=quote_plus(project),
597 'user': conf.config['user']}))
599 edit_meta(metatype='pkg',
601 path_args=(quote_plus(project), quote_plus(package)),
605 'user': conf.config['user']}))
606 elif cmd == 'prjconf':
607 edit_meta(metatype='prjconf',
609 path_args=quote_plus(project),
613 edit_meta(metatype='user',
615 path_args=(quote_plus(user)),
617 template_args=({'user': user}))
618 elif cmd == 'pattern':
619 edit_meta(metatype='pattern',
621 path_args=(project, pattern),
625 # create attribute entry
626 if (opts.create or opts.set) and cmd == 'attribute':
627 if not opts.attribute:
628 raise oscerr.WrongOptions('no attribute given to create')
631 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
632 for i in opts.set.split(','):
633 values += '<value>%s</value>' % i
634 aname = opts.attribute.split(":")
635 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
636 url = makeurl(apiurl, attributepath)
637 for data in streamfile(url, http_POST, data=d):
638 sys.stdout.write(data)
647 f = open(opts.file).read()
649 sys.exit('could not open file \'%s\'.' % opts.file)
652 edit_meta(metatype='prj',
656 path_args=quote_plus(project))
658 edit_meta(metatype='pkg',
662 path_args=(quote_plus(project), quote_plus(package)))
663 elif cmd == 'prjconf':
664 edit_meta(metatype='prjconf',
668 path_args=quote_plus(project))
670 edit_meta(metatype='user',
674 path_args=(quote_plus(user)))
675 elif cmd == 'pattern':
676 edit_meta(metatype='pattern',
680 path_args=(project, pattern))
685 path = metatypes[cmd]['path']
687 path = path % (project, pattern)
688 u = makeurl(apiurl, [path])
690 elif cmd == 'attribute':
691 if not opts.attribute:
692 raise oscerr.WrongOptions('no attribute given to create')
693 attributepath.append(opts.attribute)
694 u = makeurl(apiurl, attributepath)
695 for data in streamfile(u, http_DELETE):
696 sys.stdout.write(data)
698 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
701 @cmdln.option('-m', '--message', metavar='TEXT',
702 help='specify message TEXT')
703 @cmdln.option('-r', '--revision', metavar='REV',
704 help='for "create", specify a certain source revision ID (the md5 sum)')
705 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
706 help='Superseding another request by this one')
707 @cmdln.option('--nodevelproject', action='store_true',
708 help='do not follow a defined devel project ' \
709 '(primary project where a package is developed)')
710 @cmdln.option('--cleanup', action='store_true',
711 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
712 @cmdln.option('--no-cleanup', action='store_true',
713 help='never remove source package on accept, but update its content')
714 @cmdln.option('--no-update', action='store_true',
715 help='never touch source package on accept (will break source links)')
716 @cmdln.option('-d', '--diff', action='store_true',
717 help='show diff only instead of creating the actual request')
718 @cmdln.option('--yes', action='store_true',
719 help='proceed without asking.')
721 @cmdln.alias("submitreq")
722 @cmdln.alias("submitpac")
723 def do_submitrequest(self, subcmd, opts, *args):
724 """${cmd_name}: Create request to submit source into another Project
726 [See http://wiki.opensuse.org/openSUSE:Build_Service_Collaboration for information
729 See the "request" command for showing and modifing existing requests.
732 osc submitreq [OPTIONS]
733 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
734 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
738 src_update = conf.config['submitrequest_on_accept_action'] or None
739 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
741 src_update = "cleanup"
742 elif opts.no_cleanup:
743 src_update = "update"
745 src_update = "noupdate"
747 args = slash_split(args)
749 # remove this block later again
750 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
751 if args and args[0] in oldcmds:
752 print "************************************************************************"
753 print "* WARNING: It looks that you are using this command with a *"
754 print "* deprecated syntax. *"
755 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
756 print "* to see the new syntax. *"
757 print "************************************************************************"
758 if args[0] == 'create':
764 raise oscerr.WrongArgs('Too many arguments.')
766 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
767 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
769 apiurl = self.get_api_url()
771 if len(args) == 0 and is_project_dir(os.getcwd()):
773 # submit requests for multiple packages are currently handled via multiple requests
774 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
775 project = store_read_project(os.curdir)
781 # loop via all packages for checking their state
782 for p in meta_get_packagelist(apiurl, project):
783 if p.startswith("_patchinfo:"):
786 # get _link info from server, that knows about the local state ...
787 u = makeurl(apiurl, ['source', project, p])
789 root = ET.parse(f).getroot()
790 linkinfo = root.find('linkinfo')
792 print "Package ", p, " is not a source link."
793 sys.exit("This is currently not supported.")
794 if linkinfo.get('error'):
795 print "Package ", p, " is a broken source link."
796 sys.exit("Please fix this first")
797 t = linkinfo.get('project')
799 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
800 # Real fix is to ask the api if sources are modificated
801 # but there is no such call yet.
802 targetprojects.append(t)
804 print "Submitting package ", p
806 print " Skipping package ", p
808 print "Skipping package ", p, " since it is a source link pointing inside the project."
812 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
813 print "\nEverything fine? Can we create the requests ? [y/n]"
814 if sys.stdin.read(1) != "y":
815 print >>sys.stderr, 'Aborted...'
816 raise oscerr.UserAbort()
818 # loop via all packages to do the action
820 result = create_submit_request(apiurl, project, p)
823 sys.exit("submit request creation failed")
824 sr_ids.append(result)
826 # create submit requests for all found patchinfos
830 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
833 for t in targetprojects:
834 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
835 (project, p, t, p, options_block)
839 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
840 (actionxml, cgi.escape(opts.message or ""))
841 u = makeurl(apiurl, ['request'], query='cmd=create')
842 f = http_POST(u, data=xml)
844 root = ET.parse(f).getroot()
845 sr_ids.append(root.get('id'))
847 print "Requests created: ",
850 sys.exit('Successfully finished')
853 # try using the working copy at hand
854 p = findpacs(os.curdir)[0]
855 src_project = p.prjname
858 if len(args) == 0 and p.islink():
859 dst_project = p.linkinfo.project
860 dst_package = p.linkinfo.package
862 dst_project = args[0]
864 dst_package = args[1]
866 dst_package = src_package
868 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
869 'Please provide it the target via commandline arguments.' % p.name)
871 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
872 if len(modified) > 0:
873 print 'Your working copy has local modifications.'
874 repl = raw_input('Proceed without committing the local changes? (y|N) ')
876 raise oscerr.UserAbort()
878 # get the arguments from the commandline
879 src_project, src_package, dst_project = args[0:3]
881 dst_package = args[3]
883 dst_package = src_package
885 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
886 + self.get_cmd_help('request'))
888 if not opts.nodevelproject:
891 devloc = show_develproject(apiurl, dst_project, dst_package)
892 except urllib2.HTTPError:
893 print >>sys.stderr, """\
894 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
895 % (dst_project, dst_package)
899 dst_project != devloc and \
900 src_project != devloc:
902 A different project, %s, is defined as the place where development
903 of the package %s primarily takes place.
904 Please submit there instead, or use --nodevelproject to force direct submission.""" \
905 % (devloc, dst_package)
910 if opts.diff or not opts.message:
912 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
913 rdiff += server_diff(apiurl,
914 dst_project, dst_package, opts.revision,
915 src_project, src_package, None, True)
921 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
922 user = conf.get_apiurl_usr(apiurl)
923 myreqs = [ i for i in reqs if i.state.who == user ]
926 print 'You already created the following submit request: %s.' % \
927 ', '.join([str(i.reqid) for i in myreqs ])
928 repl = raw_input('Supersede the old requests? (y/n/c) ')
929 if repl.lower() == 'c':
930 print >>sys.stderr, 'Aborting'
931 raise oscerr.UserAbort()
936 changes_re = re.compile(r'^--- .*\.changes ')
937 for line in rdiff.split('\n'):
938 if line.startswith('--- '):
939 if changes_re.match(line):
944 difflines.append(line)
945 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
947 result = create_submit_request(apiurl,
948 src_project, src_package,
949 dst_project, dst_package,
950 opts.message, orev=opts.revision, src_update=src_update)
951 if repl.lower() == 'y':
953 change_request_state(apiurl, str(req.reqid), 'superseded',
954 'superseded by %s' % result, result)
957 change_request_state(conf.config['apiurl'], opts.supersede, 'superseded',
958 opts.message or '', result)
960 print 'created request id', result
962 def _actionparser(self, opt_str, value, parser):
964 if not hasattr(parser.values, 'actiondata'):
965 setattr(parser.values, 'actiondata', [])
966 if parser.values.actions == None:
967 parser.values.actions = []
972 if ((arg[:2] == "--" and len(arg) > 2) or
973 (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
979 parser.values.actions.append(value[0])
981 parser.values.actiondata.append(value)
983 def _submit_request(self, args, opts, options_block):
985 apiurl = self.get_api_url()
986 if len(args) == 0 and is_project_dir(os.getcwd()):
987 # submit requests for multiple packages are currently handled via multiple requests
988 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
989 project = store_read_project(os.curdir)
995 # loop via all packages for checking their state
996 for p in meta_get_packagelist(apiurl, project):
997 if p.startswith("_patchinfo:"):
1000 # get _link info from server, that knows about the local state ...
1001 u = makeurl(apiurl, ['source', project, p])
1003 root = ET.parse(f).getroot()
1004 linkinfo = root.find('linkinfo')
1005 if linkinfo == None:
1006 print "Package ", p, " is not a source link."
1007 sys.exit("This is currently not supported.")
1008 if linkinfo.get('error'):
1009 print "Package ", p, " is a broken source link."
1010 sys.exit("Please fix this first")
1011 t = linkinfo.get('project')
1015 rdiff = server_diff(apiurl, t, p, opts.revision, project, p, None, True)
1020 targetprojects.append(t)
1022 rdiffmsg.append("old: %s/%s\nnew: %s/%s\n%s" %(t, p, project, p,rdiff))
1024 print "Skipping package ", p, " since it has no difference with the target package."
1026 print "Skipping package ", p, " since it is a source link pointing inside the project."
1028 print ''.join(rdiffmsg)
1033 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
1034 print "\nEverything fine? Can we create the requests ? [y/n]"
1035 if sys.stdin.read(1) != "y":
1036 sys.exit("Aborted...")
1038 # loop via all packages to do the action
1040 s = """<action type="submit"> <source project="%s" package="%s" rev="%s"/> <target project="%s" package="%s"/> %s </action>""" % \
1041 (project, p, opts.revision or show_upstream_rev(apiurl, project, p), t, p, options_block)
1044 # create submit requests for all found patchinfos
1046 for t in targetprojects:
1047 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
1048 (project, p, t, p, options_block)
1053 elif len(args) <= 2:
1054 # try using the working copy at hand
1055 p = findpacs(os.curdir)[0]
1056 src_project = p.prjname
1057 src_package = p.name
1058 if len(args) == 0 and p.islink():
1059 dst_project = p.linkinfo.project
1060 dst_package = p.linkinfo.package
1062 dst_project = args[0]
1064 dst_package = args[1]
1066 dst_package = src_package
1068 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
1069 'Please provide it the target via commandline arguments.' % p.name)
1071 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
1072 if len(modified) > 0:
1073 print 'Your working copy has local modifications.'
1074 repl = raw_input('Proceed without committing the local changes? (y|N) ')
1077 elif len(args) >= 3:
1078 # get the arguments from the commandline
1079 src_project, src_package, dst_project = args[0:3]
1081 dst_package = args[3]
1083 dst_package = src_package
1085 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1086 + self.get_cmd_help('request'))
1088 if not opts.nodevelproject:
1091 devloc = show_develproject(apiurl, dst_project, dst_package)
1092 except urllib2.HTTPError:
1093 print >>sys.stderr, """\
1094 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
1095 % (dst_project, dst_package)
1099 dst_project != devloc and \
1100 src_project != devloc:
1102 A different project, %s, is defined as the place where development
1103 of the package %s primarily takes place.
1104 Please submit there instead, or use --nodevelproject to force direct submission.""" \
1105 % (devloc, dst_package)
1112 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
1113 rdiff += server_diff(apiurl,
1114 dst_project, dst_package, opts.revision,
1115 src_project, src_package, None, True)
1121 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
1122 user = conf.get_apiurl_usr(apiurl)
1123 myreqs = [ i for i in reqs if i.state.who == user ]
1126 print 'You already created the following submit request: %s.' % \
1127 ', '.join([str(i.reqid) for i in myreqs ])
1128 repl = raw_input('Supersede the old requests? (y/n/c) ')
1129 if repl.lower() == 'c':
1130 print >>sys.stderr, 'Aborting'
1133 actionxml = """<action type="submit"> <source project="%s" package="%s" rev="%s"/> <target project="%s" package="%s"/> %s </action>""" % \
1134 (src_project, src_package, opts.revision or show_upstream_rev(apiurl, src_project, src_package), dst_project, dst_package, options_block)
1135 if repl.lower() == 'y':
1137 change_request_state(apiurl, str(req.reqid), 'superseded',
1138 'superseded by %s' % result, result)
1141 change_request_state(apiurl, opts.supersede, 'superseded', '', result)
1143 #print 'created request id', result
1146 def _delete_request(self, args, opts):
1148 raise oscerr.WrongArgs('Please specify at least a project.')
1150 raise oscerr.WrongArgs('Too many arguments.')
1154 package = """package="%s" """ % (args[1])
1155 actionxml = """<action type="delete"> <target project="%s" %s/> </action> """ % (args[0], package)
1158 def _changedevel_request(self, args, opts):
1160 raise oscerr.WrongArgs('Too many arguments.')
1162 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
1164 devel_project = store_read_project(wd)
1165 devel_package = package = store_read_package(wd)
1166 project = conf.config['getpac_default_project']
1169 raise oscerr.WrongArgs('Too few arguments.')
1171 devel_project = args[2]
1174 devel_package = package
1176 devel_package = args[3]
1178 actionxml = """ <action type="change_devel"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> </action> """ % \
1179 (devel_project, devel_package, project, package)
1183 def _add_role(self, args, opts):
1185 raise oscerr.WrongArgs('Too many arguments.')
1187 raise oscerr.WrongArgs('Too few arguments.')
1189 apiurl = self.get_api_url()
1197 if get_user_meta(apiurl, user) == None:
1198 raise oscerr.WrongArgs('osc: an error occured.')
1200 actionxml = """ <action type="add_role"> <target project="%s" package="%s" /> <person name="%s" role="%s" /> </action> """ % \
1201 (project, package, user, role)
1205 def _set_bugowner(self, args, opts):
1207 raise oscerr.WrongArgs('Too many arguments.')
1209 raise oscerr.WrongArgs('Too few arguments.')
1211 apiurl = self.get_api_url()
1218 if get_user_meta(apiurl, user) == None:
1219 raise oscerr.WrongArgs('osc: an error occured.')
1221 actionxml = """ <action type="set_bugowner"> <target project="%s" package="%s" /> <person name="%s" /> </action> """ % \
1222 (project, package, user)
1226 @cmdln.option('-a', '--action', action='callback', callback = _actionparser,dest = 'actions',
1227 help='specify action type of a request, can be : submit/delete/change_devel/add_role/set_bugowner')
1228 @cmdln.option('-m', '--message', metavar='TEXT',
1229 help='specify message TEXT')
1230 @cmdln.option('-r', '--revision', metavar='REV',
1231 help='for "create", specify a certain source revision ID (the md5 sum)')
1232 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
1233 help='Superseding another request by this one')
1234 @cmdln.option('--nodevelproject', action='store_true',
1235 help='do not follow a defined devel project ' \
1236 '(primary project where a package is developed)')
1237 @cmdln.option('--cleanup', action='store_true',
1238 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
1239 @cmdln.option('--no-cleanup', action='store_true',
1240 help='never remove source package on accept, but update its content')
1241 @cmdln.option('--no-update', action='store_true',
1242 help='never touch source package on accept (will break source links)')
1243 @cmdln.option('-d', '--diff', action='store_true',
1244 help='show diff only instead of creating the actual request')
1245 @cmdln.option('--yes', action='store_true',
1246 help='proceed without asking.')
1247 @cmdln.alias("creq")
1248 def do_createrequest(self, subcmd, opts, *args):
1249 """${cmd_name}: create multiple requests with a single command
1252 osc creq [OPTIONS] [
1253 -a submit SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
1254 -a delete PROJECT [PACKAGE]
1255 -a change_devel PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
1256 -a add_role USER ROLE PROJECT [PACKAGE]
1257 -a set_bugowner USER PROJECT [PACKAGE]
1260 Option -m works for all types of request, the rest work only for submit.
1262 osc creq -a submit -a delete home:someone:branches:openSUSE:Tools -a change_devel openSUSE:Tools osc home:someone:branches:openSUSE:Tools -m ok
1264 This will submit all modified packages under current directory, delete project home:someone:branches:openSUSE:Tools and change the devel project to home:someone:branches:openSUSE:Tools for package osc in project openSUSE:Tools.
1267 src_update = conf.config['submitrequest_on_accept_action'] or None
1268 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
1270 src_update = "cleanup"
1271 elif opts.no_cleanup:
1272 src_update = "update"
1273 elif opts.no_update:
1274 src_update = "noupdate"
1278 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
1280 args = slash_split(args)
1282 apiurl = self.get_api_url()
1286 for ai in opts.actions:
1288 args = opts.actiondata[i]
1290 actionsxml += self._submit_request(args,opts, options_block)
1291 elif ai == 'delete':
1292 args = opts.actiondata[i]
1293 actionsxml += self._delete_request(args,opts)
1295 elif ai == 'change_devel':
1296 args = opts.actiondata[i]
1297 actionsxml += self._changedevel_request(args,opts)
1299 elif ai == 'add_role':
1300 args = opts.actiondata[i]
1301 actionsxml += self._add_role(args,opts)
1303 elif ai == 'set_bugowner':
1304 args = opts.actiondata[i]
1305 actionsxml += self._set_bugowner(args,opts)
1308 raise oscerr.WrongArgs('Unsupported action %s' % ai)
1309 if actionsxml == "":
1310 sys.exit('No actions need to be taken.')
1312 if not opts.message:
1313 opts.message = edit_message()
1316 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
1317 (actionsxml, cgi.escape(opts.message or ""))
1318 u = makeurl(apiurl, ['request'], query='cmd=create')
1319 f = http_POST(u, data=xml)
1321 root = ET.parse(f).getroot()
1322 return root.get('id')
1325 @cmdln.option('-m', '--message', metavar='TEXT',
1326 help='specify message TEXT')
1328 @cmdln.alias("deletereq")
1329 def do_deleterequest(self, subcmd, opts, *args):
1330 """${cmd_name}: Create request to delete a package or project
1333 osc deletereq [-m TEXT] # works in checked out project/package
1334 osc deletereq [-m TEXT] PROJECT [PACKAGE]
1338 args = slash_split(args)
1344 if is_project_dir(os.getcwd()):
1345 project = store_read_project(os.curdir)
1346 elif is_package_dir(os.getcwd()):
1347 project = store_read_project(os.curdir)
1348 package = store_read_package(os.curdir)
1350 raise oscerr.WrongArgs('Please specify at least a project.')
1351 elif len(args) == 1:
1357 raise oscerr.WrongArgs('Too many arguments.')
1359 apiurl = self.get_api_url()
1361 if not opts.message:
1363 if package is not None:
1364 footer=textwrap.TextWrapper(width = 66).fill(
1365 'please explain why you like to delete package %s of project %s'
1366 % (package,project))
1368 footer=textwrap.TextWrapper(width = 66).fill(
1369 'please explain why you like to delete project %s' % project)
1370 opts.message = edit_message(footer)
1372 result = create_delete_request(apiurl, project, package, opts.message)
1376 @cmdln.option('-m', '--message', metavar='TEXT',
1377 help='specify message TEXT')
1379 @cmdln.alias("changedevelreq")
1380 def do_changedevelrequest(self, subcmd, opts, *args):
1381 """${cmd_name}: Create request to change the devel package definition.
1383 [See http://wiki.opensuse.org/openSUSE:Build_Service_Collaboration
1384 for information on this topic.]
1386 See the "request" command for showing and modifing existing requests.
1388 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
1392 raise oscerr.WrongArgs('Too many arguments.')
1394 apiurl = self.get_api_url()
1396 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
1398 devel_project = store_read_project(wd)
1399 devel_package = package = store_read_package(wd)
1400 project = conf.config['getpac_default_project']
1403 raise oscerr.WrongArgs('Too few arguments.')
1405 devel_project = args[2]
1408 devel_package = package
1410 devel_package = args[3]
1412 if not opts.message:
1414 footer=textwrap.TextWrapper(width = 66).fill(
1415 'please explain why you like to change the devel project of %s/%s to %s/%s'
1416 % (project,package,devel_project,devel_package))
1417 opts.message = edit_message(footer)
1419 result = create_change_devel_request(apiurl,
1420 devel_project, devel_package,
1426 @cmdln.option('-d', '--diff', action='store_true',
1427 help='generate a diff')
1428 @cmdln.option('-u', '--unified', action='store_true',
1429 help='output the diff in the unified diff format')
1430 @cmdln.option('-m', '--message', metavar='TEXT',
1431 help='specify message TEXT')
1432 @cmdln.option('-t', '--type', metavar='TYPE',
1433 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1434 @cmdln.option('-a', '--all', action='store_true',
1435 help='all states. Same as\'-s all\'')
1436 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1437 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]')
1438 @cmdln.option('-D', '--days', metavar='DAYS',
1439 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1440 @cmdln.option('-U', '--user', metavar='USER',
1441 help='same as -M, but for the specified USER')
1442 @cmdln.option('-b', '--brief', action='store_true', default=False,
1443 help='print output in list view as list subcommand')
1444 @cmdln.option('-M', '--mine', action='store_true',
1445 help='only show requests created by yourself')
1446 @cmdln.option('-B', '--bugowner', action='store_true',
1447 help='also show requests about packages where I am bugowner')
1448 @cmdln.option('-i', '--interactive', action='store_true',
1449 help='interactive review of request')
1450 @cmdln.option('--non-interactive', action='store_true',
1451 help='non-interactive review of request')
1452 @cmdln.option('--exclude-target-project', action='append',
1453 help='exclude target project from request list')
1454 @cmdln.option('--involved-projects', action='store_true',
1455 help='show all requests for project/packages where USER is involved')
1457 @cmdln.alias("review")
1458 def do_request(self, subcmd, opts, *args):
1459 """${cmd_name}: Show and modify requests
1461 [See http://wiki.opensuse.org/openSUSE:Build_Service_Collaboration
1462 for information on this topic.]
1464 This command shows and modifies existing requests. To create new requests
1465 you need to call one of the following:
1468 osc changedevelrequest
1469 To send low level requests to the buildservice API, use:
1472 This command has the following sub commands:
1474 "list" lists open requests attached to a project or package or person.
1475 Uses the project/package of the current directory if none of
1476 -M, -U USER, project/package are given.
1478 "log" will show the history of the given ID
1480 "show" will show the request itself, and generate a diff for review, if
1481 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1483 "decline" will change the request state to "declined" and append a
1484 message that you specify with the --message option.
1486 "wipe" will permanently delete a request.
1488 "revoke" will set the request state to "revoked" and append a
1489 message that you specify with the --message option.
1491 "accept" will change the request state to "accepted" and will trigger
1492 the actual submit process. That would normally be a server-side copy of
1493 the source package to the target package.
1495 "checkout" will checkout the request's source package. This only works for "submit" requests.
1498 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1500 osc request [show] [-d] [-b] ID
1501 osc request accept [-m TEXT] ID
1502 osc request reopen [-m TEXT] ID
1503 osc request approvenew [-m TEXT] PROJECT
1504 osc request decline [-m TEXT] ID
1505 osc request revoke [-m TEXT] ID
1507 osc request checkout/co ID
1508 osc review accept [-m TEXT] ID
1509 osc review decline [-m TEXT] ID
1510 osc review new [-m TEXT] ID # for setting a temporary comment without changing the state
1514 args = slash_split(args)
1516 if opts.all and opts.state:
1517 raise oscerr.WrongOptions('Sorry, the options \'--all\' and \'--state\' ' \
1518 'are mutually exclusive.')
1519 if opts.mine and opts.user:
1520 raise oscerr.WrongOptions('Sorry, the options \'--user\' and \'--mine\' ' \
1521 'are mutually exclusive.')
1522 if opts.interactive and opts.non_interactive:
1523 raise oscerr.WrongOptions('Sorry, the options \'--interactive\' and ' \
1524 '\'--non-interactive\' are mutually exclusive')
1529 if opts.state == '':
1532 if opts.state == '':
1535 cmds = ['list', 'log', 'show', 'decline', 'reopen', 'accept', 'approvenew', 'wipe', 'revoke', 'checkout', 'co', 'help']
1536 if not args or args[0] not in cmds:
1537 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1538 % (args[0],', '.join(cmds)))
1544 return self.do_help(['help', 'request'])
1547 min_args, max_args = 0, 2
1549 min_args, max_args = 1, 1
1550 if len(args) < min_args:
1551 raise oscerr.WrongArgs('Too few arguments.')
1552 if len(args) > max_args:
1553 raise oscerr.WrongArgs('Too many arguments.')
1555 apiurl = self.get_api_url()
1557 if cmd == 'list' or cmd == 'approvenew':
1562 elif not opts.mine and not opts.user:
1564 project = store_read_project(os.curdir)
1565 package = store_read_package(os.curdir)
1566 except oscerr.NoWorkingCopy:
1571 elif cmd in ['log', 'show', 'decline', 'reopen', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1574 # list and approvenew
1575 if cmd == 'list' or cmd == 'approvenew':
1576 states = ('new', 'accepted', 'revoked', 'declined')
1578 if cmd == 'approvenew':
1580 results = get_request_list(apiurl, project, package, '', ['new'])
1582 state_list = opts.state.split(',')
1583 if opts.state == 'all':
1584 state_list = ['all']
1586 for s in state_list:
1588 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1590 who = conf.get_apiurl_usr(apiurl)
1594 state_list = ['all']
1596 ## FIXME -B not implemented!
1598 if (self.options.debug):
1599 print 'list: option --bugowner ignored: not impl.'
1601 if opts.involved_projects:
1602 who = who or conf.get_apiurl_usr(apiurl)
1603 results = get_user_projpkgs_request_list(apiurl, who, req_state=state_list,
1604 req_type=opts.type, exclude_projects=opts.exclude_target_project or [])
1606 results = get_request_list(apiurl, project, package, who,
1607 state_list, opts.type, opts.exclude_target_project or [])
1609 results.sort(reverse=True)
1611 days = opts.days or conf.config['request_list_days']
1618 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1621 ## bs has received 2009-09-20 a new xquery compare() function
1622 ## which allows us to limit the list inside of get_request_list
1623 ## That would be much faster for coolo. But counting the remainder
1624 ## would not be possible with current xquery implementation.
1625 ## Workaround: fetch all, and filter on client side.
1627 ## FIXME: date filtering should become implemented on server side
1628 for result in results:
1629 if days == 0 or result.state.when > since or result.state.name == 'new':
1630 print result.list_view()
1634 print "There are %d requests older than %s days.\n" % (skipped, days)
1636 if cmd == 'approvenew':
1637 print "\n *** Approve them all ? [y/n] ***"
1638 if sys.stdin.read(1) == "y":
1640 if not opts.message:
1641 opts.message = edit_message()
1642 for result in results:
1643 print result.reqid, ": ",
1644 r = change_request_state(conf.config['apiurl'],
1645 str(result.reqid), 'accepted', opts.message or '')
1648 print >>sys.stderr, 'Aborted...'
1649 raise oscerr.UserAbort()
1652 for l in get_request_log(conf.config['apiurl'], reqid):
1657 r = get_request(conf.config['apiurl'], reqid)
1660 elif (opts.interactive or conf.config['request_show_interactive']) and not opts.non_interactive:
1661 return request_interactive_review(conf.config['apiurl'], r)
1664 # fixme: will inevitably fail if the given target doesn't exist
1665 if opts.diff and r.actions[0].type != 'submit':
1666 raise oscerr.WrongOptions('\'--diff\' is not possible for request type: \'%s\'' % r.actions[0].type)
1669 print server_diff(conf.config['apiurl'],
1670 r.actions[0].dst_project, r.actions[0].dst_package, None,
1671 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified, True)
1672 except urllib2.HTTPError, e:
1674 e.osc_msg = 'Diff not possible'
1676 # backward compatiblity: only a recent api/backend supports the missingok parameter
1678 print server_diff(conf.config['apiurl'],
1679 r.actions[0].dst_project, r.actions[0].dst_package, None,
1680 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified, False)
1681 except urllib2.HTTPError, e:
1682 e.osc_msg = 'Diff not possible'
1686 elif cmd == 'checkout' or cmd == 'co':
1687 r = get_request(conf.config['apiurl'], reqid)
1688 submits = [ i for i in r.actions if i.type == 'submit' ]
1689 if not len(submits):
1690 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1691 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1692 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1695 if not opts.message:
1696 opts.message = edit_message()
1697 state_map = {'reopen' : 'new', 'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1698 # Change review state only
1699 if subcmd == 'review':
1700 if cmd in ['accept', 'decline', 'new']:
1701 r = change_review_state(conf.config['apiurl'],
1702 reqid, state_map[cmd], conf.config['user'], opts.message or '')
1704 # Change state of entire request
1705 elif cmd in ['reopen', 'accept', 'decline', 'wipe', 'revoke']:
1706 r = change_request_state(conf.config['apiurl'],
1707 reqid, state_map[cmd], opts.message or '')
1710 # editmeta and its aliases are all depracated
1711 @cmdln.alias("editprj")
1712 @cmdln.alias("createprj")
1713 @cmdln.alias("editpac")
1714 @cmdln.alias("createpac")
1715 @cmdln.alias("edituser")
1716 @cmdln.alias("usermeta")
1718 def do_editmeta(self, subcmd, opts, *args):
1721 Obsolete command to edit metadata. Use 'meta' now.
1723 See the help output of 'meta'.
1727 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1728 print >>sys.stderr, 'See \'osc help meta\'.'
1729 #self.do_help([None, 'meta'])
1733 @cmdln.option('-r', '--revision', metavar='rev',
1734 help='use the specified revision.')
1735 @cmdln.option('-u', '--unset', action='store_true',
1736 help='remove revision in link, it will point always to latest revision')
1737 def do_setlinkrev(self, subcmd, opts, *args):
1738 """${cmd_name}: Updates a revision number in a source link.
1740 This command adds or updates a specified revision number in a source link.
1741 The current revision of the source is used, if no revision number is specified.
1745 osc setlinkrev PROJECT [PACKAGE]
1749 args = slash_split(args)
1750 apiurl = conf.config['apiurl']
1753 p = findpacs(os.curdir)[0]
1758 sys.exit('Local directory is no checked out source link package, aborting')
1759 elif len(args) == 2:
1762 elif len(args) == 1:
1765 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1766 + self.get_cmd_help('setlinkrev'))
1769 packages = [ package ]
1771 packages = meta_get_packagelist(apiurl, project)
1774 print "setting revision for package", p
1778 rev, dummy = parseRevisionOption(opts.revision)
1779 set_link_rev(apiurl, project, p, rev)
1782 def do_linktobranch(self, subcmd, opts, *args):
1783 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1785 This command tells the server to convert a _link with or without a project.diff
1786 to a branch. This is a full copy with a _link file pointing to the branched place.
1789 osc linktobranch # can be used in checked out package
1790 osc linktobranch PROJECT PACKAGE
1793 args = slash_split(args)
1794 apiurl = self.get_api_url()
1798 project = store_read_project(wd)
1799 package = store_read_package(wd)
1800 update_local_dir = True
1802 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1804 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1808 update_local_dir = False
1811 link_to_branch(apiurl, project, package)
1812 if update_local_dir:
1814 pac.update(rev=pac.latest_rev())
1817 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1818 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1819 @cmdln.option('-c', '--current', action='store_true',
1820 help='link fixed against current revision.')
1821 @cmdln.option('-r', '--revision', metavar='rev',
1822 help='link the specified revision.')
1823 @cmdln.option('-f', '--force', action='store_true',
1824 help='overwrite an existing link file if it is there.')
1825 @cmdln.option('-d', '--disable-publish', action='store_true',
1826 help='disable publishing of the linked package')
1827 def do_linkpac(self, subcmd, opts, *args):
1828 """${cmd_name}: "Link" a package to another package
1830 A linked package is a clone of another package, but plus local
1831 modifications. It can be cross-project.
1833 The DESTPAC name is optional; the source packages' name will be used if
1836 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1838 To add a patch, add the patch as file and add it to the _link file.
1839 You can also specify text which will be inserted at the top of the spec file.
1841 See the examples in the _link file.
1843 NOTE: In case you are not aware about the difference of 'linkpac' and 'branch' command
1844 you should use the 'branch' command by default.
1847 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1851 args = slash_split(args)
1853 if not args or len(args) < 3:
1854 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1855 + self.get_cmd_help('linkpac'))
1857 rev, dummy = parseRevisionOption(opts.revision)
1859 src_project = args[0]
1860 src_package = args[1]
1861 dst_project = args[2]
1863 dst_package = args[3]
1865 dst_package = src_package
1867 if src_project == dst_project and src_package == dst_package:
1868 raise oscerr.WrongArgs('Error: source and destination are the same.')
1870 if src_project == dst_project and not opts.cicount:
1871 # in this case, the user usually wants to build different spec
1872 # files from the same source
1873 opts.cicount = "copy"
1876 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1878 if rev and not checkRevision(src_project, src_package, rev):
1879 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1882 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1884 @cmdln.option('--nosources', action='store_true',
1885 help='ignore source packages when copying build results to destination project')
1886 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1887 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1888 @cmdln.option('-d', '--disable-publish', action='store_true',
1889 help='disable publishing of the aggregated package')
1890 def do_aggregatepac(self, subcmd, opts, *args):
1891 """${cmd_name}: "Aggregate" a package to another package
1893 Aggregation of a package means that the build results (binaries) of a
1894 package are basically copied into another project.
1895 This can be used to make packages available from building that are
1896 needed in a project but available only in a different project. Note
1897 that this is done at the expense of disk space. See
1898 http://wiki.opensuse.org/openSUSE:Build_Service_Tips_and_Tricks#_link_and__aggregate
1899 for more information.
1901 The DESTPAC name is optional; the source packages' name will be used if
1905 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1909 args = slash_split(args)
1911 if not args or len(args) < 3:
1912 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1913 + self.get_cmd_help('aggregatepac'))
1915 src_project = args[0]
1916 src_package = args[1]
1917 dst_project = args[2]
1919 dst_package = args[3]
1921 dst_package = src_package
1923 if src_project == dst_project and src_package == dst_package:
1924 raise oscerr.WrongArgs('Error: source and destination are the same.')
1928 for pair in opts.map_repo.split(','):
1929 src_tgt = pair.split('=')
1930 if len(src_tgt) != 2:
1931 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1932 repo_map[src_tgt[0]] = src_tgt[1]
1934 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish, opts.nosources)
1937 @cmdln.option('-c', '--client-side-copy', action='store_true',
1938 help='do a (slower) client-side copy')
1939 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1940 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1941 @cmdln.option('-d', '--keep-develproject', action='store_true',
1942 help='keep develproject tag in the package metadata')
1943 @cmdln.option('-r', '--revision', metavar='rev',
1944 help='link the specified revision.')
1945 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1946 help='URL of destination api server. Default is the source api server.')
1947 @cmdln.option('-m', '--message', metavar='TEXT',
1948 help='specify message TEXT')
1949 @cmdln.option('-e', '--expand', action='store_true',
1950 help='if the source package is a link then copy the expanded version of the link')
1951 def do_copypac(self, subcmd, opts, *args):
1952 """${cmd_name}: Copy a package
1954 A way to copy package to somewhere else.
1956 It can be done across buildservice instances, if the -t option is used.
1957 In that case, a client-side copy is implied.
1959 Using --client-side-copy always involves downloading all files, and
1960 uploading them to the target.
1962 The DESTPAC name is optional; the source packages' name will be used if
1966 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1970 args = slash_split(args)
1972 if not args or len(args) < 3:
1973 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1974 + self.get_cmd_help('copypac'))
1976 src_project = args[0]
1977 src_package = args[1]
1978 dst_project = args[2]
1980 dst_package = args[3]
1982 dst_package = src_package
1984 src_apiurl = conf.config['apiurl']
1986 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1988 dst_apiurl = src_apiurl
1990 if src_apiurl != dst_apiurl:
1991 opts.client_side_copy = True
1993 rev, dummy = parseRevisionOption(opts.revision)
1996 comment = opts.message
1999 rev = show_upstream_rev(src_apiurl, src_project, src_package)
2000 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
2002 if src_project == dst_project and \
2003 src_package == dst_package and \
2005 src_apiurl == dst_apiurl:
2006 raise oscerr.WrongArgs('Source and destination are the same.')
2008 r = copy_pac(src_apiurl, src_project, src_package,
2009 dst_apiurl, dst_project, dst_package,
2010 client_side_copy=opts.client_side_copy,
2011 keep_maintainers=opts.keep_maintainers,
2012 keep_develproject=opts.keep_develproject,
2019 @cmdln.option('-c', '--checkout', action='store_true',
2020 help='Checkout branched package afterwards ' \
2021 '(\'osc bco\' is a shorthand for this option)' )
2022 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
2023 help='Use this attribute to find affected packages (default is OBS:Maintained)')
2024 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
2025 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
2026 def do_mbranch(self, subcmd, opts, *args):
2027 """${cmd_name}: Multiple branch of a package
2029 [See http://wiki.opensuse.org/openSUSE:Build_Service_Concept_Maintenance
2030 for information on this topic.]
2032 This command is used for creating multiple links of defined version of a package
2033 in one project. This is esp. used for maintenance updates.
2035 The branched package will live in
2036 home:USERNAME:branches:ATTRIBUTE:PACKAGE
2037 if nothing else specified.
2040 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
2043 args = slash_split(args)
2046 maintained_attribute = conf.config['maintained_attribute']
2047 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
2049 if not len(args) or len(args) > 2:
2050 raise oscerr.WrongArgs('Wrong number of arguments.')
2056 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
2060 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
2063 print "Project " + r + " created."
2066 init_project_dir(conf.config['apiurl'], r, r)
2067 print statfrmt('A', r)
2070 for package in meta_get_packagelist(conf.config['apiurl'], r):
2072 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
2074 print >>sys.stderr, 'Error while checkout package:\n', package
2076 if conf.config['verbose']:
2077 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
2080 @cmdln.alias('branchco')
2082 @cmdln.alias('getpac')
2083 @cmdln.option('--nodevelproject', action='store_true',
2084 help='do not follow a defined devel project ' \
2085 '(primary project where a package is developed)')
2086 @cmdln.option('-c', '--checkout', action='store_true',
2087 help='Checkout branched package afterwards ' \
2088 '(\'osc bco\' is a shorthand for this option)' )
2089 @cmdln.option('-f', '--force', default=False, action="store_true",
2090 help='force branch, overwrite target')
2091 @cmdln.option('-m', '--message', metavar='TEXT',
2092 help='specify message TEXT')
2093 @cmdln.option('-r', '--revision', metavar='rev',
2094 help='branch against a specific revision')
2095 def do_branch(self, subcmd, opts, *args):
2096 """${cmd_name}: Branch a package
2098 [See http://wiki.opensuse.org/openSUSE:Build_Service_Collaboration
2099 for information on this topic.]
2101 Create a source link from a package of an existing project to a new
2102 subproject of the requesters home project (home:branches:)
2104 The branched package will live in
2105 home:USERNAME:branches:PROJECT/PACKAGE
2106 if nothing else specified.
2108 With getpac or bco, the branched package will come from
2109 %(getpac_default_project)s
2110 if nothing else specified.
2114 osc branch SOURCEPROJECT SOURCEPACKAGE
2115 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
2116 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
2117 osc getpac SOURCEPACKAGE
2122 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
2123 args = slash_split(args)
2124 tproject = tpackage = None
2126 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
2127 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
2128 # python has no args.unshift ???
2129 args = [ conf.config['getpac_default_project'] , args[0] ]
2131 if len(args) == 0 and is_package_dir('.'):
2132 args = (store_read_project('.'), store_read_package('.'))
2134 if len(args) < 2 or len(args) > 4:
2135 raise oscerr.WrongArgs('Wrong number of arguments.')
2137 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
2139 expected = tproject = args[2]
2143 if not opts.message:
2144 footer='please specify the purpose of your branch'
2145 template='This package was branched from %s in order to ...\n' % args[0]
2146 opts.message = edit_message(footer, template)
2148 exists, targetprj, targetpkg, srcprj, srcpkg = \
2149 branch_pkg(conf.config['apiurl'], args[0], args[1],
2150 nodevelproject=opts.nodevelproject, rev=opts.revision,
2151 target_project=tproject, target_package=tpackage,
2152 return_existing=opts.checkout, msg=opts.message or '',
2155 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
2158 if not exists and (srcprj is not None and srcprj != args[0] or \
2159 srcprj is None and targetprj != expected):
2160 devloc = srcprj or targetprj
2161 if not srcprj and 'branches:' in targetprj:
2162 devloc = targetprj.split('branches:')[1]
2163 print '\nNote: The branch has been created of a different project,\n' \
2165 ' which is the primary location of where development for\n' \
2166 ' that package takes place.\n' \
2167 ' That\'s also where you would normally make changes against.\n' \
2168 ' A direct branch of the specified package can be forced\n' \
2169 ' with the --nodevelproject option.\n' % devloc
2171 package = tpackage or args[1]
2173 checkout_package(conf.config['apiurl'], targetprj, package,
2174 expand_link=True, prj_dir=targetprj)
2175 if conf.config['verbose']:
2176 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
2179 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
2180 apiopt = '-A %s ' % conf.config['apiurl']
2181 print 'A working copy of the branched package can be checked out with:\n\n' \
2183 % (apiopt, targetprj, package)
2184 print_request_list(conf.config['apiurl'], args[0], args[1])
2186 print_request_list(conf.config['apiurl'], devloc, args[1])
2189 def do_undelete(self, subcmd, opts, *args):
2190 """${cmd_name}: Restores a deleted project or package on the server.
2192 The server restores a package including the sources and meta configuration.
2193 Binaries remain to be lost and will be rebuild.
2196 osc undelete PROJECT
2197 osc undelete PROJECT PACKAGE [PACKAGE ...]
2202 args = slash_split(args)
2204 raise oscerr.WrongArgs('Missing argument.')
2210 undelete_package(conf.config['apiurl'], prj, pkg)
2212 undelete_project(conf.config['apiurl'], prj)
2215 @cmdln.option('-f', '--force', action='store_true',
2216 help='deletes a package or an empty project')
2217 def do_rdelete(self, subcmd, opts, *args):
2218 """${cmd_name}: Delete a project or packages on the server.
2220 As a safety measure, project must be empty (i.e., you need to delete all
2221 packages first). If you are sure that you want to remove this project and all
2222 its packages use \'--force\' switch.
2225 osc rdelete -f PROJECT
2226 osc rdelete PROJECT PACKAGE [PACKAGE ...]
2231 args = slash_split(args)
2233 raise oscerr.WrongArgs('Missing argument.')
2239 # careful: if pkg is an empty string, the package delete request results
2240 # into a project delete request - which works recursively...
2242 delete_package(conf.config['apiurl'], prj, pkg)
2243 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
2244 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
2245 'If you are sure that you want to remove this project and all its ' \
2246 'packages use the \'--force\' switch'
2249 delete_project(conf.config['apiurl'], prj)
2252 def do_deletepac(self, subcmd, opts, *args):
2253 print """${cmd_name} is obsolete !
2256 osc delete for checked out packages or projects
2258 osc rdelete for server side operations."""
2263 @cmdln.option('-f', '--force', action='store_true',
2264 help='deletes a project and its packages')
2265 def do_deleteprj(self, subcmd, opts, project):
2266 """${cmd_name} is obsolete !
2273 @cmdln.alias('metafromspec')
2274 @cmdln.option('', '--specfile', metavar='FILE',
2275 help='Path to specfile. (if you pass more than working copy this option is ignored)')
2276 def do_updatepacmetafromspec(self, subcmd, opts, *args):
2277 """${cmd_name}: Update package meta information from a specfile
2279 ARG, if specified, is a package working copy.
2285 args = parseargs(args)
2286 if opts.specfile and len(args) == 1:
2287 specfile = opts.specfile
2290 pacs = findpacs(args)
2292 p.read_meta_from_spec(specfile)
2293 p.update_package_meta()
2297 @cmdln.option('-c', '--change', metavar='rev',
2298 help='the change made by revision rev (like -r rev-1:rev).'
2299 'If rev is negative this is like -r rev:rev-1.')
2300 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
2301 help='If rev1 is specified it will compare your working copy against '
2302 'the revision (rev1) on the server. '
2303 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
2304 '(NOTE: changes in your working copy are ignored in this case)')
2305 @cmdln.option('-p', '--plain', action='store_true',
2306 help='output the diff in plain (not unified) diff format')
2307 @cmdln.option('--missingok', action='store_true',
2308 help='do not fail if the source or target project/package does not exist on the server')
2309 def do_diff(self, subcmd, opts, *args):
2310 """${cmd_name}: Generates a diff
2312 Generates a diff, comparing local changes against the repository
2315 ARG, specified, is a filename to include in the diff.
2321 args = parseargs(args)
2322 pacs = findpacs(args)
2326 rev = int(opts.change)
2336 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
2339 rev1, rev2 = parseRevisionOption(opts.revision)
2343 diff += ''.join(make_diff(pac, rev1))
2345 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
2346 pac.prjname, pac.name, rev2, not opts.plain, opts.missingok)
2351 @cmdln.option('--oldprj', metavar='OLDPRJ',
2352 help='project to compare against'
2353 ' (deprecated, use 3 argument form)')
2354 @cmdln.option('--oldpkg', metavar='OLDPKG',
2355 help='package to compare against'
2356 ' (deprecated, use 3 argument form)')
2357 @cmdln.option('-r', '--revision', metavar='N[:M]',
2358 help='revision id, where N = old revision and M = new revision')
2359 @cmdln.option('-p', '--plain', action='store_true',
2360 help='output the diff in plain (not unified) diff format')
2361 @cmdln.option('-c', '--change', metavar='rev',
2362 help='the change made by revision rev (like -r rev-1:rev). '
2363 'If rev is negative this is like -r rev:rev-1.')
2364 @cmdln.option('--missingok', action='store_true',
2365 help='do not fail if the source or target project/package does not exist on the server')
2366 def do_rdiff(self, subcmd, opts, *args):
2367 """${cmd_name}: Server-side "pretty" diff of two packages
2369 Compares two packages (three or four arguments) or shows the
2370 changes of a specified revision of a package (two arguments)
2372 If no revision is specified the latest revision is used.
2374 Note that this command doesn't return a normal diff (which could be
2375 applied as patch), but a "pretty" diff, which also compares the content
2380 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
2381 osc ${cmd_name} PROJECT PACKAGE
2385 args = slash_split(args)
2396 new_project = args[0]
2397 new_package = args[1]
2399 old_project = opts.oldprj
2401 old_package = opts.oldpkg
2402 elif len(args) == 3 or len(args) == 4:
2403 if opts.oldprj or opts.oldpkg:
2404 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
2405 old_project = args[0]
2406 new_package = old_package = args[1]
2407 new_project = args[2]
2409 new_package = args[3]
2411 raise oscerr.WrongArgs('Wrong number of arguments')
2416 rev = int(opts.change)
2426 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
2430 rev1, rev2 = parseRevisionOption(opts.revision)
2432 rdiff = server_diff(conf.config['apiurl'],
2433 old_project, old_package, rev1,
2434 new_project, new_package, rev2, not opts.plain, opts.missingok)
2439 def do_install(self, subcmd, opts, *args):
2440 """${cmd_name}: install a package after build via zypper in -r
2442 Not implemented yet. Use osc repourls,
2443 select the url you best like (standard),
2444 chop off after the last /, this should work with zypper.
2451 args = slash_split(args)
2452 args = expand_proj_pack(args)
2455 ## if there is only one argument, and it ends in .ymp
2456 ## then fetch it, Parse XML to get the first
2457 ## metapackage.group.repositories.repository.url
2458 ## and construct zypper cmd's for all
2459 ## metapackage.group.software.item.name
2461 ## if args[0] is already an url, the use it as is.
2463 cmd = "sudo zypper -p http://download.opensuse.org/repositories/%s/%s --no-refresh -v in %s" % (re.sub(':',':/',args[0]), 'openSUSE_11.1', args[1])
2464 print self.do_install.__doc__
2465 print "Example: \n" + cmd
2468 def do_repourls(self, subcmd, opts, *args):
2469 """${cmd_name}: Shows URLs of .repo files
2471 Shows URLs on which to access the project .repos files (yum-style
2472 metadata) on download.opensuse.org.
2475 osc repourls [PROJECT]
2480 apiurl = self.get_api_url()
2484 elif len(args) == 0:
2485 project = store_read_project('.')
2487 raise oscerr.WrongArgs('Wrong number of arguments')
2489 # XXX: API should somehow tell that
2490 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
2491 repos = get_repositories_of_project(apiurl, project)
2493 print url_tmpl % (project.replace(':', ':/'), repo, project)
2496 @cmdln.option('-r', '--revision', metavar='rev',
2497 help='checkout the specified revision. '
2498 'NOTE: if you checkout the complete project '
2499 'this option is ignored!')
2500 @cmdln.option('-e', '--expand-link', action='store_true',
2501 help='if a package is a link, check out the expanded '
2502 'sources (no-op, since this became the default)')
2503 @cmdln.option('-u', '--unexpand-link', action='store_true',
2504 help='if a package is a link, check out the _link file ' \
2505 'instead of the expanded sources')
2506 @cmdln.option('-M', '--meta', action='store_true',
2507 help='checkout out meta data instead of sources' )
2508 @cmdln.option('-c', '--current-dir', action='store_true',
2509 help='place PACKAGE folder in the current directory' \
2510 'instead of a PROJECT/PACKAGE directory')
2511 @cmdln.option('-s', '--source-service-files', action='store_true',
2512 help='Use server side generated sources instead of local generation.' )
2513 @cmdln.option('-S', '--server-side-source-service-files', action='store_true',
2514 help='Use server side generated sources instead of local generation.' )
2515 @cmdln.option('-l', '--limit-size', metavar='limit_size',
2516 help='Skip all files with a given size')
2518 def do_checkout(self, subcmd, opts, *args):
2519 """${cmd_name}: Check out content from the repository
2521 Check out content from the repository server, creating a local working
2524 When checking out a single package, the option --revision can be used
2525 to specify a revision of the package to be checked out.
2527 When a package is a source link, then it will be checked out in
2528 expanded form. If --unexpand-link option is used, the checkout will
2529 instead produce the raw _link file plus patches.
2532 osc co PROJECT [PACKAGE] [FILE]
2533 osc co PROJECT # entire project
2534 osc co PROJECT PACKAGE # a package
2535 osc co PROJECT PACKAGE FILE # single file -> to current dir
2537 while inside a project directory:
2538 osc co PACKAGE # check out PACKAGE from project
2543 if opts.unexpand_link:
2548 args = slash_split(args)
2549 project = package = filename = None
2551 apiurl = self.get_api_url()
2554 project = project_dir = args[0]
2560 if args and len(args) == 1:
2561 localdir = os.getcwd()
2562 if is_project_dir(localdir):
2563 project = store_read_project(localdir)
2564 project_dir = localdir
2567 rev, dummy = parseRevisionOption(opts.revision)
2571 if rev and rev != "latest" and not checkRevision(project, package, rev):
2572 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2576 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2579 if opts.current_dir:
2581 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2582 prj_dir=project_dir, service_files = opts.source_service_files, server_service_files=opts.server_side_source_service_files, progress_obj=self.download_progress, limit_size=opts.limit_size, meta=opts.meta)
2583 print_request_list(apiurl, project, package)
2587 if sys.platform[:3] == 'win':
2588 prj_dir = prj_dir.replace(':', ';')
2589 if os.path.exists(prj_dir):
2590 sys.exit('osc: project \'%s\' already exists' % project)
2592 # check if the project does exist (show_project_meta will throw an exception)
2593 show_project_meta(apiurl, project)
2595 init_project_dir(apiurl, prj_dir, project)
2596 print statfrmt('A', prj_dir)
2599 for package in meta_get_packagelist(apiurl, project):
2601 checkout_package(apiurl, project, package, expand_link = expand_link, \
2602 prj_dir = prj_dir, service_files = opts.source_service_files, server_service_files = opts.server_side_source_service_files, progress_obj=self.download_progress, limit_size=opts.limit_size, meta=opts.meta)
2603 except oscerr.LinkExpandError, e:
2604 print >>sys.stderr, 'Link cannot be expanded:\n', e
2605 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2606 # check out in unexpanded form at least
2607 checkout_package(apiurl, project, package, expand_link = False, \
2608 prj_dir = prj_dir, service_files = opts.source_service_files, server_service_files = opts.server_side_source_service_files, progress_obj=self.download_progress, limit_size=opts.limit_size, meta=opts.meta)
2609 print_request_list(apiurl, project)
2612 raise oscerr.WrongArgs('Missing argument.\n\n' \
2613 + self.get_cmd_help('checkout'))
2616 @cmdln.option('-q', '--quiet', action='store_true',
2617 help='print as little as possible')
2618 @cmdln.option('-v', '--verbose', action='store_true',
2619 help='print extra information')
2621 def do_status(self, subcmd, opts, *args):
2622 """${cmd_name}: Show status of files in working copy
2624 Show the status of files in a local working copy, indicating whether
2625 files have been changed locally, deleted, added, ...
2627 The first column in the output specifies the status and is one of the
2628 following characters:
2629 ' ' no modifications
2634 '?' item is not under version control
2635 '!' item is missing (removed by non-osc command) or incomplete
2640 osc st file1 file2 ...
2643 osc status [OPTS] [PATH...]
2647 args = parseargs(args)
2649 # storage for single Package() objects
2651 # storage for a project dir ( { prj_instance : [ package objects ] } )
2654 # when 'status' is run inside a project dir, it should
2655 # stat all packages existing in the wc
2656 if is_project_dir(arg):
2657 prj = Project(arg, False)
2659 if conf.config['do_package_tracking']:
2661 for pac in prj.pacs_have:
2662 # we cannot create package objects if the dir does not exist
2663 if not pac in prj.pacs_broken:
2664 prjpacs[prj].append(os.path.join(arg, pac))
2666 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2667 elif is_package_dir(arg):
2668 pacpaths.append(arg)
2669 elif os.path.isfile(arg):
2670 pacpaths.append(arg)
2672 msg = '\'%s\' is neither a project or a package directory' % arg
2673 raise oscerr.NoWorkingCopy, msg
2675 # process single packages
2676 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2677 # process project dirs
2678 for prj, pacs in prjpacs.iteritems():
2679 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2681 print '\n'.join(lines)
2684 def do_add(self, subcmd, opts, *args):
2685 """${cmd_name}: Mark files to be added upon the next commit
2687 In case a URL is given the file will get downloaded and registered to be downloaded
2688 by the server as well via the download_url source service.
2690 This is recommended for release tar balls to track their source and to help
2691 others to review your changes esp. on version upgrades.
2694 osc add URL [URL...]
2695 osc add FILE [FILE...]
2699 raise oscerr.WrongArgs('Missing argument.\n\n' \
2700 + self.get_cmd_help('add'))
2702 # Do some magic here, when adding a url. We want that the server to download the tar ball and to verify it
2703 for arg in parseargs(args):
2704 if arg.startswith('http://') or arg.startswith('https://') or arg.startswith('ftp://'):
2705 addDownloadUrlService(arg)
2710 def do_mkpac(self, subcmd, opts, *args):
2711 """${cmd_name}: Create a new package under version control
2714 osc mkpac new_package
2717 if not conf.config['do_package_tracking']:
2718 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2719 "in the [general] section in the co