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 import urlgrabber.progress
12 from optparse import SUPPRESS_HELP
14 MAN_HEADER = r""".TH %(ucname)s "1" "%(date)s" "%(name)s %(version)s" "User Commands"
16 %(name)s \- openSUSE build service command-line tool.
19 [\fIGLOBALOPTS\fR] \fISUBCOMMAND \fR[\fIOPTS\fR] [\fIARGS\fR...]
24 openSUSE build service command-line tool.
28 Type 'osc help <subcommand>' for more detailed help on a specific subcommand.
30 For additional information, see
31 * http://en.opensuse.org/Build_Service_Tutorial
32 * http://en.opensuse.org/Build_Service/CLI
34 You can modify osc commands, or roll you own, via the plugin API:
35 * http://en.opensuse.org/Build_Service/osc_plugins
37 osc was written by several authors. This man page is automatically generated.
40 class Osc(cmdln.Cmdln):
41 """Usage: osc [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]
42 or: osc help SUBCOMMAND
44 openSUSE build service command-line tool.
45 Type 'osc help <subcommand>' for help on a specific subcommand.
50 For additional information, see
51 * http://en.opensuse.org/Build_Service_Tutorial
52 * http://en.opensuse.org/Build_Service/CLI
54 You can modify osc commands, or roll you own, via the plugin API:
55 * http://en.opensuse.org/Build_Service/osc_plugins
60 man_header = MAN_HEADER
61 man_footer = MAN_FOOTER
63 def __init__(self, *args, **kwargs):
64 cmdln.Cmdln.__init__(self, *args, **kwargs)
65 cmdln.Cmdln.do_help.aliases.append('h')
67 def get_version(self):
68 return get_osc_version()
70 def get_optparser(self):
71 """this is the parser for "global" options (not specific to subcommand)"""
73 optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
74 optparser.add_option('--debugger', action='store_true',
75 help='jump into the debugger before executing anything')
76 optparser.add_option('--post-mortem', action='store_true',
77 help='jump into the debugger in case of errors')
78 optparser.add_option('-t', '--traceback', action='store_true',
79 help='print call trace in case of errors')
80 optparser.add_option('-H', '--http-debug', action='store_true',
81 help='debug HTTP traffic')
82 optparser.add_option('-d', '--debug', action='store_true',
83 help='print info useful for debugging')
84 optparser.add_option('-A', '--apiurl', dest='apiurl',
86 help='specify URL to access API server at or an alias')
87 optparser.add_option('-c', '--config', dest='conffile',
89 help='specify alternate configuration file')
90 optparser.add_option('--no-keyring', action='store_true',
91 help='disable usage of desktop keyring system')
92 optparser.add_option('--no-gnome-keyring', action='store_true',
93 help='disable usage of GNOME Keyring')
94 optparser.add_option('-v', '--verbose', dest='verbose', action='count', default=0,
95 help='increase verbosity')
96 optparser.add_option('-q', '--quiet', dest='verbose', action='store_const', const=-1,
97 help='be quiet, not verbose')
101 def postoptparse(self, try_again = True):
102 """merge commandline options into the config"""
104 conf.get_config(override_conffile = self.options.conffile,
105 override_apiurl = self.options.apiurl,
106 override_debug = self.options.debug,
107 override_http_debug = self.options.http_debug,
108 override_traceback = self.options.traceback,
109 override_post_mortem = self.options.post_mortem,
110 override_no_keyring = self.options.no_keyring,
111 override_no_gnome_keyring = self.options.no_gnome_keyring,
112 override_verbose = self.options.verbose)
113 except oscerr.NoConfigfile, e:
114 print >>sys.stderr, e.msg
115 print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
118 config['user'] = raw_input('Username: ')
119 config['pass'] = getpass.getpass()
120 if self.options.no_keyring:
121 config['use_keyring'] = '0'
122 if self.options.no_gnome_keyring:
123 config['gnome_keyring'] = '0'
124 if self.options.apiurl:
125 config['apiurl'] = self.options.apiurl
127 conf.write_initial_config(e.file, config)
128 print >>sys.stderr, 'done'
129 if try_again: self.postoptparse(try_again = False)
130 except oscerr.ConfigMissingApiurl, e:
131 print >>sys.stderr, e.msg
133 user = raw_input('Username: ')
134 passwd = getpass.getpass()
135 conf.add_section(e.file, e.url, user, passwd)
136 if try_again: self.postoptparse(try_again = False)
138 self.options.verbose = conf.config['verbose']
139 self.download_progress = None
140 if conf.config.get('show_download_progress', False):
141 from meter import TextMeter
142 self.download_progress = TextMeter(hide_finished=True)
145 def get_cmd_help(self, cmdname):
146 doc = self._get_cmd_handler(cmdname).__doc__
147 doc = self._help_reindent(doc)
148 doc = self._help_preprocess(doc, cmdname)
149 doc = doc.rstrip() + '\n' # trim down trailing space
150 return self._str(doc)
152 def get_api_url(self):
153 localdir = os.getcwd()
154 if (is_package_dir(localdir) or is_project_dir(localdir)) and not self.options.apiurl:
155 return store_read_apiurl(os.curdir)
157 return conf.config['apiurl']
159 # overridden from class Cmdln() to use config variables in help texts
160 def _help_preprocess(self, help, cmdname):
161 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
162 return help % conf.config
165 def do_init(self, subcmd, opts, project, package=None):
166 """${cmd_name}: Initialize a directory as working copy
168 Initialize an existing directory to be a working copy of an
169 (already existing) buildservice project/package.
171 (This is the same as checking out a package and then copying sources
172 into the directory. It does NOT create a new package. To create a
173 package, use 'osc meta pkg ... ...')
175 You wouldn't normally use this command.
177 To get a working copy of a package (e.g. for building it or working on
178 it, you would normally use the checkout command. Use "osc help
179 checkout" to get help for it.
188 init_project_dir(conf.config['apiurl'], os.curdir, project)
189 print 'Initializing %s (Project: %s)' % (os.curdir, project)
191 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
192 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
198 @cmdln.option('-a', '--arch', metavar='ARCH',
199 help='specify architecture (only for binaries)')
200 @cmdln.option('-r', '--repo', metavar='REPO',
201 help='specify repository (only for binaries)')
202 @cmdln.option('-b', '--binaries', action='store_true',
203 help='list built binaries instead of sources')
204 @cmdln.option('-R', '--revision', metavar='REVISION',
205 help='specify revision (only for sources)')
206 @cmdln.option('-e', '--expand', action='store_true',
207 help='expand linked package (only for sources)')
208 @cmdln.option('-u', '--unexpand', action='store_true',
209 help='always work with unexpanded (source) packages')
210 @cmdln.option('-v', '--verbose', action='store_true',
211 help='print extra information')
212 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
213 help='print extra information')
214 @cmdln.option('-D', '--deleted', action='store_true',
215 help='show only the former deleted projects or packages')
216 def do_list(self, subcmd, opts, *args):
217 """${cmd_name}: List sources or binaries on the server
219 Examples for listing sources:
220 ls # list all projects
221 ls PROJECT # list packages in a project
222 ls PROJECT PACKAGE # list source files of package of a project
223 ls PROJECT PACKAGE <file> # list <file> if this file exists
224 ls -v PROJECT PACKAGE # verbosely list source files of package
225 ls -l PROJECT PACKAGE # verbosely list source files of package
226 ll PROJECT PACKAGE # verbosely list source files of package
227 LL PROJECT PACKAGE # verbosely list source files of expanded link
229 With --verbose, the following fields will be shown for each item:
231 Revision number of the last commit
233 Date and time of the last commit
235 Examples for listing binaries:
236 ls -b PROJECT # list all binaries of a project
237 ls -b PROJECT -a ARCH # list ARCH binaries of a project
238 ls -b PROJECT -r REPO # list binaries in REPO
239 ls -b PROJECT PACKAGE REPO ARCH
242 ${cmd_name} [PROJECT [PACKAGE]]
243 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
247 apiurl = conf.config['apiurl']
248 args = slash_split(args)
251 if subcmd == 'lL' or subcmd == 'LL':
263 raise oscerr.WrongArgs("Too many arguments when listing deleted packages")
266 raise oscerr.WrongArgs("Too many arguments when listing deleted packages")
269 if opts.repo != args[2]:
270 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
277 if not opts.binaries:
278 raise oscerr.WrongArgs('Too many arguments')
280 if opts.arch != args[3]:
281 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
286 if opts.binaries and opts.expand:
287 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
291 # ls -b toplevel doesn't make sense, so use info from
292 # current dir if available
295 if is_project_dir(dir):
296 project = store_read_project(dir)
297 elif is_package_dir(dir):
298 project = store_read_project(dir)
299 package = store_read_package(dir)
301 apiurl = self.get_api_url()
304 raise oscerr.WrongArgs('There are no binaries to list above project level.')
306 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
310 if opts.repo and opts.arch:
311 repos.append(Repo(opts.repo, opts.arch))
312 elif opts.repo and not opts.arch:
313 repos = [repo for repo in get_repos_of_project(apiurl, project) if repo.name == opts.repo]
314 elif opts.arch and not opts.repo:
315 repos = [repo for repo in get_repos_of_project(apiurl, project) if repo.arch == opts.arch]
317 repos = get_repos_of_project(apiurl, project)
321 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
323 for result in results:
326 print '%s/%s' % (result[0].name, result[0].arch)
331 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
337 elif not opts.binaries:
339 print '\n'.join(meta_get_project_list(conf.config['apiurl'], opts.deleted))
343 if self.options.verbose:
344 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
346 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
348 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project, opts.deleted))
350 elif len(args) == 2 or len(args) == 3:
352 print_not_found = True
355 l = meta_get_filelist(conf.config['apiurl'],
358 verbose=opts.verbose,
361 link_seen = '_link' in l
363 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
364 for i in l if not fname or fname == i.name ]
366 print_not_found = False
371 print_not_found = False
374 if opts.expand or opts.unexpand or not link_seen: break
375 m = show_files_meta(conf.config['apiurl'], project, package)
377 li.read(ET.fromstring(''.join(m)).find('linkinfo'))
379 raise oscerr.LinkExpandError(project, package, li.error)
380 project, package, rev = li.project, li.package, li.rev
382 print '# -> %s %s (%s)' % (project, package, rev)
384 print '# -> %s %s (latest)' % (project, package)
386 if fname and print_not_found:
387 print 'file \'%s\' does not exist' % fname
390 @cmdln.option('-f', '--force', action='store_true',
391 help='force generation of new patchinfo file')
392 @cmdln.option('--force-update', action='store_true',
393 help='drops away collected packages from an already built patch and let it collect again')
394 def do_patchinfo(self, subcmd, opts, *args):
395 """${cmd_name}: Generate and edit a patchinfo file.
397 A patchinfo file describes the packages for an update and the kind of
402 osc patchinfo PATCH_NAME
406 project_dir = localdir = os.getcwd()
407 if is_project_dir(localdir):
408 project = store_read_project(localdir)
409 apiurl = self.get_api_url()
411 sys.exit('This command must be called in a checked out project.')
413 for p in meta_get_packagelist(apiurl, project):
414 if p.startswith("_patchinfo:"):
417 if opts.force or not patchinfo:
418 print "Creating initial patchinfo..."
419 query='cmd=createpatchinfo'
421 query += "&name=" + args[0]
422 url = makeurl(apiurl, ['source', project], query=query)
424 for p in meta_get_packagelist(apiurl, project):
425 if p.startswith("_patchinfo:"):
428 if not os.path.exists(project_dir + "/" + patchinfo):
429 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
431 filename = project_dir + "/" + patchinfo + "/_patchinfo"
435 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
436 help='affect only a given attribute')
437 @cmdln.option('--attribute-defaults', action='store_true',
438 help='include defined attribute defaults')
439 @cmdln.option('--attribute-project', action='store_true',
440 help='include project values, if missing in packages ')
441 @cmdln.option('-F', '--file', metavar='FILE',
442 help='read metadata from FILE, instead of opening an editor. '
443 '\'-\' denotes standard input. ')
444 @cmdln.option('-e', '--edit', action='store_true',
445 help='edit metadata')
446 @cmdln.option('-c', '--create', action='store_true',
447 help='create attribute without values')
448 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
449 help='set attribute values')
450 @cmdln.option('--delete', action='store_true',
451 help='delete a pattern or attribute')
452 def do_meta(self, subcmd, opts, *args):
453 """${cmd_name}: Show meta information, or edit it
455 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
457 This command displays metadata on buildservice objects like projects,
458 packages, or users. The type of metadata is specified by the word after
459 "meta", like e.g. "meta prj".
461 prj denotes metadata of a buildservice project.
462 prjconf denotes the (build) configuration of a project.
463 pkg denotes metadata of a buildservice package.
464 user denotes the metadata of a user.
465 pattern denotes installation patterns defined for a project.
467 To list patterns, use 'osc meta pattern PRJ'. An additional argument
468 will be the pattern file to view or edit.
470 With the --edit switch, the metadata can be edited. Per default, osc
471 opens the program specified by the environmental variable EDITOR with a
472 temporary file. Alternatively, content to be saved can be supplied via
473 the --file switch. If the argument is '-', input is taken from stdin:
474 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
476 When trying to edit a non-existing resource, it is created implicitly.
482 osc meta pkg PRJ PKG -e
483 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
486 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
487 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
488 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
489 osc meta pattern --delete PRJ PATTERN
493 args = slash_split(args)
495 if not args or args[0] not in metatypes.keys():
496 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
497 % ', '.join(metatypes))
502 apiurl = self.get_api_url()
505 min_args, max_args = 0, 2
506 elif cmd in ['pattern']:
507 min_args, max_args = 1, 2
508 elif cmd in ['attribute']:
509 min_args, max_args = 1, 3
510 elif cmd in ['prj', 'prjconf']:
511 min_args, max_args = 0, 1
513 min_args, max_args = 1, 1
515 if len(args) < min_args:
516 raise oscerr.WrongArgs('Too few arguments.')
517 if len(args) > max_args:
518 raise oscerr.WrongArgs('Too many arguments.')
522 if cmd in ['pkg', 'prj', 'prjconf' ]:
524 project = store_read_project(os.curdir)
530 package = store_read_package(os.curdir)
534 elif cmd == 'attribute':
540 if opts.attribute_project:
541 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
546 attributepath.append('source')
547 attributepath.append(project)
549 attributepath.append(package)
551 attributepath.append(subpackage)
552 attributepath.append('_attribute')
555 elif cmd == 'pattern':
561 # enforce pattern argument if needed
562 if opts.edit or opts.file:
563 raise oscerr.WrongArgs('A pattern file argument is required.')
566 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
568 sys.stdout.write(''.join(show_project_meta(apiurl, project)))
570 sys.stdout.write(''.join(show_package_meta(apiurl, project, package)))
571 elif cmd == 'attribute':
572 sys.stdout.write(''.join(show_attribute_meta(apiurl, project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
573 elif cmd == 'prjconf':
574 sys.stdout.write(''.join(show_project_conf(apiurl, project)))
576 r = get_user_meta(apiurl, user)
578 sys.stdout.write(''.join(r))
579 elif cmd == 'pattern':
581 r = show_pattern_meta(apiurl, project, pattern)
583 sys.stdout.write(''.join(r))
585 r = show_pattern_metalist(apiurl, project)
587 sys.stdout.write('\n'.join(r) + '\n')
590 if opts.edit and not opts.file:
592 edit_meta(metatype='prj',
594 path_args=quote_plus(project),
598 'user': conf.config['user']}))
600 edit_meta(metatype='pkg',
602 path_args=(quote_plus(project), quote_plus(package)),
606 'user': conf.config['user']}))
607 elif cmd == 'prjconf':
608 edit_meta(metatype='prjconf',
610 path_args=quote_plus(project),
614 edit_meta(metatype='user',
616 path_args=(quote_plus(user)),
618 template_args=({'user': user}))
619 elif cmd == 'pattern':
620 edit_meta(metatype='pattern',
622 path_args=(project, pattern),
626 # create attribute entry
627 if (opts.create or opts.set) and cmd == 'attribute':
628 if not opts.attribute:
629 raise oscerr.WrongOptions('no attribute given to create')
632 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
633 for i in opts.set.split(','):
634 values += '<value>%s</value>' % i
635 aname = opts.attribute.split(":")
636 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
637 url = makeurl(apiurl, attributepath)
638 for data in streamfile(url, http_POST, data=d):
639 sys.stdout.write(data)
648 f = open(opts.file).read()
650 sys.exit('could not open file \'%s\'.' % opts.file)
653 edit_meta(metatype='prj',
657 path_args=quote_plus(project))
659 edit_meta(metatype='pkg',
663 path_args=(quote_plus(project), quote_plus(package)))
664 elif cmd == 'prjconf':
665 edit_meta(metatype='prjconf',
669 path_args=quote_plus(project))
671 edit_meta(metatype='user',
675 path_args=(quote_plus(user)))
676 elif cmd == 'pattern':
677 edit_meta(metatype='pattern',
681 path_args=(project, pattern))
686 path = metatypes[cmd]['path']
688 path = path % (project, pattern)
689 u = makeurl(apiurl, [path])
691 elif cmd == 'attribute':
692 if not opts.attribute:
693 raise oscerr.WrongOptions('no attribute given to create')
694 attributepath.append(opts.attribute)
695 u = makeurl(apiurl, attributepath)
696 for data in streamfile(u, http_DELETE):
697 sys.stdout.write(data)
699 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
702 @cmdln.option('-m', '--message', metavar='TEXT',
703 help='specify message TEXT')
704 @cmdln.option('-r', '--revision', metavar='REV',
705 help='for "create", specify a certain source revision ID (the md5 sum)')
706 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
707 help='Superseding another request by this one')
708 @cmdln.option('--nodevelproject', action='store_true',
709 help='do not follow a defined devel project ' \
710 '(primary project where a package is developed)')
711 @cmdln.option('--cleanup', action='store_true',
712 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
713 @cmdln.option('--no-cleanup', action='store_true',
714 help='never remove source package on accept, but update its content')
715 @cmdln.option('--no-update', action='store_true',
716 help='never touch source package on accept (will break source links)')
717 @cmdln.option('-d', '--diff', action='store_true',
718 help='show diff only instead of creating the actual request')
719 @cmdln.option('--yes', action='store_true',
720 help='proceed without asking.')
722 @cmdln.alias("submitreq")
723 @cmdln.alias("submitpac")
724 def do_submitrequest(self, subcmd, opts, *args):
725 """${cmd_name}: Create request to submit source into another Project
727 [See http://en.opensuse.org/Build_Service/Collaboration for information
730 See the "request" command for showing and modifing existing requests.
733 osc submitreq [OPTIONS]
734 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
735 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
739 src_update = conf.config['submitrequest_on_accept_action'] or None
740 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
742 src_update = "cleanup"
743 elif opts.no_cleanup:
744 src_update = "update"
746 src_update = "noupdate"
748 args = slash_split(args)
750 # remove this block later again
751 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
752 if args and args[0] in oldcmds:
753 print "************************************************************************"
754 print "* WARNING: It looks that you are using this command with a *"
755 print "* deprecated syntax. *"
756 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
757 print "* to see the new syntax. *"
758 print "************************************************************************"
759 if args[0] == 'create':
765 raise oscerr.WrongArgs('Too many arguments.')
767 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
768 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
770 apiurl = self.get_api_url()
772 if len(args) == 0 and is_project_dir(os.getcwd()):
774 # submit requests for multiple packages are currently handled via multiple requests
775 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
776 project = store_read_project(os.curdir)
782 # loop via all packages for checking their state
783 for p in meta_get_packagelist(apiurl, project):
784 if p.startswith("_patchinfo:"):
787 # get _link info from server, that knows about the local state ...
788 u = makeurl(apiurl, ['source', project, p])
790 root = ET.parse(f).getroot()
791 linkinfo = root.find('linkinfo')
793 print "Package ", p, " is not a source link."
794 sys.exit("This is currently not supported.")
795 if linkinfo.get('error'):
796 print "Package ", p, " is a broken source link."
797 sys.exit("Please fix this first")
798 t = linkinfo.get('project')
800 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
801 # Real fix is to ask the api if sources are modificated
802 # but there is no such call yet.
803 targetprojects.append(t)
805 print "Submitting package ", p
807 print " Skipping package ", p
809 print "Skipping package ", p, " since it is a source link pointing inside the project."
813 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
814 print "\nEverything fine? Can we create the requests ? [y/n]"
815 if sys.stdin.read(1) != "y":
816 print >>sys.stderr, 'Aborted...'
817 raise oscerr.UserAbort()
819 # loop via all packages to do the action
821 result = create_submit_request(apiurl, project, p)
824 sys.exit("submit request creation failed")
825 sr_ids.append(result)
827 # create submit requests for all found patchinfos
831 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
834 for t in targetprojects:
835 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
836 (project, p, t, p, options_block)
840 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
841 (actionxml, cgi.escape(opts.message or ""))
842 u = makeurl(apiurl, ['request'], query='cmd=create')
843 f = http_POST(u, data=xml)
845 root = ET.parse(f).getroot()
846 sr_ids.append(root.get('id'))
848 print "Requests created: ",
851 sys.exit('Successfully finished')
854 # try using the working copy at hand
855 p = findpacs(os.curdir)[0]
856 src_project = p.prjname
859 if len(args) == 0 and p.islink():
860 dst_project = p.linkinfo.project
861 dst_package = p.linkinfo.package
863 dst_project = args[0]
865 dst_package = args[1]
867 dst_package = src_package
869 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
870 'Please provide it the target via commandline arguments.' % p.name)
872 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
873 if len(modified) > 0:
874 print 'Your working copy has local modifications.'
875 repl = raw_input('Proceed without committing the local changes? (y|N) ')
877 raise oscerr.UserAbort()
879 # get the arguments from the commandline
880 src_project, src_package, dst_project = args[0:3]
882 dst_package = args[3]
884 dst_package = src_package
886 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
887 + self.get_cmd_help('request'))
889 if not opts.nodevelproject:
892 devloc = show_develproject(apiurl, dst_project, dst_package)
893 except urllib2.HTTPError:
894 print >>sys.stderr, """\
895 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
896 % (dst_project, dst_package)
900 dst_project != devloc and \
901 src_project != devloc:
903 A different project, %s, is defined as the place where development
904 of the package %s primarily takes place.
905 Please submit there instead, or use --nodevelproject to force direct submission.""" \
906 % (devloc, dst_package)
911 if opts.diff or not opts.message:
913 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
914 rdiff += server_diff(apiurl,
915 dst_project, dst_package, opts.revision,
916 src_project, src_package, None, True)
922 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
923 user = conf.get_apiurl_usr(apiurl)
924 myreqs = [ i for i in reqs if i.state.who == user ]
927 print 'You already created the following submit request: %s.' % \
928 ', '.join([str(i.reqid) for i in myreqs ])
929 repl = raw_input('Supersede the old requests? (y/n/c) ')
930 if repl.lower() == 'c':
931 print >>sys.stderr, 'Aborting'
932 raise oscerr.UserAbort()
937 changes_re = re.compile(r'^--- .*\.changes ')
938 for line in rdiff.split('\n'):
939 if line.startswith('--- '):
940 if changes_re.match(line):
945 difflines.append(line)
946 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
948 result = create_submit_request(apiurl,
949 src_project, src_package,
950 dst_project, dst_package,
951 opts.message, orev=opts.revision, src_update=src_update)
952 if repl.lower() == 'y':
954 change_request_state(apiurl, str(req.reqid), 'superseded',
955 'superseded by %s' % result, result)
958 r = change_request_state(conf.config['apiurl'],
959 opts.supersede, 'superseded', opts.message or '', result)
961 print 'created request id', result
963 def _actionparser(self, opt_str, value, parser):
965 if not hasattr(parser.values, 'actiondata'):
966 setattr(parser.values, 'actiondata', [])
967 if parser.values.actions == None:
968 parser.values.actions = []
973 if ((arg[:2] == "--" and len(arg) > 2) or
974 (arg[:1] == "-" and len(arg) > 1 and arg[1] != "-")):
980 parser.values.actions.append(value[0])
982 parser.values.actiondata.append(value)
984 def _submit_request(self, args, opts, options_block):
986 apiurl = self.get_api_url()
987 if len(args) == 0 and is_project_dir(os.getcwd()):
989 # submit requests for multiple packages are currently handled via multiple requests
990 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
991 project = store_read_project(os.curdir)
997 # loop via all packages for checking their state
998 for p in meta_get_packagelist(apiurl, project):
999 if p.startswith("_patchinfo:"):
1002 # get _link info from server, that knows about the local state ...
1003 u = makeurl(apiurl, ['source', project, p])
1005 root = ET.parse(f).getroot()
1006 linkinfo = root.find('linkinfo')
1007 if linkinfo == None:
1008 print "Package ", p, " is not a source link."
1009 sys.exit("This is currently not supported.")
1010 if linkinfo.get('error'):
1011 print "Package ", p, " is a broken source link."
1012 sys.exit("Please fix this first")
1013 t = linkinfo.get('project')
1017 rdiff = server_diff(apiurl, t, p, opts.revision, project, p, None, True)
1022 targetprojects.append(t)
1024 rdiffmsg.append("old: %s/%s\nnew: %s/%s\n%s" %(t, p, project, p,rdiff))
1026 print "Skipping package ", p, " since it has no difference with the target package."
1028 print "Skipping package ", p, " since it is a source link pointing inside the project."
1030 print ''.join(rdiffmsg)
1035 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
1036 print "\nEverything fine? Can we create the requests ? [y/n]"
1037 if sys.stdin.read(1) != "y":
1038 sys.exit("Aborted...")
1040 # loop via all packages to do the action
1042 s = """<action type="submit"> <source project="%s" package="%s" rev="%s"/> <target project="%s" package="%s"/> %s </action>""" % \
1043 (project, p, opts.revision or show_upstream_rev(apiurl, project, p), t, p, options_block)
1046 # create submit requests for all found patchinfos
1048 for t in targetprojects:
1049 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
1050 (project, p, t, p, options_block)
1055 elif len(args) <= 2:
1056 # try using the working copy at hand
1057 p = findpacs(os.curdir)[0]
1058 src_project = p.prjname
1059 src_package = p.name
1060 if len(args) == 0 and p.islink():
1061 dst_project = p.linkinfo.project
1062 dst_package = p.linkinfo.package
1064 dst_project = args[0]
1066 dst_package = args[1]
1068 dst_package = src_package
1070 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
1071 'Please provide it the target via commandline arguments.' % p.name)
1073 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
1074 if len(modified) > 0:
1075 print 'Your working copy has local modifications.'
1076 repl = raw_input('Proceed without committing the local changes? (y|N) ')
1079 elif len(args) >= 3:
1080 # get the arguments from the commandline
1081 src_project, src_package, dst_project = args[0:3]
1083 dst_package = args[3]
1085 dst_package = src_package
1087 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1088 + self.get_cmd_help('request'))
1090 if not opts.nodevelproject:
1093 devloc = show_develproject(apiurl, dst_project, dst_package)
1094 except urllib2.HTTPError:
1095 print >>sys.stderr, """\
1096 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
1097 % (dst_project, dst_package)
1101 dst_project != devloc and \
1102 src_project != devloc:
1104 A different project, %s, is defined as the place where development
1105 of the package %s primarily takes place.
1106 Please submit there instead, or use --nodevelproject to force direct submission.""" \
1107 % (devloc, dst_package)
1114 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
1115 rdiff += server_diff(apiurl,
1116 dst_project, dst_package, opts.revision,
1117 src_project, src_package, None, True)
1123 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
1124 user = conf.get_apiurl_usr(apiurl)
1125 myreqs = [ i for i in reqs if i.state.who == user ]
1128 print 'You already created the following submit request: %s.' % \
1129 ', '.join([str(i.reqid) for i in myreqs ])
1130 repl = raw_input('Supersede the old requests? (y/n/c) ')
1131 if repl.lower() == 'c':
1132 print >>sys.stderr, 'Aborting'
1135 actionxml = """<action type="submit"> <source project="%s" package="%s" rev="%s"/> <target project="%s" package="%s"/> %s </action>""" % \
1136 (src_project, src_package, opts.revision or show_upstream_rev(apiurl, src_project, src_package), dst_project, dst_package, options_block)
1137 if repl.lower() == 'y':
1139 change_request_state(apiurl, str(req.reqid), 'superseded',
1140 'superseded by %s' % result, result)
1143 r = change_request_state(apiurl,
1144 opts.supersede, 'superseded', '', result)
1146 #print 'created request id', result
1149 def _delete_request(self, args, opts):
1151 raise oscerr.WrongArgs('Please specify at least a project.')
1153 raise oscerr.WrongArgs('Too many arguments.')
1157 package = """package="%s" """ % (args[1])
1158 actionxml = """<action type="delete"> <target project="%s" %s/> </action> """ % (args[0], package)
1161 def _changedevel_request(self, args, opts):
1163 raise oscerr.WrongArgs('Too many arguments.')
1165 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
1167 devel_project = store_read_project(wd)
1168 devel_package = package = store_read_package(wd)
1169 project = conf.config['getpac_default_project']
1172 raise oscerr.WrongArgs('Too few arguments.')
1174 devel_project = args[2]
1177 devel_package = package
1179 devel_package = args[3]
1181 actionxml = """ <action type="change_devel"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> </action> """ % \
1182 (devel_project, devel_package, project, package)
1186 def _add_role(self, args, opts):
1188 raise oscerr.WrongArgs('Too many arguments.')
1190 raise oscerr.WrongArgs('Too few arguments.')
1192 apiurl = self.get_api_url()
1200 if get_user_meta(apiurl, user) == None:
1201 raise oscerr.WrongArgs('osc: an error occured.')
1203 actionxml = """ <action type="add_role"> <target project="%s" package="%s" /> <person name="%s" role="%s" /> </action> """ % \
1204 (project, package, user, role)
1208 def _set_bugowner(self, args, opts):
1210 raise oscerr.WrongArgs('Too many arguments.')
1212 raise oscerr.WrongArgs('Too few arguments.')
1214 apiurl = self.get_api_url()
1221 if get_user_meta(apiurl, user) == None:
1222 raise oscerr.WrongArgs('osc: an error occured.')
1224 actionxml = """ <action type="set_bugowner"> <target project="%s" package="%s" /> <person name="%s" /> </action> """ % \
1225 (project, package, user)
1229 @cmdln.option('-a', '--action', action='callback', callback = _actionparser,dest = 'actions',
1230 help='specify action type of a request, can be : submit/delete/change_devel/add_role/set_bugowner')
1231 @cmdln.option('-m', '--message', metavar='TEXT',
1232 help='specify message TEXT')
1233 @cmdln.option('-r', '--revision', metavar='REV',
1234 help='for "create", specify a certain source revision ID (the md5 sum)')
1235 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
1236 help='Superseding another request by this one')
1237 @cmdln.option('--nodevelproject', action='store_true',
1238 help='do not follow a defined devel project ' \
1239 '(primary project where a package is developed)')
1240 @cmdln.option('--cleanup', action='store_true',
1241 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
1242 @cmdln.option('--no-cleanup', action='store_true',
1243 help='never remove source package on accept, but update its content')
1244 @cmdln.option('--no-update', action='store_true',
1245 help='never touch source package on accept (will break source links)')
1246 @cmdln.option('-d', '--diff', action='store_true',
1247 help='show diff only instead of creating the actual request')
1248 @cmdln.option('--yes', action='store_true',
1249 help='proceed without asking.')
1250 @cmdln.alias("creq")
1251 def do_createrequest(self, subcmd, opts, *args):
1252 """${cmd_name}: create multiple requests with a single command
1255 osc creq [OPTIONS] [
1256 -a submit SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
1257 -a delete PROJECT [PACKAGE]
1258 -a change_devel PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
1259 -a add_role USER ROLE PROJECT [PACKAGE]
1260 -a set_bugowner USER PROJECT [PACKAGE]
1263 Option -m works for all types of request, the rest work only for submit.
1265 osc creq -a submit -a delete home:someone:branches:openSUSE:Tools -a change_devel openSUSE:Tools osc home:someone:branches:openSUSE:Tools -m ok
1267 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.
1270 src_update = conf.config['submitrequest_on_accept_action'] or None
1271 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
1273 src_update = "cleanup"
1274 elif opts.no_cleanup:
1275 src_update = "update"
1276 elif opts.no_update:
1277 src_update = "noupdate"
1281 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
1283 args = slash_split(args)
1285 apiurl = self.get_api_url()
1289 for ai in opts.actions:
1291 args = opts.actiondata[i]
1293 actionsxml += self._submit_request(args,opts, options_block)
1294 elif ai == 'delete':
1295 args = opts.actiondata[i]
1296 actionsxml += self._delete_request(args,opts)
1298 elif ai == 'change_devel':
1299 args = opts.actiondata[i]
1300 actionsxml += self._changedevel_request(args,opts)
1302 elif ai == 'add_role':
1303 args = opts.actiondata[i]
1304 actionsxml += self._add_role(args,opts)
1306 elif ai == 'set_bugowner':
1307 args = opts.actiondata[i]
1308 actionsxml += self._set_bugowner(args,opts)
1311 raise oscerr.WrongArgs('Unsupported action %s' % ai)
1312 if actionsxml == "":
1313 sys.exit('No actions need to be taken.')
1315 if not opts.message:
1316 opts.message = edit_message()
1319 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
1320 (actionsxml, cgi.escape(opts.message or ""))
1321 u = makeurl(apiurl, ['request'], query='cmd=create')
1322 f = http_POST(u, data=xml)
1324 root = ET.parse(f).getroot()
1325 return root.get('id')
1328 @cmdln.option('-m', '--message', metavar='TEXT',
1329 help='specify message TEXT')
1331 @cmdln.alias("deletereq")
1332 def do_deleterequest(self, subcmd, opts, *args):
1333 """${cmd_name}: Create request to delete a package or project
1337 osc deletereq [-m TEXT] PROJECT [PACKAGE]
1341 args = slash_split(args)
1344 raise oscerr.WrongArgs('Please specify at least a project.')
1346 raise oscerr.WrongArgs('Too many arguments.')
1348 apiurl = conf.config['apiurl']
1355 if not opts.message:
1356 opts.message = edit_message()
1358 result = create_delete_request(apiurl, project, package, opts.message)
1362 @cmdln.option('-m', '--message', metavar='TEXT',
1363 help='specify message TEXT')
1365 @cmdln.alias("changedevelreq")
1366 def do_changedevelrequest(self, subcmd, opts, *args):
1367 """${cmd_name}: Create request to change the devel package definition.
1369 [See http://en.opensuse.org/Build_Service/Collaboration for information
1372 See the "request" command for showing and modifing existing requests.
1374 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
1378 raise oscerr.WrongArgs('Too many arguments.')
1380 apiurl = self.get_api_url()
1382 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
1384 devel_project = store_read_project(wd)
1385 devel_package = package = store_read_package(wd)
1386 project = conf.config['getpac_default_project']
1389 raise oscerr.WrongArgs('Too few arguments.')
1391 devel_project = args[2]
1394 devel_package = package
1396 devel_package = args[3]
1398 if not opts.message:
1400 footer=textwrap.TextWrapper(width = 66).fill(
1401 'please explain why you like to change the devel project of %s/%s to %s/%s'
1402 % (project,package,devel_project,devel_package))
1403 opts.message = edit_message(footer)
1405 result = create_change_devel_request(apiurl,
1406 devel_project, devel_package,
1412 @cmdln.option('-d', '--diff', action='store_true',
1413 help='generate a diff')
1414 @cmdln.option('-u', '--unified', action='store_true',
1415 help='output the diff in the unified diff format')
1416 @cmdln.option('-m', '--message', metavar='TEXT',
1417 help='specify message TEXT')
1418 @cmdln.option('-t', '--type', metavar='TYPE',
1419 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1420 @cmdln.option('-a', '--all', action='store_true',
1421 help='all states. Same as\'-s all\'')
1422 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1423 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]')
1424 @cmdln.option('-D', '--days', metavar='DAYS',
1425 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1426 @cmdln.option('-U', '--user', metavar='USER',
1427 help='same as -M, but for the specified USER')
1428 @cmdln.option('-b', '--brief', action='store_true', default=False,
1429 help='print output in list view as list subcommand')
1430 @cmdln.option('-M', '--mine', action='store_true',
1431 help='only show requests created by yourself')
1432 @cmdln.option('-B', '--bugowner', action='store_true',
1433 help='also show requests about packages where I am bugowner')
1434 @cmdln.option('-i', '--interactive', action='store_true',
1435 help='interactive review of request')
1436 @cmdln.option('--non-interactive', action='store_true',
1437 help='non-interactive review of request')
1438 @cmdln.option('--exclude-target-project', action='append',
1439 help='exclude target project from request list')
1440 @cmdln.option('--involved-projects', action='store_true',
1441 help='show all requests for project/packages where USER is involved')
1443 @cmdln.alias("review")
1444 def do_request(self, subcmd, opts, *args):
1445 """${cmd_name}: Show and modify requests
1447 [See http://en.opensuse.org/Build_Service/Collaboration for information
1450 This command shows and modifies existing requests. To create new requests
1451 you need to call one of the following:
1454 osc changedevelrequest
1455 To send low level requests to the buildservice API, use:
1458 This command has the following sub commands:
1460 "list" lists open requests attached to a project or package or person.
1461 Uses the project/package of the current directory if none of
1462 -M, -U USER, project/package are given.
1464 "log" will show the history of the given ID
1466 "show" will show the request itself, and generate a diff for review, if
1467 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1469 "decline" will change the request state to "declined" and append a
1470 message that you specify with the --message option.
1472 "wipe" will permanently delete a request.
1474 "revoke" will set the request state to "revoked" and append a
1475 message that you specify with the --message option.
1477 "accept" will change the request state to "accepted" and will trigger
1478 the actual submit process. That would normally be a server-side copy of
1479 the source package to the target package.
1481 "checkout" will checkout the request's source package. This only works for "submit" requests.
1484 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1486 osc request [show] [-d] [-b] ID
1487 osc request accept [-m TEXT] ID
1488 osc request reopen [-m TEXT] ID
1489 osc request approvenew [-m TEXT] PROJECT
1490 osc request decline [-m TEXT] ID
1491 osc request revoke [-m TEXT] ID
1493 osc request checkout/co ID
1494 osc review accept [-m TEXT] ID
1495 osc review decline [-m TEXT] ID
1496 osc review new [-m TEXT] ID # for setting a temporary comment without changing the state
1500 args = slash_split(args)
1502 if opts.all and opts.state:
1503 raise oscerr.WrongOptions('Sorry, the options \'--all\' and \'--state\' ' \
1504 'are mutually exclusive.')
1505 if opts.mine and opts.user:
1506 raise oscerr.WrongOptions('Sorry, the options \'--user\' and \'--mine\' ' \
1507 'are mutually exclusive.')
1508 if opts.interactive and opts.non_interactive:
1509 raise oscerr.WrongOptions('Sorry, the options \'--interactive\' and ' \
1510 '\'--non-interactive\' are mutually exclusive')
1515 if opts.state == '':
1518 if opts.state == '':
1521 cmds = ['list', 'log', 'show', 'decline', 'reopen', 'accept', 'approvenew', 'wipe', 'revoke', 'checkout', 'co', 'help']
1522 if not args or args[0] not in cmds:
1523 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1524 % (args[0],', '.join(cmds)))
1530 return self.do_help(['help', 'request'])
1533 min_args, max_args = 0, 2
1535 min_args, max_args = 1, 1
1536 if len(args) < min_args:
1537 raise oscerr.WrongArgs('Too few arguments.')
1538 if len(args) > max_args:
1539 raise oscerr.WrongArgs('Too many arguments.')
1541 apiurl = self.get_api_url()
1543 if cmd == 'list' or cmd == 'approvenew':
1548 elif not opts.mine and not opts.user:
1550 project = store_read_project(os.curdir)
1551 package = store_read_package(os.curdir)
1552 except oscerr.NoWorkingCopy:
1557 elif cmd in ['log', 'show', 'decline', 'reopen', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1560 # list and approvenew
1561 if cmd == 'list' or cmd == 'approvenew':
1562 states = ('new', 'accepted', 'revoked', 'declined')
1564 if cmd == 'approvenew':
1566 results = get_request_list(apiurl, project, package, '', ['new'])
1568 state_list = opts.state.split(',')
1569 if opts.state == 'all':
1570 state_list = ['all']
1572 for s in state_list:
1574 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1576 who = conf.get_apiurl_usr(apiurl)
1580 state_list = ['all']
1582 ## FIXME -B not implemented!
1584 if (self.options.debug):
1585 print 'list: option --bugowner ignored: not impl.'
1587 if opts.involved_projects:
1588 who = who or conf.get_apiurl_usr(apiurl)
1589 results = get_user_projpkgs_request_list(apiurl, who, req_state=state_list,
1590 req_type=opts.type, exclude_projects=opts.exclude_target_project or [])
1592 results = get_request_list(apiurl, project, package, who,
1593 state_list, opts.type, opts.exclude_target_project or [])
1595 results.sort(reverse=True)
1597 days = opts.days or conf.config['request_list_days']
1604 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1607 ## bs has received 2009-09-20 a new xquery compare() function
1608 ## which allows us to limit the list inside of get_request_list
1609 ## That would be much faster for coolo. But counting the remainder
1610 ## would not be possible with current xquery implementation.
1611 ## Workaround: fetch all, and filter on client side.
1613 ## FIXME: date filtering should become implemented on server side
1614 for result in results:
1615 if days == 0 or result.state.when > since or result.state.name == 'new':
1616 print result.list_view()
1620 print "There are %d requests older than %s days.\n" % (skipped, days)
1622 if cmd == 'approvenew':
1623 print "\n *** Approve them all ? [y/n] ***"
1624 if sys.stdin.read(1) == "y":
1626 if not opts.message:
1627 opts.message = edit_message()
1628 for result in results:
1629 print result.reqid, ": ",
1630 r = change_request_state(conf.config['apiurl'],
1631 str(result.reqid), 'accepted', opts.message or '')
1634 print >>sys.stderr, 'Aborted...'
1635 raise oscerr.UserAbort()
1638 for l in get_request_log(conf.config['apiurl'], reqid):
1643 r = get_request(conf.config['apiurl'], reqid)
1646 elif (opts.interactive or conf.config['request_show_interactive']) and not opts.non_interactive:
1647 return request_interactive_review(conf.config['apiurl'], r)
1650 # fixme: will inevitably fail if the given target doesn't exist
1651 if opts.diff and r.actions[0].type != 'submit':
1652 raise oscerr.WrongOptions('\'--diff\' is not possible for request type: \'%s\'' % r.actions[0].type)
1655 print server_diff(conf.config['apiurl'],
1656 r.actions[0].dst_project, r.actions[0].dst_package, None,
1657 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified, True)
1658 except urllib2.HTTPError, e:
1660 e.osc_msg = 'Diff not possible'
1662 # backward compatiblity: only a recent api/backend supports the missingok parameter
1664 print server_diff(conf.config['apiurl'],
1665 r.actions[0].dst_project, r.actions[0].dst_package, None,
1666 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified, False)
1667 except urllib2.HTTPError, e:
1668 e.osc_msg = 'Diff not possible'
1672 elif cmd == 'checkout' or cmd == 'co':
1673 r = get_request(conf.config['apiurl'], reqid)
1674 submits = [ i for i in r.actions if i.type == 'submit' ]
1675 if not len(submits):
1676 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1677 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1678 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1681 if not opts.message:
1682 opts.message = edit_message()
1683 state_map = {'reopen' : 'new', 'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1684 # Change review state only
1685 if subcmd == 'review':
1686 if cmd in ['accept', 'decline', 'new']:
1687 r = change_review_state(conf.config['apiurl'],
1688 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1690 # Change state of entire request
1691 elif cmd in ['reopen', 'accept', 'decline', 'wipe', 'revoke']:
1692 r = change_request_state(conf.config['apiurl'],
1693 reqid, state_map[cmd], opts.message or '')
1696 # editmeta and its aliases are all depracated
1697 @cmdln.alias("editprj")
1698 @cmdln.alias("createprj")
1699 @cmdln.alias("editpac")
1700 @cmdln.alias("createpac")
1701 @cmdln.alias("edituser")
1702 @cmdln.alias("usermeta")
1704 def do_editmeta(self, subcmd, opts, *args):
1707 Obsolete command to edit metadata. Use 'meta' now.
1709 See the help output of 'meta'.
1713 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1714 print >>sys.stderr, 'See \'osc help meta\'.'
1715 #self.do_help([None, 'meta'])
1719 @cmdln.option('-r', '--revision', metavar='rev',
1720 help='use the specified revision.')
1721 @cmdln.option('-u', '--unset', action='store_true',
1722 help='remove revision in link, it will point always to latest revision')
1723 def do_setlinkrev(self, subcmd, opts, *args):
1724 """${cmd_name}: Updates a revision number in a source link.
1726 This command adds or updates a specified revision number in a source link.
1727 The current revision of the source is used, if no revision number is specified.
1731 osc setlinkrev PROJECT [PACKAGE]
1735 args = slash_split(args)
1736 apiurl = conf.config['apiurl']
1739 p = findpacs(os.curdir)[0]
1744 sys.exit('Local directory is no checked out source link package, aborting')
1745 elif len(args) == 2:
1748 elif len(args) == 1:
1751 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1752 + self.get_cmd_help('setlinkrev'))
1755 packages = [ package ]
1757 packages = meta_get_packagelist(apiurl, project)
1760 print "setting revision for package", p
1764 rev, dummy = parseRevisionOption(opts.revision)
1765 set_link_rev(apiurl, project, p, rev)
1768 def do_linktobranch(self, subcmd, opts, *args):
1769 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1771 This command tells the server to convert a _link with or without a project.diff
1772 to a branch. This is a full copy with a _link file pointing to the branched place.
1775 osc linktobranch # can be used in checked out package
1776 osc linktobranch PROJECT PACKAGE
1779 args = slash_split(args)
1780 apiurl = self.get_api_url()
1784 project = store_read_project(wd)
1785 package = store_read_package(wd)
1786 update_local_dir = True
1788 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1790 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1794 update_local_dir = False
1797 link_to_branch(apiurl, project, package)
1798 if update_local_dir:
1800 pac.update(rev=pac.latest_rev())
1803 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1804 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1805 @cmdln.option('-c', '--current', action='store_true',
1806 help='link fixed against current revision.')
1807 @cmdln.option('-r', '--revision', metavar='rev',
1808 help='link the specified revision.')
1809 @cmdln.option('-f', '--force', action='store_true',
1810 help='overwrite an existing link file if it is there.')
1811 @cmdln.option('-d', '--disable-publish', action='store_true',
1812 help='disable publishing of the linked package')
1813 def do_linkpac(self, subcmd, opts, *args):
1814 """${cmd_name}: "Link" a package to another package
1816 A linked package is a clone of another package, but plus local
1817 modifications. It can be cross-project.
1819 The DESTPAC name is optional; the source packages' name will be used if
1822 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1824 To add a patch, add the patch as file and add it to the _link file.
1825 You can also specify text which will be inserted at the top of the spec file.
1827 See the examples in the _link file.
1830 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1834 args = slash_split(args)
1836 if not args or len(args) < 3:
1837 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1838 + self.get_cmd_help('linkpac'))
1840 rev, dummy = parseRevisionOption(opts.revision)
1842 src_project = args[0]
1843 src_package = args[1]
1844 dst_project = args[2]
1846 dst_package = args[3]
1848 dst_package = src_package
1850 if src_project == dst_project and src_package == dst_package:
1851 raise oscerr.WrongArgs('Error: source and destination are the same.')
1853 if src_project == dst_project and not opts.cicount:
1854 # in this case, the user usually wants to build different spec
1855 # files from the same source
1856 opts.cicount = "copy"
1859 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1861 if rev and not checkRevision(src_project, src_package, rev):
1862 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1865 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1867 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1868 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1869 @cmdln.option('-d', '--disable-publish', action='store_true',
1870 help='disable publishing of the aggregated package')
1871 def do_aggregatepac(self, subcmd, opts, *args):
1872 """${cmd_name}: "Aggregate" a package to another package
1874 Aggregation of a package means that the build results (binaries) of a
1875 package are basically copied into another project.
1876 This can be used to make packages available from building that are
1877 needed in a project but available only in a different project. Note
1878 that this is done at the expense of disk space. See
1879 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1880 for more information.
1882 The DESTPAC name is optional; the source packages' name will be used if
1886 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1890 args = slash_split(args)
1892 if not args or len(args) < 3:
1893 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1894 + self.get_cmd_help('aggregatepac'))
1896 src_project = args[0]
1897 src_package = args[1]
1898 dst_project = args[2]
1900 dst_package = args[3]
1902 dst_package = src_package
1904 if src_project == dst_project and src_package == dst_package:
1905 raise oscerr.WrongArgs('Error: source and destination are the same.')
1909 for pair in opts.map_repo.split(','):
1910 src_tgt = pair.split('=')
1911 if len(src_tgt) != 2:
1912 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1913 repo_map[src_tgt[0]] = src_tgt[1]
1915 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1918 @cmdln.option('-c', '--client-side-copy', action='store_true',
1919 help='do a (slower) client-side copy')
1920 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1921 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1922 @cmdln.option('-d', '--keep-develproject', action='store_true',
1923 help='keep develproject tag in the package metadata')
1924 @cmdln.option('-r', '--revision', metavar='rev',
1925 help='link the specified revision.')
1926 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1927 help='URL of destination api server. Default is the source api server.')
1928 @cmdln.option('-m', '--message', metavar='TEXT',
1929 help='specify message TEXT')
1930 @cmdln.option('-e', '--expand', action='store_true',
1931 help='if the source package is a link then copy the expanded version of the link')
1932 def do_copypac(self, subcmd, opts, *args):
1933 """${cmd_name}: Copy a package
1935 A way to copy package to somewhere else.
1937 It can be done across buildservice instances, if the -t option is used.
1938 In that case, a client-side copy is implied.
1940 Using --client-side-copy always involves downloading all files, and
1941 uploading them to the target.
1943 The DESTPAC name is optional; the source packages' name will be used if
1947 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1951 args = slash_split(args)
1953 if not args or len(args) < 3:
1954 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1955 + self.get_cmd_help('copypac'))
1957 src_project = args[0]
1958 src_package = args[1]
1959 dst_project = args[2]
1961 dst_package = args[3]
1963 dst_package = src_package
1965 src_apiurl = conf.config['apiurl']
1967 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1969 dst_apiurl = src_apiurl
1971 if src_apiurl != dst_apiurl:
1972 opts.client_side_copy = True
1974 rev, dummy = parseRevisionOption(opts.revision)
1977 comment = opts.message
1980 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1981 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1983 if src_project == dst_project and \
1984 src_package == dst_package and \
1986 src_apiurl == dst_apiurl:
1987 raise oscerr.WrongArgs('Source and destination are the same.')
1989 r = copy_pac(src_apiurl, src_project, src_package,
1990 dst_apiurl, dst_project, dst_package,
1991 client_side_copy=opts.client_side_copy,
1992 keep_maintainers=opts.keep_maintainers,
1993 keep_develproject=opts.keep_develproject,
2000 @cmdln.option('-c', '--checkout', action='store_true',
2001 help='Checkout branched package afterwards ' \
2002 '(\'osc bco\' is a shorthand for this option)' )
2003 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
2004 help='Use this attribute to find affected packages (default is OBS:Maintained)')
2005 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
2006 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
2007 def do_mbranch(self, subcmd, opts, *args):
2008 """${cmd_name}: Multiple branch of a package
2010 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
2013 This command is used for creating multiple links of defined version of a package
2014 in one project. This is esp. used for maintenance updates.
2016 The branched package will live in
2017 home:USERNAME:branches:ATTRIBUTE:PACKAGE
2018 if nothing else specified.
2021 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
2024 args = slash_split(args)
2027 maintained_attribute = conf.config['maintained_attribute']
2028 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
2030 if not len(args) or len(args) > 2:
2031 raise oscerr.WrongArgs('Wrong number of arguments.')
2037 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
2041 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
2044 print "Project " + r + " created."
2047 init_project_dir(conf.config['apiurl'], r, r)
2048 print statfrmt('A', r)
2051 for package in meta_get_packagelist(conf.config['apiurl'], r):
2053 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
2055 print >>sys.stderr, 'Error while checkout package:\n', package
2057 if conf.config['verbose']:
2058 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
2061 @cmdln.alias('branchco')
2063 @cmdln.alias('getpac')
2064 @cmdln.option('--nodevelproject', action='store_true',
2065 help='do not follow a defined devel project ' \
2066 '(primary project where a package is developed)')
2067 @cmdln.option('-c', '--checkout', action='store_true',
2068 help='Checkout branched package afterwards ' \
2069 '(\'osc bco\' is a shorthand for this option)' )
2070 @cmdln.option('-f', '--force', default=False, action="store_true",
2071 help='force branch, overwrite target')
2072 @cmdln.option('-m', '--message', metavar='TEXT',
2073 help='specify message TEXT')
2074 @cmdln.option('-r', '--revision', metavar='rev',
2075 help='branch against a specific revision')
2076 def do_branch(self, subcmd, opts, *args):
2077 """${cmd_name}: Branch a package
2079 [See http://en.opensuse.org/Build_Service/Collaboration for information
2082 Create a source link from a package of an existing project to a new
2083 subproject of the requesters home project (home:branches:)
2085 The branched package will live in
2086 home:USERNAME:branches:PROJECT/PACKAGE
2087 if nothing else specified.
2089 With getpac or bco, the branched package will come from
2090 %(getpac_default_project)s
2091 if nothing else specified.
2095 osc branch SOURCEPROJECT SOURCEPACKAGE
2096 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
2097 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
2098 osc getpac SOURCEPACKAGE
2103 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
2104 args = slash_split(args)
2105 tproject = tpackage = None
2107 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
2108 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
2109 # python has no args.unshift ???
2110 args = [ conf.config['getpac_default_project'] , args[0] ]
2112 if len(args) == 0 and is_package_dir('.'):
2113 args = (store_read_project('.'), store_read_package('.'))
2115 if len(args) < 2 or len(args) > 4:
2116 raise oscerr.WrongArgs('Wrong number of arguments.')
2118 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
2120 expected = tproject = args[2]
2124 if not opts.message:
2125 footer='please specify the purpose of your branch'
2126 template='This package was branched from %s in order to ...\n' % args[0]
2127 opts.message = edit_message(footer, template)
2129 exists, targetprj, targetpkg, srcprj, srcpkg = \
2130 branch_pkg(conf.config['apiurl'], args[0], args[1],
2131 nodevelproject=opts.nodevelproject, rev=opts.revision,
2132 target_project=tproject, target_package=tpackage,
2133 return_existing=opts.checkout, msg=opts.message or '',
2136 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
2139 if not exists and (srcprj is not None and srcprj != args[0] or \
2140 srcprj is None and targetprj != expected):
2141 devloc = srcprj or targetprj
2142 if not srcprj and 'branches:' in targetprj:
2143 devloc = targetprj.split('branches:')[1]
2144 print '\nNote: The branch has been created of a different project,\n' \
2146 ' which is the primary location of where development for\n' \
2147 ' that package takes place.\n' \
2148 ' That\'s also where you would normally make changes against.\n' \
2149 ' A direct branch of the specified package can be forced\n' \
2150 ' with the --nodevelproject option.\n' % devloc
2152 package = tpackage or args[1]
2154 checkout_package(conf.config['apiurl'], targetprj, package,
2155 expand_link=True, prj_dir=targetprj)
2156 if conf.config['verbose']:
2157 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
2160 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
2161 apiopt = '-A %s ' % conf.config['apiurl']
2162 print 'A working copy of the branched package can be checked out with:\n\n' \
2164 % (apiopt, targetprj, package)
2165 print_request_list(conf.config['apiurl'], args[0], args[1])
2167 print_request_list(conf.config['apiurl'], devloc, args[1])
2170 def do_undelete(self, subcmd, opts, *args):
2171 """${cmd_name}: Restores a deleted project or package on the server.
2173 The server restores a package including the sources and meta configuration.
2174 Binaries remain to be lost and will be rebuild.
2177 osc undelete PROJECT
2178 osc undelete PROJECT PACKAGE [PACKAGE ...]
2183 args = slash_split(args)
2185 raise oscerr.WrongArgs('Missing argument.')
2191 undelete_package(conf.config['apiurl'], prj, pkg)
2193 undelete_project(conf.config['apiurl'], prj)
2196 @cmdln.option('-f', '--force', action='store_true',
2197 help='deletes a package or an empty project')
2198 def do_rdelete(self, subcmd, opts, *args):
2199 """${cmd_name}: Delete a project or packages on the server.
2201 As a safety measure, project must be empty (i.e., you need to delete all
2202 packages first). If you are sure that you want to remove this project and all
2203 its packages use \'--force\' switch.
2206 osc rdelete -f PROJECT
2207 osc rdelete PROJECT PACKAGE [PACKAGE ...]
2212 args = slash_split(args)
2214 raise oscerr.WrongArgs('Missing argument.')
2220 # careful: if pkg is an empty string, the package delete request results
2221 # into a project delete request - which works recursively...
2223 delete_package(conf.config['apiurl'], prj, pkg)
2224 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
2225 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
2226 'If you are sure that you want to remove this project and all its ' \
2227 'packages use the \'--force\' switch'
2230 delete_project(conf.config['apiurl'], prj)
2233 def do_deletepac(self, subcmd, opts, *args):
2234 print """${cmd_name} is obsolete !
2237 osc delete for checked out packages or projects
2239 osc rdelete for server side operations."""
2244 @cmdln.option('-f', '--force', action='store_true',
2245 help='deletes a project and its packages')
2246 def do_deleteprj(self, subcmd, opts, project):
2247 """${cmd_name} is obsolete !
2254 @cmdln.alias('metafromspec')
2255 @cmdln.option('', '--specfile', metavar='FILE',
2256 help='Path to specfile. (if you pass more than working copy this option is ignored)')
2257 def do_updatepacmetafromspec(self, subcmd, opts, *args):
2258 """${cmd_name}: Update package meta information from a specfile
2260 ARG, if specified, is a package working copy.
2266 args = parseargs(args)
2267 if opts.specfile and len(args) == 1:
2268 specfile = opts.specfile
2271 pacs = findpacs(args)
2273 p.read_meta_from_spec(specfile)
2274 p.update_package_meta()
2278 @cmdln.option('-c', '--change', metavar='rev',
2279 help='the change made by revision rev (like -r rev-1:rev).'
2280 'If rev is negative this is like -r rev:rev-1.')
2281 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
2282 help='If rev1 is specified it will compare your working copy against '
2283 'the revision (rev1) on the server. '
2284 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
2285 '(NOTE: changes in your working copy are ignored in this case)')
2286 @cmdln.option('-p', '--plain', action='store_true',
2287 help='output the diff in plain (not unified) diff format')
2288 @cmdln.option('--missingok', action='store_true',
2289 help='do not fail if the source or target project/package does not exist on the server')
2290 def do_diff(self, subcmd, opts, *args):
2291 """${cmd_name}: Generates a diff
2293 Generates a diff, comparing local changes against the repository
2296 ARG, specified, is a filename to include in the diff.
2302 args = parseargs(args)
2303 pacs = findpacs(args)
2307 rev = int(opts.change)
2317 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
2320 rev1, rev2 = parseRevisionOption(opts.revision)
2324 diff += ''.join(make_diff(pac, rev1))
2326 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
2327 pac.prjname, pac.name, rev2, not opts.plain, opts.missingok)
2332 @cmdln.option('--oldprj', metavar='OLDPRJ',
2333 help='project to compare against'
2334 ' (deprecated, use 3 argument form)')
2335 @cmdln.option('--oldpkg', metavar='OLDPKG',
2336 help='package to compare against'
2337 ' (deprecated, use 3 argument form)')
2338 @cmdln.option('-r', '--revision', metavar='N[:M]',
2339 help='revision id, where N = old revision and M = new revision')
2340 @cmdln.option('-p', '--plain', action='store_true',
2341 help='output the diff in plain (not unified) diff format')
2342 @cmdln.option('-c', '--change', metavar='rev',
2343 help='the change made by revision rev (like -r rev-1:rev). '
2344 'If rev is negative this is like -r rev:rev-1.')
2345 @cmdln.option('--missingok', action='store_true',
2346 help='do not fail if the source or target project/package does not exist on the server')
2347 def do_rdiff(self, subcmd, opts, *args):
2348 """${cmd_name}: Server-side "pretty" diff of two packages
2350 Compares two packages (three or four arguments) or shows the
2351 changes of a specified revision of a package (two arguments)
2353 If no revision is specified the latest revision is used.
2355 Note that this command doesn't return a normal diff (which could be
2356 applied as patch), but a "pretty" diff, which also compares the content
2361 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
2362 osc ${cmd_name} PROJECT PACKAGE
2366 args = slash_split(args)
2377 new_project = args[0]
2378 new_package = args[1]
2380 old_project = opts.oldprj
2382 old_package = opts.oldpkg
2383 elif len(args) == 3 or len(args) == 4:
2384 if opts.oldprj or opts.oldpkg:
2385 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
2386 old_project = args[0]
2387 new_package = old_package = args[1]
2388 new_project = args[2]
2390 new_package = args[3]
2392 raise oscerr.WrongArgs('Wrong number of arguments')
2397 rev = int(opts.change)
2407 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
2411 rev1, rev2 = parseRevisionOption(opts.revision)
2413 rdiff = server_diff(conf.config['apiurl'],
2414 old_project, old_package, rev1,
2415 new_project, new_package, rev2, not opts.plain, opts.missingok)
2420 def do_install(self, subcmd, opts, *args):
2421 """${cmd_name}: install a package after build via zypper in -r
2423 Not implemented yet. Use osc repourls,
2424 select the url you best like (standard),
2425 chop off after the last /, this should work with zypper.
2432 args = slash_split(args)
2433 args = expand_proj_pack(args)
2436 ## if there is only one argument, and it ends in .ymp
2437 ## then fetch it, Parse XML to get the first
2438 ## metapackage.group.repositories.repository.url
2439 ## and construct zypper cmd's for all
2440 ## metapackage.group.software.item.name
2442 ## if args[0] is already an url, the use it as is.
2444 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])
2445 print self.do_install.__doc__
2446 print "Example: \n" + cmd
2449 def do_repourls(self, subcmd, opts, *args):
2450 """${cmd_name}: Shows URLs of .repo files
2452 Shows URLs on which to access the project .repos files (yum-style
2453 metadata) on download.opensuse.org.
2456 osc repourls [PROJECT]
2461 apiurl = self.get_api_url()
2465 elif len(args) == 0:
2466 project = store_read_project('.')
2468 raise oscerr.WrongArgs('Wrong number of arguments')
2470 # XXX: API should somehow tell that
2471 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
2472 repos = get_repositories_of_project(apiurl, project)
2474 print url_tmpl % (project.replace(':', ':/'), repo, project)
2477 @cmdln.option('-r', '--revision', metavar='rev',
2478 help='checkout the specified revision. '
2479 'NOTE: if you checkout the complete project '
2480 'this option is ignored!')
2481 @cmdln.option('-e', '--expand-link', action='store_true',
2482 help='if a package is a link, check out the expanded '
2483 'sources (no-op, since this became the default)')
2484 @cmdln.option('-u', '--unexpand-link', action='store_true',
2485 help='if a package is a link, check out the _link file ' \
2486 'instead of the expanded sources')
2487 @cmdln.option('-M', '--meta', action='store_true',
2488 help='checkout out meta data instead of sources' )
2489 @cmdln.option('-c', '--current-dir', action='store_true',
2490 help='place PACKAGE folder in the current directory' \
2491 'instead of a PROJECT/PACKAGE directory')
2492 @cmdln.option('-s', '--source-service-files', action='store_true',
2493 help='Use server side generated sources instead of local generation.' )
2494 @cmdln.option('-S', '--server-side-source-service-files', action='store_true',
2495 help='Use server side generated sources instead of local generation.' )
2496 @cmdln.option('-l', '--limit-size', metavar='limit_size',
2497 help='Skip all files with a given size')
2499 def do_checkout(self, subcmd, opts, *args):
2500 """${cmd_name}: Check out content from the repository
2502 Check out content from the repository server, creating a local working
2505 When checking out a single package, the option --revision can be used
2506 to specify a revision of the package to be checked out.
2508 When a package is a source link, then it will be checked out in
2509 expanded form. If --unexpand-link option is used, the checkout will
2510 instead produce the raw _link file plus patches.
2513 osc co PROJECT [PACKAGE] [FILE]
2514 osc co PROJECT # entire project
2515 osc co PROJECT PACKAGE # a package
2516 osc co PROJECT PACKAGE FILE # single file -> to current dir
2518 while inside a project directory:
2519 osc co PACKAGE # check out PACKAGE from project
2524 if opts.unexpand_link:
2529 args = slash_split(args)
2530 project = package = filename = None
2532 apiurl = self.get_api_url()
2535 project = project_dir = args[0]
2541 if args and len(args) == 1:
2542 localdir = os.getcwd()
2543 if is_project_dir(localdir):
2544 project = store_read_project(localdir)
2545 project_dir = localdir
2548 rev, dummy = parseRevisionOption(opts.revision)
2552 if rev and rev != "latest" and not checkRevision(project, package, rev):
2553 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2557 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2560 if opts.current_dir:
2562 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2563 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)
2564 print_request_list(apiurl, project, package)
2568 if sys.platform[:3] == 'win':
2569 prj_dir = prj_dir.replace(':', ';')
2570 if os.path.exists(prj_dir):
2571 sys.exit('osc: project \'%s\' already exists' % project)
2573 # check if the project does exist (show_project_meta will throw an exception)
2574 show_project_meta(apiurl, project)
2576 init_project_dir(apiurl, prj_dir, project)
2577 print statfrmt('A', prj_dir)
2580 for package in meta_get_packagelist(apiurl, project):
2582 checkout_package(apiurl, project, package, expand_link = expand_link, \
2583 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)
2584 except oscerr.LinkExpandError, e:
2585 print >>sys.stderr, 'Link cannot be expanded:\n', e
2586 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2587 # check out in unexpanded form at least
2588 checkout_package(apiurl, project, package, expand_link = False, \
2589 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)
2590 print_request_list(apiurl, project)
2593 raise oscerr.WrongArgs('Missing argument.\n\n' \
2594 + self.get_cmd_help('checkout'))
2597 @cmdln.option('-q', '--quiet', action='store_true',
2598 help='print as little as possible')
2599 @cmdln.option('-v', '--verbose', action='store_true',
2600 help='print extra information')
2602 def do_status(self, subcmd, opts, *args):
2603 """${cmd_name}: Show status of files in working copy
2605 Show the status of files in a local working copy, indicating whether
2606 files have been changed locally, deleted, added, ...
2608 The first column in the output specifies the status and is one of the
2609 following characters:
2610 ' ' no modifications
2615 '?' item is not under version control
2616 '!' item is missing (removed by non-osc command) or incomplete
2621 osc st file1 file2 ...
2624 osc status [OPTS] [PATH...]
2628 args = parseargs(args)
2630 # storage for single Package() objects
2632 # storage for a project dir ( { prj_instance : [ package objects ] } )
2635 # when 'status' is run inside a project dir, it should
2636 # stat all packages existing in the wc
2637 if is_project_dir(arg):
2638 prj = Project(arg, False)
2640 if conf.config['do_package_tracking']:
2642 for pac in prj.pacs_have:
2643 # we cannot create package objects if the dir does not exist
2644 if not pac in prj.pacs_broken:
2645 prjpacs[prj].append(os.path.join(arg, pac))
2647 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2648 elif is_package_dir(arg):
2649 pacpaths.append(arg)
2650 elif os.path.isfile(arg):
2651 pacpaths.append(arg)
2653 msg = '\'%s\' is neither a project or a package directory' % arg
2654 raise oscerr.NoWorkingCopy, msg
2656 # process single packages
2657 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2658 # process project dirs
2659 for prj, pacs in prjpacs.iteritems():
2660 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2662 print '\n'.join(lines)
2665 def do_add(self, subcmd, opts, *args):
2666 """${cmd_name}: Mark files to be added upon the next commit
2668 In case a URL is given the file will get downloaded and registered to be downloaded
2669 by the server as well via the download_url source service.
2671 This is recommended for release tar balls to track their source and to help
2672 others to review your changes esp. on version upgrades.
2675 osc add URL [URL...]
2676 osc add FILE [FILE...]
2680 raise oscerr.WrongArgs('Missing argument.\n\n' \
2681 + self.get_cmd_help('add'))
2683 # Do some magic here, when adding a url. We want that the server to download the tar ball and to verify it
2684 for arg in parseargs(args):
2685 if arg.startswith('http://') or arg.startswith('https://') or arg.startswith('ftp://'):
2686 addDownloadUrlService(arg)
2691 def do_mkpac(self, subcmd, opts, *args):
2692 """${cmd_name}: Create a new package under version control
2695 osc mkpac new_package
2698 if not conf.config['do_package_tracking']:
2699 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2700 "in the [general] section in the configuration file"
2704 raise oscerr.WrongArgs('Wrong number of arguments.')
2706 createPackageDir(args[0])
2708 @cmdln.option('-r', '--recursive', action='store_true',
2709 help='If CWD is a project dir then scan all package dirs as well')
2711 def do_addremove(self, subcmd, opts, *args):
2712 """${cmd_name}: Adds new files, removes disappeared files
2714 Adds all files new in the local copy, and removes all disappeared files.
2716 ARG, if specified, is a package working copy.
2722 args = parseargs(args)
2724 for arg in arg_list: