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()
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)
153 # overridden from class Cmdln() to use config variables in help texts
154 def _help_preprocess(self, help, cmdname):
155 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
156 return help % conf.config
159 def do_init(self, subcmd, opts, project, package=None):
160 """${cmd_name}: Initialize a directory as working copy
162 Initialize an existing directory to be a working copy of an
163 (already existing) buildservice project/package.
165 (This is the same as checking out a package and then copying sources
166 into the directory. It does NOT create a new package. To create a
167 package, use 'osc meta pkg ... ...')
169 You wouldn't normally use this command.
171 To get a working copy of a package (e.g. for building it or working on
172 it, you would normally use the checkout command. Use "osc help
173 checkout" to get help for it.
182 init_project_dir(conf.config['apiurl'], os.curdir, project)
183 print 'Initializing %s (Project: %s)' % (os.curdir, project)
185 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
186 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
192 @cmdln.option('-a', '--arch', metavar='ARCH',
193 help='specify architecture (only for binaries)')
194 @cmdln.option('-r', '--repo', metavar='REPO',
195 help='specify repository (only for binaries)')
196 @cmdln.option('-b', '--binaries', action='store_true',
197 help='list built binaries instead of sources')
198 @cmdln.option('-R', '--revision', metavar='REVISION',
199 help='specify revision (only for sources)')
200 @cmdln.option('-e', '--expand', action='store_true',
201 help='expand linked package (only for sources)')
202 @cmdln.option('-v', '--verbose', action='store_true',
203 help='print extra information')
204 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
205 help='print extra information')
206 def do_list(self, subcmd, opts, *args):
207 """${cmd_name}: List sources or binaries on the server
209 Examples for listing sources:
210 ls # list all projects
211 ls PROJECT # list packages in a project
212 ls PROJECT PACKAGE # list source files of package of a project
213 ls PROJECT PACKAGE <file> # list <file> if this file exists
214 ls -v PROJECT PACKAGE # verbosely list source files of package
215 ls -l PROJECT PACKAGE # verbosely list source files of package
216 ll PROJECT PACKAGE # verbosely list source files of package
217 LL PROJECT PACKAGE # verbosely list source files of expanded link
219 With --verbose, the following fields will be shown for each item:
221 Revision number of the last commit
223 Date and time of the last commit
225 Examples for listing binaries:
226 ls -b PROJECT # list all binaries of a project
227 ls -b PROJECT -a ARCH # list ARCH binaries of a project
228 ls -b PROJECT -r REPO # list binaries in REPO
229 ls -b PROJECT PACKAGE REPO ARCH
232 ${cmd_name} [PROJECT [PACKAGE]]
233 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
237 apiurl = conf.config['apiurl']
238 args = slash_split(args)
241 if subcmd == 'lL' or subcmd == 'LL':
255 if opts.repo != args[2]:
256 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
263 if not opts.binaries:
264 raise oscerr.WrongArgs('Too many arguments')
266 if opts.arch != args[3]:
267 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
272 if opts.binaries and opts.expand:
273 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
277 # ls -b toplevel doesn't make sense, so use info from
278 # current dir if available
281 if is_project_dir(dir):
282 project = store_read_project(dir)
283 apiurl = store_read_apiurl(dir)
284 elif is_package_dir(dir):
285 project = store_read_project(dir)
286 package = store_read_package(dir)
287 apiurl = store_read_apiurl(dir)
290 raise oscerr.WrongArgs('There are no binaries to list above project level.')
292 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
296 if opts.repo and opts.arch:
297 repos.append(Repo(opts.repo, opts.arch))
298 elif opts.repo and not opts.arch:
299 for repo in get_repos_of_project(apiurl, project):
300 if repo.name == opts.repo:
302 elif opts.arch and not opts.repo:
303 for repo in get_repos_of_project(apiurl, project):
304 if repo.arch == opts.arch:
307 repos = get_repos_of_project(apiurl, project)
311 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
313 for result in results:
316 print '%s/%s' % (result[0].name, result[0].arch)
321 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
327 elif not opts.binaries:
329 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
333 if self.options.verbose:
334 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
336 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
338 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
340 elif len(args) == 2 or len(args) == 3:
341 l = meta_get_filelist(conf.config['apiurl'],
344 verbose=opts.verbose,
346 revision=opts.revision)
348 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
349 for i in l if not fname or fname == i.name ]
352 print 'file \'%s\' does not exist' % fname
360 print 'file \'%s\' does not exist' % fname
365 @cmdln.option('-f', '--force', action='store_true',
366 help='force generation of new patchinfo file')
367 @cmdln.option('--force-update', action='store_true',
368 help='drops away collected packages from an already built patch and let it collect again')
369 def do_patchinfo(self, subcmd, opts, *args):
370 """${cmd_name}: Generate and edit a patchinfo file.
372 A patchinfo file describes the packages for an update and the kind of
377 osc patchinfo PATCH_NAME
381 project_dir = localdir = os.getcwd()
382 if is_project_dir(localdir):
383 project = store_read_project(localdir)
384 apiurl = store_read_apiurl(localdir)
386 sys.exit('This command must be called in a checked out project.')
388 for p in meta_get_packagelist(apiurl, project):
389 if p.startswith("_patchinfo:"):
392 if opts.force or not patchinfo:
393 print "Creating initial patchinfo..."
394 query='cmd=createpatchinfo'
396 query += "&name=" + args[0]
397 url = makeurl(apiurl, ['source', project], query=query)
399 for p in meta_get_packagelist(apiurl, project):
400 if p.startswith("_patchinfo:"):
403 if not os.path.exists(project_dir + "/" + patchinfo):
404 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
406 if sys.platform[:3] != 'win':
407 editor = os.getenv('EDITOR', default='vim')
409 editor = os.getenv('EDITOR', default='notepad')
410 subprocess.call('%s %s' % (editor, project_dir + "/" + patchinfo + "/_patchinfo"), shell=True)
413 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
414 help='affect only a given attribute')
415 @cmdln.option('--attribute-defaults', action='store_true',
416 help='include defined attribute defaults')
417 @cmdln.option('--attribute-project', action='store_true',
418 help='include project values, if missing in packages ')
419 @cmdln.option('-F', '--file', metavar='FILE',
420 help='read metadata from FILE, instead of opening an editor. '
421 '\'-\' denotes standard input. ')
422 @cmdln.option('-e', '--edit', action='store_true',
423 help='edit metadata')
424 @cmdln.option('-c', '--create', action='store_true',
425 help='create attribute without values')
426 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
427 help='set attribute values')
428 @cmdln.option('--delete', action='store_true',
429 help='delete a pattern or attribute')
430 def do_meta(self, subcmd, opts, *args):
431 """${cmd_name}: Show meta information, or edit it
433 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
435 This command displays metadata on buildservice objects like projects,
436 packages, or users. The type of metadata is specified by the word after
437 "meta", like e.g. "meta prj".
439 prj denotes metadata of a buildservice project.
440 prjconf denotes the (build) configuration of a project.
441 pkg denotes metadata of a buildservice package.
442 user denotes the metadata of a user.
443 pattern denotes installation patterns defined for a project.
445 To list patterns, use 'osc meta pattern PRJ'. An additional argument
446 will be the pattern file to view or edit.
448 With the --edit switch, the metadata can be edited. Per default, osc
449 opens the program specified by the environmental variable EDITOR with a
450 temporary file. Alternatively, content to be saved can be supplied via
451 the --file switch. If the argument is '-', input is taken from stdin:
452 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
454 When trying to edit a non-existing resource, it is created implicitly.
460 osc meta pkg PRJ PKG -e
461 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
464 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
465 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
466 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
467 osc meta pattern --delete PRJ PATTERN
471 args = slash_split(args)
473 if not args or args[0] not in metatypes.keys():
474 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
475 % ', '.join(metatypes))
481 min_args, max_args = 2, 2
482 elif cmd in ['pattern']:
483 min_args, max_args = 1, 2
484 elif cmd in ['attribute']:
485 min_args, max_args = 1, 3
487 min_args, max_args = 1, 1
488 if len(args) < min_args:
489 raise oscerr.WrongArgs('Too few arguments.')
490 if len(args) > max_args:
491 raise oscerr.WrongArgs('Too many arguments.')
498 project, package = args[0:2]
499 elif cmd == 'attribute':
505 if opts.attribute_project:
506 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
511 attributepath.append('source')
512 attributepath.append(project)
514 attributepath.append(package)
516 attributepath.append(subpackage)
517 attributepath.append('_attribute')
518 elif cmd == 'prjconf':
522 elif cmd == 'pattern':
528 # enforce pattern argument if needed
529 if opts.edit or opts.file:
530 raise oscerr.WrongArgs('A pattern file argument is required.')
533 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
535 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
537 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
538 elif cmd == 'attribute':
539 sys.stdout.write(''.join(show_attribute_meta(conf.config['apiurl'], project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
540 elif cmd == 'prjconf':
541 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
543 r = get_user_meta(conf.config['apiurl'], user)
545 sys.stdout.write(''.join(r))
546 elif cmd == 'pattern':
548 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
550 sys.stdout.write(''.join(r))
552 r = show_pattern_metalist(conf.config['apiurl'], project)
554 sys.stdout.write('\n'.join(r) + '\n')
557 if opts.edit and not opts.file:
559 edit_meta(metatype='prj',
561 path_args=quote_plus(project),
564 'user': conf.config['user']}))
566 edit_meta(metatype='pkg',
568 path_args=(quote_plus(project), quote_plus(package)),
571 'user': conf.config['user']}))
572 elif cmd == 'prjconf':
573 edit_meta(metatype='prjconf',
575 path_args=quote_plus(project),
578 edit_meta(metatype='user',
580 path_args=(quote_plus(user)),
581 template_args=({'user': user}))
582 elif cmd == 'pattern':
583 edit_meta(metatype='pattern',
585 path_args=(project, pattern),
588 # create attribute entry
589 if (opts.create or opts.set) and cmd == 'attribute':
590 if not opts.attribute:
591 raise oscerr.WrongOptions('no attribute given to create')
594 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
595 for i in opts.set.split(','):
596 values += '<value>%s</value>' % i
597 aname = opts.attribute.split(":")
598 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
599 url = makeurl(conf.config['apiurl'], attributepath)
600 for data in streamfile(url, http_POST, data=d):
601 sys.stdout.write(data)
610 f = open(opts.file).read()
612 sys.exit('could not open file \'%s\'.' % opts.file)
615 edit_meta(metatype='prj',
618 path_args=quote_plus(project))
620 edit_meta(metatype='pkg',
623 path_args=(quote_plus(project), quote_plus(package)))
624 elif cmd == 'prjconf':
625 edit_meta(metatype='prjconf',
628 path_args=quote_plus(project))
630 edit_meta(metatype='user',
633 path_args=(quote_plus(user)))
634 elif cmd == 'pattern':
635 edit_meta(metatype='pattern',
638 path_args=(project, pattern))
643 path = metatypes[cmd]['path']
645 path = path % (project, pattern)
646 u = makeurl(conf.config['apiurl'], [path])
648 elif cmd == 'attribute':
649 if not opts.attribute:
650 raise oscerr.WrongOptions('no attribute given to create')
651 attributepath.append(opts.attribute)
652 u = makeurl(conf.config['apiurl'], attributepath)
653 for data in streamfile(u, http_DELETE):
654 sys.stdout.write(data)
656 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
659 @cmdln.option('-m', '--message', metavar='TEXT',
660 help='specify message TEXT')
661 @cmdln.option('-r', '--revision', metavar='REV',
662 help='for "create", specify a certain source revision ID (the md5 sum)')
663 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
664 help='Superseding another request by this one')
665 @cmdln.option('--nodevelproject', action='store_true',
666 help='do not follow a defined devel project ' \
667 '(primary project where a package is developed)')
668 @cmdln.option('--cleanup', action='store_true',
669 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
670 @cmdln.option('--no-cleanup', action='store_true',
671 help='never remove source package on accept, but update its content')
672 @cmdln.option('--no-update', action='store_true',
673 help='never touch source package on accept (will break source links)')
674 @cmdln.option('-d', '--diff', action='store_true',
675 help='show diff only instead of creating the actual request')
676 @cmdln.option('--yes', action='store_true',
677 help='proceed without asking.')
679 @cmdln.alias("submitreq")
680 @cmdln.alias("submitpac")
681 def do_submitrequest(self, subcmd, opts, *args):
682 """${cmd_name}: Create request to submit source into another Project
684 [See http://en.opensuse.org/Build_Service/Collaboration for information
687 See the "request" command for showing and modifing existing requests.
690 osc submitreq [OPTIONS]
691 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
692 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
696 src_update = conf.config['submitrequest_on_accept_action'] or None
697 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
699 src_update = "cleanup"
700 elif opts.no_cleanup:
701 src_update = "update"
703 src_update = "noupdate"
705 args = slash_split(args)
707 # remove this block later again
708 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
709 if args and args[0] in oldcmds:
710 print "************************************************************************"
711 print "* WARNING: It looks that you are using this command with a *"
712 print "* deprecated syntax. *"
713 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
714 print "* to see the new syntax. *"
715 print "************************************************************************"
716 if args[0] == 'create':
722 raise oscerr.WrongArgs('Too many arguments.')
724 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
725 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
727 apiurl = conf.config['apiurl']
729 if len(args) == 0 and is_project_dir(os.getcwd()):
731 # submit requests for multiple packages are currently handled via multiple requests
732 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
733 project = store_read_project(os.curdir)
734 apiurl = store_read_apiurl(os.curdir)
740 # loop via all packages for checking their state
741 for p in meta_get_packagelist(apiurl, project):
742 if p.startswith("_patchinfo:"):
745 # get _link info from server, that knows about the local state ...
746 u = makeurl(apiurl, ['source', project, p])
748 root = ET.parse(f).getroot()
749 linkinfo = root.find('linkinfo')
751 print "Package ", p, " is not a source link."
752 sys.exit("This is currently not supported.")
753 if linkinfo.get('error'):
754 print "Package ", p, " is a broken source link."
755 sys.exit("Please fix this first")
756 t = linkinfo.get('project')
758 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
759 # Real fix is to ask the api if sources are modificated
760 # but there is no such call yet.
761 targetprojects.append(t)
763 print "Submitting package ", p
765 print " Skipping package ", p
767 print "Skipping package ", p, " since it is a source link pointing inside the project."
771 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
772 print "\nEverything fine? Can we create the requests ? [y/n]"
773 if sys.stdin.read(1) != "y":
774 sys.exit("Aborted...")
776 # loop via all packages to do the action
778 result = create_submit_request(apiurl, project, p)
781 sys.exit("submit request creation failed")
782 sr_ids.append(result)
784 # create submit requests for all found patchinfos
788 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
791 for t in targetprojects:
792 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
793 (project, p, t, p, options_block)
797 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
798 (actionxml, cgi.escape(opts.message or ""))
799 u = makeurl(apiurl, ['request'], query='cmd=create')
800 f = http_POST(u, data=xml)
802 root = ET.parse(f).getroot()
803 sr_ids.append(root.get('id'))
805 print "Requests created: ",
808 sys.exit('Successfull finished')
811 # try using the working copy at hand
812 p = findpacs(os.curdir)[0]
813 src_project = p.prjname
816 if len(args) == 0 and p.islink():
817 dst_project = p.linkinfo.project
818 dst_package = p.linkinfo.package
820 dst_project = args[0]
822 dst_package = args[1]
824 dst_package = src_package
826 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
827 'Please provide it the target via commandline arguments.' % p.name)
829 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
830 if len(modified) > 0:
831 print 'Your working copy has local modifications.'
832 repl = raw_input('Proceed without committing the local changes? (y|N) ')
836 # get the arguments from the commandline
837 src_project, src_package, dst_project = args[0:3]
839 dst_package = args[3]
841 dst_package = src_package
843 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
844 + self.get_cmd_help('request'))
846 if not opts.nodevelproject:
849 devloc = show_develproject(apiurl, dst_project, dst_package)
850 except urllib2.HTTPError:
851 print >>sys.stderr, """\
852 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
853 % (dst_project, dst_package)
857 dst_project != devloc and \
858 src_project != devloc:
860 A different project, %s, is defined as the place where development
861 of the package %s primarily takes place.
862 Please submit there instead, or use --nodevelproject to force direct submission.""" \
863 % (devloc, dst_package)
868 if opts.diff or not opts.message:
870 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
871 rdiff += server_diff(apiurl,
872 dst_project, dst_package, opts.revision,
873 src_project, src_package, None, True)
879 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
880 user = conf.get_apiurl_usr(apiurl)
881 myreqs = [ i for i in reqs if i.state.who == user ]
884 print 'You already created the following submit request: %s.' % \
885 ', '.join([str(i.reqid) for i in myreqs ])
886 repl = raw_input('Supersede the old requests? (y/n/c) ')
887 if repl.lower() == 'c':
888 print >>sys.stderr, 'Aborting'
894 changes_re = re.compile(r'^--- .*\.changes ')
895 for line in rdiff.split('\n'):
896 if line.startswith('--- '):
897 if changes_re.match(line):
902 difflines.append(line)
903 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
905 result = create_submit_request(apiurl,
906 src_project, src_package,
907 dst_project, dst_package,
908 opts.message, orev=opts.revision, src_update=src_update)
909 if repl.lower() == 'y':
911 change_request_state(apiurl, str(req.reqid), 'superseded',
912 'superseded by %s' % result, result)
915 r = change_request_state(conf.config['apiurl'],
916 opts.supersede, 'superseded', opts.message or '', result)
918 print 'created request id', result
921 @cmdln.option('-m', '--message', metavar='TEXT',
922 help='specify message TEXT')
924 @cmdln.alias("deletereq")
925 def do_deleterequest(self, subcmd, opts, *args):
926 """${cmd_name}: Create request to delete a package or project
930 osc deletereq [-m TEXT] PROJECT [PACKAGE]
934 args = slash_split(args)
937 raise oscerr.WrongArgs('Please specify at least a project.')
939 raise oscerr.WrongArgs('Too many arguments.')
941 apiurl = conf.config['apiurl']
949 opts.message = edit_message()
951 result = create_delete_request(apiurl, project, package, opts.message)
955 @cmdln.option('-m', '--message', metavar='TEXT',
956 help='specify message TEXT')
958 @cmdln.alias("changedevelreq")
959 def do_changedevelrequest(self, subcmd, opts, *args):
960 """${cmd_name}: Create request to change the devel package definition.
962 [See http://en.opensuse.org/Build_Service/Collaboration for information
965 See the "request" command for showing and modifing existing requests.
967 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
971 raise oscerr.WrongArgs('Too many arguments.')
973 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
975 devel_project = store_read_project(wd)
976 devel_package = package = store_read_package(wd)
977 apiurl = store_read_apiurl(wd)
978 project = conf.config['getpac_default_project']
981 raise oscerr.WrongArgs('Too few arguments.')
983 apiurl = conf.config['apiurl']
985 devel_project = args[2]
988 devel_package = package
990 devel_package = args[3]
994 footer=textwrap.TextWrapper(width = 66).fill(
995 'please explain why you like to change the devel project of %s/%s to %s/%s'
996 % (project,package,devel_project,devel_package))
997 opts.message = edit_message(footer)
999 result = create_change_devel_request(apiurl,
1000 devel_project, devel_package,
1006 @cmdln.option('-d', '--diff', action='store_true',
1007 help='generate a diff')
1008 @cmdln.option('-u', '--unified', action='store_true',
1009 help='output the diff in the unified diff format')
1010 @cmdln.option('-m', '--message', metavar='TEXT',
1011 help='specify message TEXT')
1012 @cmdln.option('-t', '--type', metavar='TYPE',
1013 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1014 @cmdln.option('-a', '--all', action='store_true',
1015 help='all states. Same as\'-s all\'')
1016 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1017 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]')
1018 @cmdln.option('-D', '--days', metavar='DAYS',
1019 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1020 @cmdln.option('-U', '--user', metavar='USER',
1021 help='same as -M, but for the specified USER')
1022 @cmdln.option('-b', '--brief', action='store_true', default=False,
1023 help='print output in list view as list subcommand')
1024 @cmdln.option('-M', '--mine', action='store_true',
1025 help='only show requests created by yourself')
1026 @cmdln.option('-B', '--bugowner', action='store_true',
1027 help='also show requests about packages where I am bugowner')
1028 @cmdln.option('-i', '--interactive', action='store_true',
1029 help='interactive review of request')
1030 @cmdln.option('--exclude-target-project', action='append',
1031 help='exclude target project from request list')
1033 @cmdln.alias("review")
1034 def do_request(self, subcmd, opts, *args):
1035 """${cmd_name}: Show and modify requests
1037 [See http://en.opensuse.org/Build_Service/Collaboration for information
1040 This command shows and modifies existing requests. To create new requests
1041 you need to call one of the following:
1044 osc changedevelrequest
1045 To send low level requests to the buildservice API, use:
1048 This command has the following sub commands:
1050 "list" lists open requests attached to a project or package or person.
1051 Uses the project/package of the current directory if none of
1052 -M, -U USER, project/package are given.
1054 "log" will show the history of the given ID
1056 "show" will show the request itself, and generate a diff for review, if
1057 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1059 "decline" will change the request state to "declined" and append a
1060 message that you specify with the --message option.
1062 "wipe" will permanently delete a request.
1064 "revoke" will set the request state to "revoked" and append a
1065 message that you specify with the --message option.
1067 "accept" will change the request state to "accepted" and will trigger
1068 the actual submit process. That would normally be a server-side copy of
1069 the source package to the target package.
1071 "checkout" will checkout the request's source package. This only works for "submit" requests.
1074 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1076 osc request [show] [-d] [-b] ID
1077 osc request accept [-m TEXT] ID
1078 osc request decline [-m TEXT] ID
1079 osc request revoke [-m TEXT] ID
1081 osc request checkout/co ID
1082 osc review accept [-m TEXT] ID
1083 osc review decline [-m TEXT] ID
1087 args = slash_split(args)
1089 if opts.all and opts.state:
1090 raise oscerr.WrongOptions('Sorry, the options --all and --state ' \
1091 'are mutually exclusive.')
1092 if opts.mine and opts.user:
1093 raise oscerr.WrongOptions('Sorry, the options --user and --mine ' \
1094 'are mutually exclusive.')
1099 if opts.state == '':
1102 if opts.state == '':
1105 cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co', 'help']
1106 if not args or args[0] not in cmds:
1107 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1108 % (args[0],', '.join(cmds)))
1114 return self.do_help(['help', 'request'])
1117 min_args, max_args = 1, 1
1118 elif cmd in ['list']:
1119 min_args, max_args = 0, 2
1121 min_args, max_args = 1, 1
1122 if len(args) < min_args:
1123 raise oscerr.WrongArgs('Too few arguments.')
1124 if len(args) > max_args:
1125 raise oscerr.WrongArgs('Too many arguments.')
1127 apiurl = conf.config['apiurl']
1134 elif not opts.mine and not opts.user:
1136 project = store_read_project(os.curdir)
1137 apiurl = store_read_apiurl(os.curdir)
1138 package = store_read_package(os.curdir)
1139 except oscerr.NoWorkingCopy:
1144 elif cmd in ['log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1149 states = ('new', 'accepted', 'revoked', 'declined')
1150 state_list = opts.state.split(',')
1151 if opts.state == 'all':
1152 state_list = ['all']
1154 for s in state_list:
1156 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1159 who = conf.get_apiurl_usr(apiurl)
1163 state_list = ['all']
1165 ## FIXME -B not implemented!
1167 if (self.options.debug):
1168 print 'list: option --bugowner ignored: not impl.'
1170 results = get_request_list(apiurl, project, package, who,
1171 state_list, opts.type, opts.exclude_target_project or [])
1172 results.sort(reverse=True)
1174 days = opts.days or conf.config['request_list_days']
1181 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1184 ## bs has received 2009-09-20 a new xquery compare() function
1185 ## which allows us to limit the list inside of get_request_list
1186 ## That would be much faster for coolo. But counting the remainder
1187 ## would not be possible with current xquery implementation.
1188 ## Workaround: fetch all, and filter on client side.
1190 ## FIXME: date filtering should become implemented on server side
1191 for result in results:
1192 if days == 0 or result.state.when > since or result.state.name == 'new':
1193 print result.list_view()
1197 print "There are %d requests older than %s days.\n" % (skipped, days)
1200 for l in get_request_log(conf.config['apiurl'], reqid):
1205 r = get_request(conf.config['apiurl'], reqid)
1208 elif opts.interactive or conf.config['request_show_interactive']:
1209 return request_interactive_review(conf.config['apiurl'], r)
1212 # fixme: will inevitably fail if the given target doesn't exist
1215 print server_diff(conf.config['apiurl'],
1216 r.actions[0].dst_project, r.actions[0].dst_package, None,
1217 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified)
1218 except urllib2.HTTPError, e:
1219 e.osc_msg = 'Diff not possible'
1223 elif cmd == 'checkout' or cmd == 'co':
1224 r = get_request(conf.config['apiurl'], reqid)
1225 submits = [ i for i in r.actions if i.type == 'submit' ]
1226 if not len(submits):
1227 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1228 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1229 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1232 if not opts.message:
1233 opts.message = edit_message()
1234 state_map = {'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1235 # Change review state only
1236 if subcmd == 'review':
1237 if cmd in ['accept', 'decline']:
1238 r = change_review_state(conf.config['apiurl'],
1239 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1241 # Change state of entire request
1242 elif cmd in ['accept', 'decline', 'wipe', 'revoke']:
1243 r = change_request_state(conf.config['apiurl'],
1244 reqid, state_map[cmd], opts.message or '')
1247 # editmeta and its aliases are all depracated
1248 @cmdln.alias("editprj")
1249 @cmdln.alias("createprj")
1250 @cmdln.alias("editpac")
1251 @cmdln.alias("createpac")
1252 @cmdln.alias("edituser")
1253 @cmdln.alias("usermeta")
1255 def do_editmeta(self, subcmd, opts, *args):
1258 Obsolete command to edit metadata. Use 'meta' now.
1260 See the help output of 'meta'.
1264 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1265 print >>sys.stderr, 'See \'osc help meta\'.'
1266 #self.do_help([None, 'meta'])
1270 @cmdln.option('-r', '--revision', metavar='rev',
1271 help='use the specified revision.')
1272 @cmdln.option('-u', '--unset', action='store_true',
1273 help='remove revision in link, it will point always to latest revision')
1274 def do_setlinkrev(self, subcmd, opts, *args):
1275 """${cmd_name}: Updates a revision number in a source link.
1277 This command adds or updates a specified revision number in a source link.
1278 The current revision of the source is used, if no revision number is specified.
1282 osc setlinkrev PROJECT [PACKAGE]
1286 args = slash_split(args)
1287 apiurl = conf.config['apiurl']
1290 p = findpacs(os.curdir)[0]
1295 sys.exit('Local directory is no checked out source link package, aborting')
1296 elif len(args) == 2:
1299 elif len(args) == 1:
1302 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1303 + self.get_cmd_help('setlinkrev'))
1306 packages = [ package ]
1308 packages = meta_get_packagelist(apiurl, project)
1311 print "setting revision for package", p
1315 rev, dummy = parseRevisionOption(opts.revision)
1316 set_link_rev(apiurl, project, p, rev)
1319 def do_linktobranch(self, subcmd, opts, *args):
1320 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1322 This command tells the server to convert a _link with or without a project.diff
1323 to a branch. This is a full copy with a _link file pointing to the branched place.
1326 osc linktobranch # can be used in checked out package
1327 osc linktobranch PROJECT PACKAGE
1331 args = slash_split(args)
1334 project = store_read_project(wd)
1335 package = store_read_package(wd)
1336 apiurl = store_read_apiurl(wd)
1337 update_local_dir = True
1339 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1341 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1343 apiurl = conf.config['apiurl']
1346 update_local_dir = False
1349 link_to_branch(apiurl, project, package)
1350 if update_local_dir:
1352 pac.update(rev=pac.latest_rev())
1355 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1356 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1357 @cmdln.option('-c', '--current', action='store_true',
1358 help='link fixed against current revision.')
1359 @cmdln.option('-r', '--revision', metavar='rev',
1360 help='link the specified revision.')
1361 @cmdln.option('-f', '--force', action='store_true',
1362 help='overwrite an existing link file if it is there.')
1363 @cmdln.option('-d', '--disable-publish', action='store_true',
1364 help='disable publishing of the linked package')
1365 def do_linkpac(self, subcmd, opts, *args):
1366 """${cmd_name}: "Link" a package to another package
1368 A linked package is a clone of another package, but plus local
1369 modifications. It can be cross-project.
1371 The DESTPAC name is optional; the source packages' name will be used if
1374 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1376 To add a patch, add the patch as file and add it to the _link file.
1377 You can also specify text which will be inserted at the top of the spec file.
1379 See the examples in the _link file.
1382 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1386 args = slash_split(args)
1388 if not args or len(args) < 3:
1389 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1390 + self.get_cmd_help('linkpac'))
1392 rev, dummy = parseRevisionOption(opts.revision)
1394 src_project = args[0]
1395 src_package = args[1]
1396 dst_project = args[2]
1398 dst_package = args[3]
1400 dst_package = src_package
1402 if src_project == dst_project and src_package == dst_package:
1403 raise oscerr.WrongArgs('Error: source and destination are the same.')
1405 if src_project == dst_project and not opts.cicount:
1406 # in this case, the user usually wants to build different spec
1407 # files from the same source
1408 opts.cicount = "copy"
1411 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1413 if rev and not checkRevision(src_project, src_package, rev):
1414 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1417 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1419 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1420 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1421 @cmdln.option('-d', '--disable-publish', action='store_true',
1422 help='disable publishing of the aggregated package')
1423 def do_aggregatepac(self, subcmd, opts, *args):
1424 """${cmd_name}: "Aggregate" a package to another package
1426 Aggregation of a package means that the build results (binaries) of a
1427 package are basically copied into another project.
1428 This can be used to make packages available from building that are
1429 needed in a project but available only in a different project. Note
1430 that this is done at the expense of disk space. See
1431 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1432 for more information.
1434 The DESTPAC name is optional; the source packages' name will be used if
1438 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1442 args = slash_split(args)
1444 if not args or len(args) < 3:
1445 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1446 + self.get_cmd_help('aggregatepac'))
1448 src_project = args[0]
1449 src_package = args[1]
1450 dst_project = args[2]
1452 dst_package = args[3]
1454 dst_package = src_package
1456 if src_project == dst_project and src_package == dst_package:
1457 raise oscerr.WrongArgs('Error: source and destination are the same.')
1461 for pair in opts.map_repo.split(','):
1462 src_tgt = pair.split('=')
1463 if len(src_tgt) != 2:
1464 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1465 repo_map[src_tgt[0]] = src_tgt[1]
1467 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1470 @cmdln.option('-c', '--client-side-copy', action='store_true',
1471 help='do a (slower) client-side copy')
1472 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1473 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1474 @cmdln.option('-d', '--keep-develproject', action='store_true',
1475 help='keep develproject tag in the package metadata')
1476 @cmdln.option('-r', '--revision', metavar='rev',
1477 help='link the specified revision.')
1478 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1479 help='URL of destination api server. Default is the source api server.')
1480 @cmdln.option('-m', '--message', metavar='TEXT',
1481 help='specify message TEXT')
1482 @cmdln.option('-e', '--expand', action='store_true',
1483 help='if the source package is a link then copy the expanded version of the link')
1484 def do_copypac(self, subcmd, opts, *args):
1485 """${cmd_name}: Copy a package
1487 A way to copy package to somewhere else.
1489 It can be done across buildservice instances, if the -t option is used.
1490 In that case, a client-side copy is implied.
1492 Using --client-side-copy always involves downloading all files, and
1493 uploading them to the target.
1495 The DESTPAC name is optional; the source packages' name will be used if
1499 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1503 args = slash_split(args)
1505 if not args or len(args) < 3:
1506 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1507 + self.get_cmd_help('copypac'))
1509 src_project = args[0]
1510 src_package = args[1]
1511 dst_project = args[2]
1513 dst_package = args[3]
1515 dst_package = src_package
1517 src_apiurl = conf.config['apiurl']
1519 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1521 dst_apiurl = src_apiurl
1523 if src_project == dst_project and \
1524 src_package == dst_package and \
1525 src_apiurl == dst_apiurl:
1526 raise oscerr.WrongArgs('Source and destination are the same.')
1528 if src_apiurl != dst_apiurl:
1529 opts.client_side_copy = True
1531 rev, dummy = parseRevisionOption(opts.revision)
1534 comment = opts.message
1537 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1538 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1540 r = copy_pac(src_apiurl, src_project, src_package,
1541 dst_apiurl, dst_project, dst_package,
1542 client_side_copy=opts.client_side_copy,
1543 keep_maintainers=opts.keep_maintainers,
1544 keep_develproject=opts.keep_develproject,
1551 @cmdln.option('-c', '--checkout', action='store_true',
1552 help='Checkout branched package afterwards ' \
1553 '(\'osc bco\' is a shorthand for this option)' )
1554 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
1555 help='Use this attribute to find affected packages (default is OBS:Maintained)')
1556 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
1557 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
1558 def do_mbranch(self, subcmd, opts, *args):
1559 """${cmd_name}: Multiple branch of a package
1561 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
1564 This command is used for creating multiple links of defined version of a package
1565 in one project. This is esp. used for maintenance updates.
1567 The branched package will live in
1568 home:USERNAME:branches:ATTRIBUTE:PACKAGE
1569 if nothing else specified.
1572 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
1575 args = slash_split(args)
1578 maintained_attribute = conf.config['maintained_attribute']
1579 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
1581 if not len(args) or len(args) > 2:
1582 raise oscerr.WrongArgs('Wrong number of arguments.')
1588 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
1592 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
1595 print "Project " + r + " created."
1598 init_project_dir(conf.config['apiurl'], r, r)
1599 print statfrmt('A', r)
1602 for package in meta_get_packagelist(conf.config['apiurl'], r):
1604 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
1606 print >>sys.stderr, 'Error while checkout package:\n', package
1608 if conf.config['verbose']:
1609 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1612 @cmdln.alias('branchco')
1614 @cmdln.alias('getpac')
1615 @cmdln.option('--nodevelproject', action='store_true',
1616 help='do not follow a defined devel project ' \
1617 '(primary project where a package is developed)')
1618 @cmdln.option('-c', '--checkout', action='store_true',
1619 help='Checkout branched package afterwards ' \
1620 '(\'osc bco\' is a shorthand for this option)' )
1621 @cmdln.option('-r', '--revision', metavar='rev',
1622 help='branch against a specific revision')
1623 @cmdln.option('-m', '--message', metavar='TEXT',
1624 help='specify message TEXT')
1625 def do_branch(self, subcmd, opts, *args):
1626 """${cmd_name}: Branch a package
1628 [See http://en.opensuse.org/Build_Service/Collaboration for information
1631 Create a source link from a package of an existing project to a new
1632 subproject of the requesters home project (home:branches:)
1634 The branched package will live in
1635 home:USERNAME:branches:PROJECT/PACKAGE
1636 if nothing else specified.
1638 With getpac or bco, the branched package will come from
1639 %(getpac_default_project)s
1640 if nothing else specified.
1643 osc branch SOURCEPROJECT SOURCEPACKAGE
1644 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
1645 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
1646 osc getpac SOURCEPACKAGE
1651 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
1652 args = slash_split(args)
1653 tproject = tpackage = None
1655 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
1656 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
1657 # python has no args.unshift ???
1658 args = [ conf.config['getpac_default_project'] , args[0] ]
1660 if len(args) < 2 or len(args) > 4:
1661 raise oscerr.WrongArgs('Wrong number of arguments.')
1662 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
1664 expected = tproject = args[2]
1668 exists, targetprj, targetpkg, srcprj, srcpkg = \
1669 branch_pkg(conf.config['apiurl'], args[0], args[1],
1670 nodevelproject=opts.nodevelproject, rev=opts.revision,
1671 target_project=tproject, target_package=tpackage,
1672 return_existing=opts.checkout, msg=opts.message or '')
1674 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
1677 if not exists and (srcprj is not None and srcprj != args[0] or \
1678 srcprj is None and targetprj != expected):
1679 devloc = srcprj or targetprj
1680 if not srcprj and 'branches:' in targetprj:
1681 devloc = targetprj.split('branches:')[1]
1682 print '\nNote: The branch has been created of a different project,\n' \
1684 ' which is the primary location of where development for\n' \
1685 ' that package takes place.\n' \
1686 ' That\'s also where you would normally make changes against.\n' \
1687 ' A direct branch of the specified package can be forced\n' \
1688 ' with the --nodevelproject option.\n' % devloc
1690 package = tpackage or args[1]
1692 checkout_package(conf.config['apiurl'], targetprj, package,
1693 expand_link=True, prj_dir=targetprj)
1694 if conf.config['verbose']:
1695 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1698 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
1699 apiopt = '-A %s ' % conf.config['apiurl']
1700 print 'A working copy of the branched package can be checked out with:\n\n' \
1702 % (apiopt, targetprj, package)
1703 print_request_list(conf.config['apiurl'], args[0], args[1])
1705 print_request_list(conf.config['apiurl'], devloc, args[1])
1709 @cmdln.option('-f', '--force', action='store_true',
1710 help='deletes a package or an empty project')
1711 def do_rdelete(self, subcmd, opts, *args):
1712 """${cmd_name}: Delete a project or packages on the server.
1714 As a safety measure, project must be empty (i.e., you need to delete all
1715 packages first). If you are sure that you want to remove this project and all
1716 its packages use \'--force\' switch.
1719 osc rdelete -f PROJECT
1720 osc rdelete PROJECT PACKAGE [PACKAGE ...]
1725 args = slash_split(args)
1727 raise oscerr.WrongArgs('Missing argument.')
1733 # careful: if pkg is an empty string, the package delete request results
1734 # into a project delete request - which works recursively...
1736 delete_package(conf.config['apiurl'], prj, pkg)
1737 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
1738 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
1739 'If you are sure that you want to remove this project and all its ' \
1740 'packages use the \'--force\' switch'
1743 delete_project(conf.config['apiurl'], prj)
1746 def do_deletepac(self, subcmd, opts, *args):
1747 print """${cmd_name} is obsolete !
1750 osc delete for checked out packages or projects
1752 osc rdelete for server side operations."""
1757 @cmdln.option('-f', '--force', action='store_true',
1758 help='deletes a project and its packages')
1759 def do_deleteprj(self, subcmd, opts, project):
1760 """${cmd_name} is obsolete !
1767 @cmdln.alias('metafromspec')
1768 @cmdln.option('', '--specfile', metavar='FILE',
1769 help='Path to specfile. (if you pass more than working copy this option is ignored)')
1770 def do_updatepacmetafromspec(self, subcmd, opts, *args):
1771 """${cmd_name}: Update package meta information from a specfile
1773 ARG, if specified, is a package working copy.
1779 args = parseargs(args)
1780 if opts.specfile and len(args) == 1:
1781 specfile = opts.specfile
1784 pacs = findpacs(args)
1786 p.read_meta_from_spec(specfile)
1787 p.update_package_meta()
1791 @cmdln.option('-c', '--change', metavar='rev',
1792 help='the change made by revision rev (like -r rev-1:rev).'
1793 'If rev is negative this is like -r rev:rev-1.')
1794 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
1795 help='If rev1 is specified it will compare your working copy against '
1796 'the revision (rev1) on the server. '
1797 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
1798 '(NOTE: changes in your working copy are ignored in this case)')
1799 @cmdln.option('-p', '--plain', action='store_true',
1800 help='output the diff in plain (not unified) diff format')
1801 def do_diff(self, subcmd, opts, *args):
1802 """${cmd_name}: Generates a diff
1804 Generates a diff, comparing local changes against the repository
1807 ARG, specified, is a filename to include in the diff.
1813 args = parseargs(args)
1814 pacs = findpacs(args)
1818 rev = int(opts.change)
1828 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1831 rev1, rev2 = parseRevisionOption(opts.revision)
1835 diff += ''.join(make_diff(pac, rev1))
1837 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
1838 pac.prjname, pac.name, rev2, not opts.plain)
1843 @cmdln.option('--oldprj', metavar='OLDPRJ',
1844 help='project to compare against'
1845 ' (deprecated, use 3 argument form)')
1846 @cmdln.option('--oldpkg', metavar='OLDPKG',
1847 help='package to compare against'
1848 ' (deprecated, use 3 argument form)')
1849 @cmdln.option('-r', '--revision', metavar='N[:M]',
1850 help='revision id, where N = old revision and M = new revision')
1851 @cmdln.option('-p', '--plain', action='store_true',
1852 help='output the diff in plain (not unified) diff format')
1853 @cmdln.option('-c', '--change', metavar='rev',
1854 help='the change made by revision rev (like -r rev-1:rev). '
1855 'If rev is negative this is like -r rev:rev-1.')
1856 def do_rdiff(self, subcmd, opts, *args):
1857 """${cmd_name}: Server-side "pretty" diff of two packages
1859 Compares two packages (three or four arguments) or shows the
1860 changes of a specified revision of a package (two arguments)
1862 If no revision is specified the latest revision is used.
1864 Note that this command doesn't return a normal diff (which could be
1865 applied as patch), but a "pretty" diff, which also compares the content
1870 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
1871 osc ${cmd_name} PROJECT PACKAGE
1875 args = slash_split(args)
1886 new_project = args[0]
1887 new_package = args[1]
1889 old_project = opts.oldprj
1891 old_package = opts.oldpkg
1892 elif len(args) == 3 or len(args) == 4:
1893 if opts.oldprj or opts.oldpkg:
1894 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
1895 old_project = args[0]
1896 new_package = old_package = args[1]
1897 new_project = args[2]
1899 new_package = args[3]
1901 raise oscerr.WrongArgs('Wrong number of arguments')
1906 rev = int(opts.change)
1916 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1920 rev1, rev2 = parseRevisionOption(opts.revision)
1922 rdiff = server_diff(conf.config['apiurl'],
1923 old_project, old_package, rev1,
1924 new_project, new_package, rev2, not opts.plain)
1930 def do_install(self, subcmd, opts, *args):
1931 """${cmd_name}: install a package after build via zypper in -r
1933 Not implemented yet. Use osc repourls,
1934 select the url you best like (standard),
1935 chop off after the last /, this should work with zypper.
1942 args = slash_split(args)
1943 args = expand_proj_pack(args)
1946 ## if there is only one argument, and it ends in .ymp
1947 ## then fetch it, Parse XML to get the first
1948 ## metapackage.group.repositories.repository.url
1949 ## and construct zypper cmd's for all
1950 ## metapackage.group.software.item.name
1952 ## if args[0] is already an url, the use it as is.
1954 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])
1955 print self.do_install.__doc__
1956 print "Example: \n" + cmd
1959 def do_repourls(self, subcmd, opts, *args):
1960 """${cmd_name}: Shows URLs of .repo files
1962 Shows URLs on which to access the project .repos files (yum-style
1963 metadata) on download.opensuse.org.
1966 osc repourls [PROJECT]
1971 apiurl = conf.config['apiurl']
1975 elif len(args) == 0:
1976 project = store_read_project('.')
1977 apiurl = store_read_apiurl('.')
1979 raise oscerr.WrongArgs('Wrong number of arguments')
1981 # XXX: API should somehow tell that
1982 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1983 repos = get_repositories_of_project(apiurl, project)
1985 print url_tmpl % (project.replace(':', ':/'), repo, project)
1988 @cmdln.option('-r', '--revision', metavar='rev',
1989 help='checkout the specified revision. '
1990 'NOTE: if you checkout the complete project '
1991 'this option is ignored!')
1992 @cmdln.option('-e', '--expand-link', action='store_true',
1993 help='if a package is a link, check out the expanded '
1994 'sources (no-op, since this became the default)')
1995 @cmdln.option('-u', '--unexpand-link', action='store_true',
1996 help='if a package is a link, check out the _link file ' \
1997 'instead of the expanded sources')
1998 @cmdln.option('-c', '--current-dir', action='store_true',
1999 help='place PACKAGE folder in the current directory' \
2000 'instead of a PROJECT/PACKAGE directory')
2001 @cmdln.option('-s', '--source-service-files', action='store_true',
2002 help='server side generated files of source services' \
2003 'gets downloaded as well' )
2005 def do_checkout(self, subcmd, opts, *args):
2006 """${cmd_name}: Check out content from the repository
2008 Check out content from the repository server, creating a local working
2011 When checking out a single package, the option --revision can be used
2012 to specify a revision of the package to be checked out.
2014 When a package is a source link, then it will be checked out in
2015 expanded form. If --unexpand-link option is used, the checkout will
2016 instead produce the raw _link file plus patches.
2019 osc co PROJECT [PACKAGE] [FILE]
2020 osc co PROJECT # entire project
2021 osc co PROJECT PACKAGE # a package
2022 osc co PROJECT PACKAGE FILE # single file -> to current dir
2024 while inside a project directory:
2025 osc co PACKAGE # check out PACKAGE from project
2030 if opts.unexpand_link:
2034 if opts.source_service_files:
2035 service_files = True
2037 service_files = False
2039 args = slash_split(args)
2040 project = package = filename = None
2041 apiurl = conf.config['apiurl']
2043 project = project_dir = args[0]
2049 if args and len(args) == 1:
2050 localdir = os.getcwd()
2051 if is_project_dir(localdir):
2052 project = store_read_project(localdir)
2053 project_dir = localdir
2055 apiurl = store_read_apiurl(localdir)
2057 rev, dummy = parseRevisionOption(opts.revision)
2061 if rev and rev != "latest" and not checkRevision(project, package, rev):
2062 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2066 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2069 if opts.current_dir:
2071 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2072 prj_dir=project_dir, service_files=service_files, progress_obj=self.download_progress)
2073 print_request_list(apiurl, project, package)
2077 if sys.platform[:3] == 'win':
2078 prj_dir = prj_dir.replace(':', ';')
2079 if os.path.exists(prj_dir):
2080 sys.exit('osc: project \'%s\' already exists' % project)
2082 # check if the project does exist (show_project_meta will throw an exception)
2083 show_project_meta(apiurl, project)
2085 init_project_dir(apiurl, prj_dir, project)
2086 print statfrmt('A', prj_dir)
2089 for package in meta_get_packagelist(apiurl, project):
2091 checkout_package(apiurl, project, package, expand_link = expand_link, \
2092 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2093 except oscerr.LinkExpandError, e:
2094 print >>sys.stderr, 'Link cannot be expanded:\n', e
2095 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2096 # check out in unexpanded form at least
2097 checkout_package(apiurl, project, package, expand_link = False, \
2098 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2099 print_request_list(apiurl, project)
2102 raise oscerr.WrongArgs('Missing argument.\n\n' \
2103 + self.get_cmd_help('checkout'))
2106 @cmdln.option('-q', '--quiet', action='store_true',
2107 help='print as little as possible')
2108 @cmdln.option('-v', '--verbose', action='store_true',
2109 help='print extra information')
2111 def do_status(self, subcmd, opts, *args):
2112 """${cmd_name}: Show status of files in working copy
2114 Show the status of files in a local working copy, indicating whether
2115 files have been changed locally, deleted, added, ...
2117 The first column in the output specifies the status and is one of the
2118 following characters:
2119 ' ' no modifications
2124 '?' item is not under version control
2125 '!' item is missing (removed by non-osc command) or incomplete
2130 osc st file1 file2 ...
2133 osc status [OPTS] [PATH...]
2137 args = parseargs(args)
2139 # storage for single Package() objects
2141 # storage for a project dir ( { prj_instance : [ package objects ] } )
2144 # when 'status' is run inside a project dir, it should
2145 # stat all packages existing in the wc
2146 if is_project_dir(arg):
2147 prj = Project(arg, False)
2149 if conf.config['do_package_tracking']:
2151 for pac in prj.pacs_have:
2152 # we cannot create package objects if the dir does not exist
2153 if not pac in prj.pacs_broken:
2154 prjpacs[prj].append(os.path.join(arg, pac))
2156 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2157 elif is_package_dir(arg):
2158 pacpaths.append(arg)
2159 elif os.path.isfile(arg):
2160 pacpaths.append(arg)
2162 msg = '\'%s\' is neither a project or a package directory' % arg
2163 raise oscerr.NoWorkingCopy, msg
2165 # process single packages
2166 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2167 # process project dirs
2168 for prj, pacs in prjpacs.iteritems():
2169 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2171 print '\n'.join(lines)
2174 def do_add(self, subcmd, opts, *args):
2175 """${cmd_name}: Mark files to be added upon the next commit
2178 osc add FILE [FILE...]
2182 raise oscerr.WrongArgs('Missing argument.\n\n' \
2183 + self.get_cmd_help('add'))
2185 filenames = parseargs(args)
2189 def do_mkpac(self, subcmd, opts, *args):
2190 """${cmd_name}: Create a new package under version control
2193 osc mkpac new_package
2196 if not conf.config['do_package_tracking']:
2197 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2198 "in the [general] section in the configuration file"
2202 raise oscerr.WrongArgs('Wrong number of arguments.')
2204 createPackageDir(args[0])
2206 @cmdln.option('-r', '--recursive', action='store_true',
2207 help='If CWD is a project dir then scan all package dirs as well')
2209 def do_addremove(self, subcmd, opts, *args):
2210 """${cmd_name}: Adds new files, removes disappeared files
2212 Adds all files new in the local copy, and removes all disappeared files.
2214 ARG, if specified, is a package working copy.
2220 args = parseargs(args)
2222 for arg in arg_list:
2223 if is_project_dir(arg) and conf.config['do_package_tracking']:
2224 prj = Project(arg, False)
2225 for pac in prj.pacs_unvers:
2226 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2227 if os.path.isdir(pac_dir):
2228 addFiles([pac_dir], prj)
2229 for pac in prj.pacs_broken:
2230 if prj.get_state(pac) != 'D':
2231 prj.set_state(pac, 'D')
2232 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
2234 for pac in prj.pacs_have:
2235 state = prj.get_state(pac)
2236 if state != None and state != 'D':
2237 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2238 args.append(pac_dir)
2240 prj.write_packages()
2241 elif is_project_dir(arg):
2242 print >>sys.stderr, 'osc: addremove is not supported in a project dir unless ' \
2243 '\'do_package_tracking\' is enabled in the configuration file'
2246 pacs = findpacs(args)
2248 p.todo = p.filenamelist + p.filenamelist_unvers
2250 for filename in p.todo:
2251 if os.path.isdir(filename):
2253 # ignore foo.rXX, foo.mine for files which are in 'C' state
2254 if os.path.splitext(filename)[0] in p.in_conflict:
2256 state = p.status(filename)
2259 # TODO: should ignore typical backup files suffix ~ or .orig
2261 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
2263 p.put_on_deletelist(filename)
2264 p.write_deletelist()
2265 os.unlink(os.path.join(p.storedir, filename))
2266 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
2271 @cmdln.alias('checkin')
2272 @cmdln.option('-m', '--message', metavar='TEXT',
2273 help='specify log message TEXT')
2274 @cmdln.option('-F', '--file', metavar='FILE',
2275 help='read log message from FILE')
2276 @cmdln.option('-f', '--force', default=False, action="store_true",
2277 help='force commit - do not tests a file list')
2278 def do_commit(self, subcmd, opts, *args):
2279 """${cmd_name}: Upload content to the repository server
2281 Upload content which is changed in your working copy, to the repository
2284 Optionally checks the state of a working copy, if found a file with
2285 unknown state, it requests an user input:
2286 * skip - don't change anything, just move to another file
2287 * remove - remove a file from dir
2288 * edit file list - edit filelist using EDITOR
2289 * commit - don't check anything and commit package
2290 * abort - abort commit - this is default value
2291 This can be supressed by check_filelist config item, or -f/--force
2292 command line option.
2295 osc ci # current dir
2297 osc ci file1 file2 ...
2303 args = parseargs(args)
2310 msg = open(opts.file).read()
2312 sys.exit('could not open file \'%s\'.' % opts.file)
2315 for arg in arg_list:
2316 if conf.config['do_package_tracking'] and is_project_dir(arg):
2317 Project(arg).commit(msg=msg)
2319 msg = edit_message()
2322 pacs = findpacs(args)
2324 if conf.config['check_filelist'] and not opts.force:
2325 check_filelist_before_commit(pacs)
2328 template = store_read_file(os.path.abspath('.'), '_commit_msg')
2329 # open editor for commit message
2330 # but first, produce status and diff to append to the template
2334 changed = getStatus([pac], quiet=True)
2337 diffs += ['\nDiff for working copy: %s' % pac.dir]
2338 diffs += make_diff(pac, 0)
2339 lines.extend(get_commit_message_template(pac))
2340 if template == None:
2341 template='\n'.join(lines)
2342 # if footer is empty, there is nothing to commit, and no edit needed.
2344 msg = edit_message(footer='\n'.join(footer), template=template)
2347 store_write_string(os.path.abspath('.'), '_commit_msg', msg)
2349 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2351 if conf.config['do_package_tracking'] and len(pacs) > 0:
2355 # it is possible to commit packages from different projects at the same
2356 # time: iterate over all pacs and put each pac to the right project in the dict
2358 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
2359 if is_project_dir(path):
2360 pac_path = os.path.basename(os.path.normpath(pac.absdir))
2361 prj_paths.setdefault(path, []).append(pac_path)
2362 files[pac_path] = pac.todo
2364 single_paths.append(pac.dir)
2365 for prj, packages in prj_paths.iteritems():
2366 Project(prj).commit(tuple(packages), msg, files)
2367 for pac in single_paths:
2368 Package(pac).commit(msg)
2373 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2375 @cmdln.option('-r', '--revision', metavar='REV',
2376 help='update to specified revision (this option will be ignored '
2377 'if you are going to update the complete project or more than '
2379 @cmdln.option('-u', '--unexpand-link', action='store_true',
2380 help='if a package is an expanded link, update to the raw _link file')
2381 @cmdln.option('-e', '--expand-link', action='store_true',
2382 help='if a package is a link, update to the expanded sources')
2383 @cmdln.option('-s', '--source-service-files', action='store_true',
2384 help='Use server side generated sources instead of local generation.' )
2386 def do_update(self, subcmd, opts, *args):
2387 """${cmd_name}: Update a working copy
2392 If the current working directory is a package, update it.
2393 If the directory is a project directory, update all contained
2394 packages, AND check out newly added packages.
2396 To update only checked out packages, without checking out new
2397 ones, you might want to use "osc up *" from within the project
2401 Update the packages specified by the path argument(s)
2403 When --expand-link is used with source link packages, the expanded
2404 sources will be checked out. Without this option, the _link file and
2405 patches will be checked out. The option --unexpand-link can be used to
2406 switch back to the "raw" source with a _link file plus patch(es).
2412 if (opts.expand_link and opts.unexpand_link) \
2413 or (opts.expand_link and opts.revision) \
2414 or (opts.unexpand_link and opts.revision):
2415 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
2416 '--revision are mutually exclusive.')
2418 if opts.source_service_files: service_files = True
2419 else: service_files = False
2421 args = parseargs(args)
2424 for arg in arg_list:
2425 if is_project_dir(arg):
2426 prj = Project(arg, progress_obj=self.download_progress)
2428 if conf.config['do_package_tracking']:
2429 prj.update(expand_link=opts.expand_link,
2430 unexpand_link=opts.unexpand_link)
2433 # if not tracking package, and 'update' is run inside a project dir,
2434 # it should do the following:
2435 # (a) update all packages
2436 args += prj.pacs_have
2437 # (b) fetch new packages
2438 prj.checkout_missing_pacs(expand_link = not opts.unexpand_link)
2440 print_request_list(prj.apiurl, prj.name)
2443 pacs = findpacs(args, progress_obj=self.download_progress)
2445 if opts.revision and len(args) == 1:
2446 rev, dummy = parseRevisionOption(opts.revision)
2447 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
2448 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2455 print 'Updating %s' % p.name
2457 # FIXME: ugly workaround for #399247
2458 if opts.expand_link or opts.unexpand_link:
2459 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' and p.status(i) != '?']:
2460 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
2461 'copy has local modifications.\nPlease revert/commit them ' \
2466 if opts.expand_link and p.islink() and not p.isexpanded():
2467 if p.haslinkerror():
2469 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev)
2471 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev, linkrev="base")
2474 rev = p.linkinfo.xsrcmd5
2475 print 'Expanding to rev', rev
2476 elif opts.unexpand_link and p.islink() and p.isexpanded():
2477 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
2478 rev = p.linkinfo.lsrcmd5
2479 elif p.islink() and p.isexpanded():
2480 rev = p.latest_rev()
2482 p.update(rev, service_files)
2483 if opts.unexpand_link:
2486 print_request_list(p.apiurl, p.prjname, p.name)
2489 @cmdln.option('-f', '--force', action='store_true',
2490 help='forces removal of entire package and its files')
2493 @cmdln.alias('remove')
2494 def do_delete(self, subcmd, opts, *args):
2495 """${cmd_name}: Mark files or package directories to be deleted upon the next 'checkin'
2498 cd .../PROJECT/PACKAGE
2499 osc delete FILE [...]
2501 osc delete PACKAGE [...]
2503 This command works on check out copies. Use "rdelete" for working on server
2504 side only. This is needed for removing the entire project.
2506 As a safety measure, projects must be empty (i.e., you need to delete all
2509 If you are sure that you want to remove a package and all
2510 its files use \'--force\' switch. Sometimes this also works without --force.
2516 raise oscerr.WrongArgs('Missing argument.\n\n' \
2517 + self.get_cmd_help('delete'))
2519 args = parseargs(args)
2520 # check if args contains a package which was removed by
2521 # a non-osc command and mark it with the 'D'-state
2524 if not os.path.exists(i):
2525 prj_dir, pac_dir = getPrjPacPaths(i)
2526 if is_project_dir(prj_dir):
2527 prj = Project(prj_dir, False)
2528 if i in prj.pacs_broken:
2529 if prj.get_state(i) != 'A':
2530 prj.set_state(pac_dir, 'D')
2532 prj.del_package_node(i)
2533 print statfrmt('D', getTransActPath(i))
2535 prj.write_packages()
2536 pacs = findpacs(args)
2540 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
2541 if is_project_dir(prj_dir):
2542 if conf.config['do_package_tracking']:
2543 prj = Project(prj_dir, False)
2544 prj.delPackage(p, opts.force)
2546 print "WARNING: package tracking is disabled, operation skipped !"
2548 pathn = getTransActPath(p.dir)
2549 for filename in p.todo:
2550 ret, state = p.delete_file(filename, opts.force)
2552 print statfrmt('D', os.path.join(pathn, filename))
2555 sys.exit('\'%s\' is not under version control' % filename)
2556 elif state in ['A', 'M'] and not opts.force:
2557 sys.exit('\'%s\' has local modifications (use --force to remove this file)' % filename)
2560 def do_resolved(self, subcmd, opts, *args):
2561 """${cmd_name}: Remove 'conflicted' state on working copy files
2563 If an upstream change can't be merged automatically, a file is put into
2564 in 'conflicted' ('C') state. Within the file, conflicts are marked with
2565 special <<<<<<< as well as ======== and >>>>>>> lines.
2567 After manually resolving all conflicting parts, use this command to
2568 remove the 'conflicted' state.
2570 Note: this subcommand does not semantically resolve conflicts or
2571 remove conflict markers; it merely removes the conflict-related
2572 artifact files and allows PATH to be committed again.
2575 osc resolved FILE [FILE...]
2580 raise oscerr.WrongArgs('Missing argument.\n\n' \
2581 + self.get_cmd_help('resolved'))
2583 args = parseargs(args)
2584 pacs = findpacs(args)
2587 for filename in p.todo:
2588 print 'Resolved conflicted state of "%s"' % filename
2589 p.clear_from_conflictlist(filename)
2592 @cmdln.alias('platforms')
2593 def do_repositories(self, subcmd, opts, *args):
2594 """${cmd_name}: Shows available repositories
2598 Shows all available repositories/build targets
2600 2. osc repositories <project>
2601 Shows the configured repositories/build targets of a project
2609 print '\n'.join(get_repositories_of_project(conf.config['apiurl'], project))
2611 print '\n'.join(get_repositories(conf.config['apiurl']))
2615 def do_results_meta(self, subcmd, opts, *args):
2616 print "Command results_meta is obsolete. Please use: osc results --xml"
2620 @cmdln.option('-l', '--last-build', action='store_true',
2621 help='show last build results (succeeded/failed/unknown)')
2622 @cmdln.option('-r', '--repo', action='append', default = [],
2623 help='Show results only for specified repo(s)')
2624 @cmdln.option('-a', '--arch', action='append', default = [],
2625 help='Show results only for specified architecture(s)')
2626 @cmdln.option('', '--xml', action='store_true',
2627 help='generate output in XML (former results_meta)')
2628 def do_rresults(self, subcmd, opts, *args):
2629 print "Command rresults is obsolete. Running 'osc results' instead"
2630 self.do_results('results', opts, *args)
2634 @cmdln.option('-f', '--force', action='store_true', default=False,
2635 help="Don't ask and delete files")
2636 def do_rremove(self, subcmd, opts, project, package, *files):
2637 """${cmd_name}: Remove source files from selected package
2644 if not '/' in project:
2645 raise oscerr.WrongArgs("Missing operand, type osc help rremove for help")
2648 project, package = project.split('/')
2652 resp = raw_input("rm: remove source file `%s' from `%s/%s'? (yY|nN) " % (file, project, package))
2653 if resp not in ('y', 'Y'):
2656 delete_files(conf.config['apiurl'], project, package, (file, ))
2657 except urllib2.HTTPError, e:
2659 print >>sys.stderr, e
2661 if e.code in [ 400, 403, 404, 500 ]:
2662 if '<summary>' in body:
2663 msg = body.split('<summary>')[1]
2664 msg = msg.split('</summary>')[0]
2665 print >>sys.stderr, msg
2670 @cmdln.option('-l', '--last-build', action='store_true',
2671 help='show last build results (succeeded/failed/unknown)')
2672 @cmdln.option('-r', '--repo', action='append', default = [],
2673 help='Show results only for specified repo(s)')
2674 @cmdln.option('-a', '--arch', action='append', default = [],
2675 help='Show results only for specified architecture(s)')
2676 @cmdln.option('', '--xml', action='store_true',
2677 help='generate output in XML (former results_meta)')
2678 def do_results(self, subcmd, opts, *args):
2679 """${cmd_name}: Shows the build results of a package
2682 osc results (inside working copy)
2683 osc results remote_project remote_package
2688 args = slash_split(args)
2690 apiurl = conf.config['apiurl']
2693 if is_project_dir(wd):
2697 opts.hide_legend = None
2698 opts.name_filter = None
2699 opts.status_filter = None
2700 opts.vertical = None
2701 self.do_prjresults('prjresults', opts, *args)
2704 project = store_read_project(wd)
2705 package = store_read_package(wd)
2706 apiurl = store_read_apiurl(wd)
2708 raise oscerr.WrongArgs('Too few arguments (required none or two)')
2710 raise oscerr.WrongArgs('Too many arguments (required none or two)')
2719 func = show_results_meta
2722 print delim.join(func(apiurl, project, package, opts.last_build, opts.repo, opts.arch))
2724 # WARNING: this function is also called by do_results. You need to set a default there
2725 # as well when adding a new option!
2726 @cmdln.option('-q', '--hide-legend', action='store_true',
2727 help='hide the legend')
2728 @cmdln.option('-c', '--csv', action='store_true',
2730 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2731 help='show only packages with buildstatus STATUS (see legend)')
2732 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2733 help='show only packages whose names match EXPR')
2734 @cmdln.option('-a', '--arch', metavar='ARCH',
2735 help='show results only for specified architecture(s)')
2736 @cmdln.option('-r', '--repo', metavar='REPO',
2737 help='show results only for specified repo(s)')
2738 @cmdln.option('-V', '--vertical', action='store_true',
2739 help='list packages vertically instead horizontally')
2741 def do_prjresults(self, subcmd, opts, *args):