3 # Copyright (C) 2006 Novell Inc. All rights reserved.
4 # This program is free software; it may be used, copied, modified
5 # and distributed under the terms of the GNU General Public Licence,
6 # either version 2, or version 3 (at your option).
13 import urlgrabber.progress
14 from optparse import SUPPRESS_HELP
16 MAN_HEADER = r""".TH %(ucname)s "1" "%(date)s" "%(name)s %(version)s" "User Commands"
18 %(name)s \- openSUSE build service command-line tool.
21 [\fIGLOBALOPTS\fR] \fISUBCOMMAND \fR[\fIOPTS\fR] [\fIARGS\fR...]
26 openSUSE build service command-line tool.
30 Type 'osc help <subcommand>' for more detailed help on a specific subcommand.
32 For additional information, see
33 * http://www.opensuse.org/Build_Service_Tutorial
34 * http://www.opensuse.org/Build_Service/CLI
36 You can modify osc commands, or roll you own, via the plugin API:
37 * http://www.opensuse.org/Build_Service/osc_plugins
39 osc was written by several authors. This man page is automatically generated.
42 class OscTextMeter(urlgrabber.progress.TextMeter):
43 """show the progress bar immediately"""
44 def _do_start(self, *args, **kwargs):
45 urlgrabber.progress.TextMeter._do_start(self, *args, **kwargs)
48 class Osc(cmdln.Cmdln):
49 """Usage: osc [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]
50 or: osc help SUBCOMMAND
52 openSUSE build service command-line tool.
53 Type 'osc help <subcommand>' for help on a specific subcommand.
58 For additional information, see
59 * http://www.opensuse.org/Build_Service_Tutorial
60 * http://www.opensuse.org/Build_Service/CLI
62 You can modify osc commands, or roll you own, via the plugin API:
63 * http://www.opensuse.org/Build_Service/osc_plugins
68 man_header = MAN_HEADER
69 man_footer = MAN_FOOTER
71 def __init__(self, *args, **kwargs):
72 cmdln.Cmdln.__init__(self, *args, **kwargs)
73 cmdln.Cmdln.do_help.aliases.append('h')
75 def get_version(self):
76 return get_osc_version()
78 def get_optparser(self):
79 """this is the parser for "global" options (not specific to subcommand)"""
81 optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
82 optparser.add_option('--debugger', action='store_true',
83 help='jump into the debugger before executing anything')
84 optparser.add_option('--post-mortem', action='store_true',
85 help='jump into the debugger in case of errors')
86 optparser.add_option('-t', '--traceback', action='store_true',
87 help='print call trace in case of errors')
88 optparser.add_option('-H', '--http-debug', action='store_true',
89 help='debug HTTP traffic')
90 optparser.add_option('-d', '--debug', action='store_true',
91 help='print info useful for debugging')
92 optparser.add_option('-A', '--apiurl', dest='apiurl',
94 help='specify URL to access API server at or an alias')
95 optparser.add_option('-c', '--config', dest='conffile',
97 help='specify alternate configuration file')
98 optparser.add_option('--no-keyring', action='store_true',
99 help='disable usage of desktop keyring system')
100 optparser.add_option('--no-gnome-keyring', action='store_true',
101 help='disable usage of GNOME Keyring')
102 optparser.add_option('-v', '--verbose', dest='verbose', action='count', default=0,
103 help='increase verbosity')
104 optparser.add_option('-q', '--quiet', dest='verbose', action='store_const', const=-1,
105 help='be quiet, not verbose')
109 def postoptparse(self, try_again = True):
110 """merge commandline options into the config"""
112 conf.get_config(override_conffile = self.options.conffile,
113 override_apiurl = self.options.apiurl,
114 override_debug = self.options.debug,
115 override_http_debug = self.options.http_debug,
116 override_traceback = self.options.traceback,
117 override_post_mortem = self.options.post_mortem,
118 override_no_keyring = self.options.no_keyring,
119 override_no_gnome_keyring = self.options.no_gnome_keyring,
120 override_verbose = self.options.verbose)
121 except oscerr.NoConfigfile, e:
122 print >>sys.stderr, e.msg
123 print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
126 config['user'] = raw_input('Username: ')
127 config['pass'] = getpass.getpass()
128 if self.options.apiurl:
129 config['apiurl'] = self.options.apiurl
131 conf.write_initial_config(e.file, config)
132 print >>sys.stderr, 'done'
133 if try_again: self.postoptparse(try_again = False)
134 except oscerr.ConfigMissingApiurl, e:
135 print >>sys.stderr, e.msg
137 user = raw_input('Username: ')
138 passwd = getpass.getpass()
139 conf.add_section(e.file, e.url, user, passwd)
140 if try_again: self.postoptparse(try_again = False)
142 self.options.verbose = conf.config['verbose']
143 self.download_progress = None
144 if conf.config.get('show_download_progress', False):
145 self.download_progress = OscTextMeter()
148 def get_cmd_help(self, cmdname):
149 doc = self._get_cmd_handler(cmdname).__doc__
150 doc = self._help_reindent(doc)
151 doc = self._help_preprocess(doc, cmdname)
152 doc = doc.rstrip() + '\n' # trim down trailing space
153 return self._str(doc)
156 # overridden from class Cmdln() to use config variables in help texts
157 def _help_preprocess(self, help, cmdname):
158 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
159 return help % conf.config
162 def do_init(self, subcmd, opts, project, package=None):
163 """${cmd_name}: Initialize a directory as working copy
165 Initialize an existing directory to be a working copy of an
166 (already existing) buildservice project/package.
168 (This is the same as checking out a package and then copying sources
169 into the directory. It does NOT create a new package. To create a
170 package, use 'osc meta pkg ... ...')
172 You wouldn't normally use this command.
174 To get a working copy of a package (e.g. for building it or working on
175 it, you would normally use the checkout command. Use "osc help
176 checkout" to get help for it.
185 init_project_dir(conf.config['apiurl'], os.curdir, project)
186 print 'Initializing %s (Project: %s)' % (os.curdir, project)
188 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
189 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
195 @cmdln.option('-a', '--arch', metavar='ARCH',
196 help='specify architecture (only for binaries)')
197 @cmdln.option('-r', '--repo', metavar='REPO',
198 help='specify repository (only for binaries)')
199 @cmdln.option('-b', '--binaries', action='store_true',
200 help='list built binaries instead of sources')
201 @cmdln.option('-R', '--revision', metavar='REVISION',
202 help='specify revision (only for sources)')
203 @cmdln.option('-e', '--expand', action='store_true',
204 help='expand linked package (only for sources)')
205 @cmdln.option('-v', '--verbose', action='store_true',
206 help='print extra information')
207 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
208 help='print extra information')
209 def do_list(self, subcmd, opts, *args):
210 """${cmd_name}: List sources or binaries on the server
212 Examples for listing sources:
213 ls # list all projects
214 ls PROJECT # list packages in a project
215 ls PROJECT PACKAGE # list source files of package of a project
216 ls PROJECT PACKAGE <file> # list <file> if this file exists
217 ls -v PROJECT PACKAGE # verbosely list source files of package
218 ls -l PROJECT PACKAGE # verbosely list source files of package
219 ll PROJECT PACKAGE # verbosely list source files of package
220 LL PROJECT PACKAGE # verbosely list source files of expanded link
222 With --verbose, the following fields will be shown for each item:
224 Revision number of the last commit
226 Date and time of the last commit
228 Examples for listing binaries:
229 ls -b PROJECT # list all binaries of a project
230 ls -b PROJECT -a ARCH # list ARCH binaries of a project
231 ls -b PROJECT -r REPO # list binaries in REPO
232 ls -b PROJECT PACKAGE REPO ARCH
235 ${cmd_name} [PROJECT [PACKAGE]]
236 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
240 apiurl = conf.config['apiurl']
241 args = slash_split(args)
244 if subcmd == 'lL' or subcmd == 'LL':
258 if opts.repo != args[2]:
259 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
266 if not opts.binaries:
267 raise oscerr.WrongArgs('Too many arguments')
269 if opts.arch != args[3]:
270 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
275 if opts.binaries and opts.expand:
276 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
280 # ls -b toplevel doesn't make sense, so use info from
281 # current dir if available
284 if is_project_dir(dir):
285 project = store_read_project(dir)
286 apiurl = store_read_apiurl(dir)
287 elif is_package_dir(dir):
288 project = store_read_project(dir)
289 package = store_read_package(dir)
290 apiurl = store_read_apiurl(dir)
293 raise oscerr.WrongArgs('There are no binaries to list above project level.')
295 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
299 if opts.repo and opts.arch:
300 repos.append(Repo(opts.repo, opts.arch))
301 elif opts.repo and not opts.arch:
302 for repo in get_repos_of_project(apiurl, project):
303 if repo.name == opts.repo:
305 elif opts.arch and not opts.repo:
306 for repo in get_repos_of_project(apiurl, project):
307 if repo.arch == opts.arch:
310 repos = get_repos_of_project(apiurl, project)
314 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
316 for result in results:
319 print '%s/%s' % (result[0].name, result[0].arch)
324 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
330 elif not opts.binaries:
332 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
336 if self.options.verbose:
337 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
339 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
341 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
343 elif len(args) == 2 or len(args) == 3:
344 l = meta_get_filelist(conf.config['apiurl'],
347 verbose=opts.verbose,
349 revision=opts.revision)
351 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
352 for i in l if not fname or fname == i.name ]
355 print 'file \'%s\' does not exist' % fname
363 print 'file \'%s\' does not exist' % fname
368 @cmdln.option('-f', '--force', action='store_true',
369 help='force generation of new patchinfo file')
370 @cmdln.option('--force-update', action='store_true',
371 help='drops away collected packages from an already built patch and let it collect again')
372 def do_patchinfo(self, subcmd, opts, *args):
373 """${cmd_name}: Generate and edit a patchinfo file.
375 A patchinfo file describes the packages for an update and the kind of
380 osc patchinfo PATCH_NAME
384 project_dir = localdir = os.getcwd()
385 if is_project_dir(localdir):
386 project = store_read_project(localdir)
387 apiurl = store_read_apiurl(localdir)
389 sys.exit('This command must be called in a checked out project.')
391 for p in meta_get_packagelist(apiurl, project):
392 if p.startswith("_patchinfo:"):
395 if opts.force or not patchinfo:
396 print "Creating initial patchinfo..."
397 query='cmd=createpatchinfo'
399 query += "&name=" + args[0]
400 url = makeurl(apiurl, ['source', project], query=query)
402 for p in meta_get_packagelist(apiurl, project):
403 if p.startswith("_patchinfo:"):
406 if not os.path.exists(project_dir + "/" + patchinfo):
407 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
409 if sys.platform[:3] != 'win':
410 editor = os.getenv('EDITOR', default='vim')
412 editor = os.getenv('EDITOR', default='notepad')
413 subprocess.call('%s %s' % (editor, project_dir + "/" + patchinfo + "/_patchinfo"), shell=True)
416 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
417 help='affect only a given attribute')
418 @cmdln.option('--attribute-defaults', action='store_true',
419 help='include defined attribute defaults')
420 @cmdln.option('--attribute-project', action='store_true',
421 help='include project values, if missing in packages ')
422 @cmdln.option('-F', '--file', metavar='FILE',
423 help='read metadata from FILE, instead of opening an editor. '
424 '\'-\' denotes standard input. ')
425 @cmdln.option('-e', '--edit', action='store_true',
426 help='edit metadata')
427 @cmdln.option('-c', '--create', action='store_true',
428 help='create attribute without values')
429 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
430 help='set attribute values')
431 @cmdln.option('--delete', action='store_true',
432 help='delete a pattern or attribute')
433 def do_meta(self, subcmd, opts, *args):
434 """${cmd_name}: Show meta information, or edit it
436 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
438 This command displays metadata on buildservice objects like projects,
439 packages, or users. The type of metadata is specified by the word after
440 "meta", like e.g. "meta prj".
442 prj denotes metadata of a buildservice project.
443 prjconf denotes the (build) configuration of a project.
444 pkg denotes metadata of a buildservice package.
445 user denotes the metadata of a user.
446 pattern denotes installation patterns defined for a project.
448 To list patterns, use 'osc meta pattern PRJ'. An additional argument
449 will be the pattern file to view or edit.
451 With the --edit switch, the metadata can be edited. Per default, osc
452 opens the program specified by the environmental variable EDITOR with a
453 temporary file. Alternatively, content to be saved can be supplied via
454 the --file switch. If the argument is '-', input is taken from stdin:
455 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
457 When trying to edit a non-existing resource, it is created implicitly.
463 osc meta pkg PRJ PKG -e
464 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
467 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
468 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
469 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
470 osc meta pattern --delete PRJ PATTERN
474 args = slash_split(args)
476 if not args or args[0] not in metatypes.keys():
477 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
478 % ', '.join(metatypes))
484 min_args, max_args = 2, 2
485 elif cmd in ['pattern']:
486 min_args, max_args = 1, 2
487 elif cmd in ['attribute']:
488 min_args, max_args = 1, 3
490 min_args, max_args = 1, 1
491 if len(args) < min_args:
492 raise oscerr.WrongArgs('Too few arguments.')
493 if len(args) > max_args:
494 raise oscerr.WrongArgs('Too many arguments.')
501 project, package = args[0:2]
502 elif cmd == 'attribute':
508 if opts.attribute_project:
509 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
514 attributepath.append('source')
515 attributepath.append(project)
517 attributepath.append(package)
519 attributepath.append(subpackage)
520 attributepath.append('_attribute')
521 elif cmd == 'prjconf':
525 elif cmd == 'pattern':
531 # enforce pattern argument if needed
532 if opts.edit or opts.file:
533 raise oscerr.WrongArgs('A pattern file argument is required.')
536 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
538 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
540 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
541 elif cmd == 'attribute':
542 sys.stdout.write(''.join(show_attribute_meta(conf.config['apiurl'], project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
543 elif cmd == 'prjconf':
544 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
546 r = get_user_meta(conf.config['apiurl'], user)
548 sys.stdout.write(''.join(r))
549 elif cmd == 'pattern':
551 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
553 sys.stdout.write(''.join(r))
555 r = show_pattern_metalist(conf.config['apiurl'], project)
557 sys.stdout.write('\n'.join(r) + '\n')
560 if opts.edit and not opts.file:
562 edit_meta(metatype='prj',
564 path_args=quote_plus(project),
567 'user': conf.config['user']}))
569 edit_meta(metatype='pkg',
571 path_args=(quote_plus(project), quote_plus(package)),
574 'user': conf.config['user']}))
575 elif cmd == 'prjconf':
576 edit_meta(metatype='prjconf',
578 path_args=quote_plus(project),
581 edit_meta(metatype='user',
583 path_args=(quote_plus(user)),
584 template_args=({'user': user}))
585 elif cmd == 'pattern':
586 edit_meta(metatype='pattern',
588 path_args=(project, pattern),
591 # create attribute entry
592 if (opts.create or opts.set) and cmd == 'attribute':
593 if not opts.attribute:
594 raise oscerr.WrongOptions('no attribute given to create')
597 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
598 for i in opts.set.split(','):
599 values += '<value>%s</value>' % i
600 aname = opts.attribute.split(":")
601 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
602 url = makeurl(conf.config['apiurl'], attributepath)
603 for data in streamfile(url, http_POST, data=d):
604 sys.stdout.write(data)
613 f = open(opts.file).read()
615 sys.exit('could not open file \'%s\'.' % opts.file)
618 edit_meta(metatype='prj',
621 path_args=quote_plus(project))
623 edit_meta(metatype='pkg',
626 path_args=(quote_plus(project), quote_plus(package)))
627 elif cmd == 'prjconf':
628 edit_meta(metatype='prjconf',
631 path_args=quote_plus(project))
633 edit_meta(metatype='user',
636 path_args=(quote_plus(user)))
637 elif cmd == 'pattern':
638 edit_meta(metatype='pattern',
641 path_args=(project, pattern))
646 path = metatypes[cmd]['path']
648 path = path % (project, pattern)
649 u = makeurl(conf.config['apiurl'], [path])
651 elif cmd == 'attribute':
652 if not opts.attribute:
653 raise oscerr.WrongOptions('no attribute given to create')
654 attributepath.append(opts.attribute)
655 u = makeurl(conf.config['apiurl'], attributepath)
656 for data in streamfile(u, http_DELETE):
657 sys.stdout.write(data)
659 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
662 @cmdln.option('-m', '--message', metavar='TEXT',
663 help='specify message TEXT')
664 @cmdln.option('-r', '--revision', metavar='REV',
665 help='for "create", specify a certain source revision ID (the md5 sum)')
666 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
667 help='Superseding another request by this one')
668 @cmdln.option('--nodevelproject', action='store_true',
669 help='do not follow a defined devel project ' \
670 '(primary project where a package is developed)')
671 @cmdln.option('--cleanup', action='store_true',
672 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
673 @cmdln.option('--no-cleanup', action='store_true',
674 help='never remove source package on accept, but update its content')
675 @cmdln.option('--no-update', action='store_true',
676 help='never touch source package on accept (will break source links)')
677 @cmdln.option('-d', '--diff', action='store_true',
678 help='show diff only instead of creating the actual request')
679 @cmdln.option('--yes', action='store_true',
680 help='proceed without asking.')
682 @cmdln.alias("submitreq")
683 @cmdln.alias("submitpac")
684 def do_submitrequest(self, subcmd, opts, *args):
685 """${cmd_name}: Create request to submit source into another Project
687 [See http://en.opensuse.org/Build_Service/Collaboration for information
690 See the "request" command for showing and modifing existing requests.
693 osc submitreq [OPTIONS]
694 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
695 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
699 src_update = conf.config['submitrequest_on_accept_action'] or None
700 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
702 src_update = "cleanup"
703 elif opts.no_cleanup:
704 src_update = "update"
706 src_update = "noupdate"
708 args = slash_split(args)
710 # remove this block later again
711 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
712 if args and args[0] in oldcmds:
713 print "************************************************************************"
714 print "* WARNING: It looks that you are using this command with a *"
715 print "* deprecated syntax. *"
716 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
717 print "* to see the new syntax. *"
718 print "************************************************************************"
719 if args[0] == 'create':
725 raise oscerr.WrongArgs('Too many arguments.')
727 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
728 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
730 apiurl = conf.config['apiurl']
732 if len(args) == 0 and is_project_dir(os.getcwd()):
734 # submit requests for multiple packages are currently handled via multiple requests
735 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
736 project = store_read_project(os.curdir)
737 apiurl = store_read_apiurl(os.curdir)
743 # loop via all packages for checking their state
744 for p in meta_get_packagelist(apiurl, project):
745 if p.startswith("_patchinfo:"):
748 # get _link info from server, who knows about the local state ...
749 u = makeurl(apiurl, ['source', project, p])
751 root = ET.parse(f).getroot()
752 linkinfo = root.find('linkinfo')
754 print "Package ", p, " is not a source link."
755 sys.exit("This is currently not supported.")
756 if linkinfo.get('error'):
757 print "Package ", p, " is a broken source link."
758 sys.exit("Please fix this first")
759 t = linkinfo.get('project')
761 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
762 # Real fix is to ask the api if sources are modificated
763 # but there is no such call yet.
764 targetprojects.append(t)
766 print "Submitting package ", p
768 print " Skipping package ", p
770 print "Skipping package ", p, " since it is a source link pointing inside to the project."
774 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
775 print "\nEverything fine? Can we create the requests ? [y/n]"
776 if sys.stdin.read(1) != "y":
777 sys.exit("Aborted...")
779 # loop via all packages to do the action
781 result = create_submit_request(apiurl, project, p)
784 sys.exit("submit request creation failed")
785 sr_ids.append(result)
787 # create submit requests for all found patchinfos
791 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
794 for t in targetprojects:
795 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
796 (project, p, t, p, options_block)
800 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
801 (actionxml, cgi.escape(opts.message or ""))
802 u = makeurl(apiurl, ['request'], query='cmd=create')
803 f = http_POST(u, data=xml)
805 root = ET.parse(f).getroot()
806 sr_ids.append(root.get('id'))
808 print "Requests created: ",
811 sys.exit('Successfull finished')
814 # try using the working copy at hand
815 p = findpacs(os.curdir)[0]
816 src_project = p.prjname
819 if len(args) == 0 and p.islink():
820 dst_project = p.linkinfo.project
821 dst_package = p.linkinfo.package
823 dst_project = args[0]
825 dst_package = args[1]
827 dst_package = src_package
829 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
830 'Please provide it the target via commandline arguments.' % p.name)
832 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
833 if len(modified) > 0:
834 print 'Your working copy has local modifications.'
835 repl = raw_input('Proceed without committing the local changes? (y|N) ')
839 # get the arguments from the commandline
840 src_project, src_package, dst_project = args[0:3]
842 dst_package = args[3]
844 dst_package = src_package
846 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
847 + self.get_cmd_help('request'))
849 if not opts.nodevelproject:
852 devloc = show_develproject(apiurl, dst_project, dst_package)
853 except urllib2.HTTPError:
854 print >>sys.stderr, """\
855 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
856 % (dst_project, dst_package)
860 dst_project != devloc and \
861 src_project != devloc:
863 A different project, %s, is defined as the place where development
864 of the package %s primarily takes place.
865 Please submit there instead, or use --nodevelproject to force direct submission.""" \
866 % (devloc, dst_package)
871 if opts.diff or not opts.message:
873 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
874 rdiff += server_diff(apiurl,
875 dst_project, dst_package, opts.revision,
876 src_project, src_package, None, True)
882 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
883 user = conf.get_apiurl_usr(apiurl)
884 myreqs = [ i for i in reqs if i.state.who == user ]
887 print 'You already created the following submit request: %s.' % \
888 ', '.join([str(i.reqid) for i in myreqs ])
889 repl = raw_input('Supersede the old requests? (y/n/c) ')
890 if repl.lower() == 'c':
891 print >>sys.stderr, 'Aborting'
897 changes_re = re.compile(r'^--- .*\.changes ')
898 for line in rdiff.split('\n'):
899 if line.startswith('--- '):
900 if changes_re.match(line):
905 difflines.append(line)
906 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
908 result = create_submit_request(apiurl,
909 src_project, src_package,
910 dst_project, dst_package,
911 opts.message, orev=opts.revision, src_update=src_update)
912 if repl.lower() == 'y':
914 change_request_state(apiurl, str(req.reqid), 'superseded',
915 'superseded by %s' % result, result)
918 r = change_request_state(conf.config['apiurl'],
919 opts.supersede, 'superseded', opts.message or '', result)
921 print 'created request id', result
924 @cmdln.option('-m', '--message', metavar='TEXT',
925 help='specify message TEXT')
927 @cmdln.alias("deletereq")
928 def do_deleterequest(self, subcmd, opts, *args):
929 """${cmd_name}: Create request to delete a package or project
933 osc deletereq [-m TEXT] PROJECT [PACKAGE]
937 args = slash_split(args)
940 raise oscerr.WrongArgs('Please specify at least a project.')
942 raise oscerr.WrongArgs('Too many arguments.')
944 apiurl = conf.config['apiurl']
952 opts.message = edit_message()
954 result = create_delete_request(apiurl, project, package, opts.message)
958 @cmdln.option('-m', '--message', metavar='TEXT',
959 help='specify message TEXT')
961 @cmdln.alias("changedevelreq")
962 def do_changedevelrequest(self, subcmd, opts, *args):
963 """${cmd_name}: Create request to change the devel package definition.
965 [See http://en.opensuse.org/Build_Service/Collaboration for information
968 See the "request" command for showing and modifing existing requests.
970 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
974 raise oscerr.WrongArgs('Too many arguments.')
976 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
978 devel_project = store_read_project(wd)
979 devel_package = package = store_read_package(wd)
980 apiurl = store_read_apiurl(wd)
981 project = conf.config['getpac_default_project']
984 raise oscerr.WrongArgs('Too few arguments.')
986 apiurl = conf.config['apiurl']
988 devel_project = args[2]
991 devel_package = package
993 devel_package = args[3]
997 footer=textwrap.TextWrapper(width = 66).fill(
998 'please explain why you like to change the devel project of %s/%s to %s/%s'
999 % (project,package,devel_project,devel_package))
1000 opts.message = edit_message(footer)
1002 result = create_change_devel_request(apiurl,
1003 devel_project, devel_package,
1009 @cmdln.option('-d', '--diff', action='store_true',
1010 help='generate a diff')
1011 @cmdln.option('-u', '--unified', action='store_true',
1012 help='output the diff in the unified diff format')
1013 @cmdln.option('-m', '--message', metavar='TEXT',
1014 help='specify message TEXT')
1015 @cmdln.option('-t', '--type', metavar='TYPE',
1016 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1017 @cmdln.option('-a', '--all', action='store_true',
1018 help='all states. Same as\'-s all\'')
1019 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1020 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]')
1021 @cmdln.option('-D', '--days', metavar='DAYS',
1022 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1023 @cmdln.option('-U', '--user', metavar='USER',
1024 help='same as -M, but for the specified USER')
1025 @cmdln.option('-b', '--brief', action='store_true', default=False,
1026 help='print output in list view as list subcommand')
1027 @cmdln.option('-M', '--mine', action='store_true',
1028 help='only show requests created by yourself')
1029 @cmdln.option('-B', '--bugowner', action='store_true',
1030 help='also show requests about packages where I am bugowner')
1031 @cmdln.option('-i', '--interactive', action='store_true',
1032 help='interactive review of request')
1034 @cmdln.alias("review")
1035 def do_request(self, subcmd, opts, *args):
1036 """${cmd_name}: Show and modify requests
1038 [See http://en.opensuse.org/Build_Service/Collaboration for information
1041 This command shows and modifies existing requests. To create new requests
1042 you need to call one of the following:
1045 osc changedevelrequest
1046 To send low level requests to the buildservice API, use:
1049 This command has the following sub commands:
1051 "list" lists open requests attached to a project or package or person.
1052 Uses the project/package of the current directory if none of
1053 -M, -U USER, project/package are given.
1055 "log" will show the history of the given ID
1057 "show" will show the request itself, and generate a diff for review, if
1058 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1060 "decline" will change the request state to "declined" and append a
1061 message that you specify with the --message option.
1063 "wipe" will permanently delete a request.
1065 "revoke" will set the request state to "revoked" and append a
1066 message that you specify with the --message option.
1068 "accept" will change the request state to "accepted" and will trigger
1069 the actual submit process. That would normally be a server-side copy of
1070 the source package to the target package.
1072 "checkout" will checkout the request's source package. This only works for "submit" requests.
1075 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1077 osc request [show] [-d] [-b] ID
1078 osc request accept [-m TEXT] ID
1079 osc request decline [-m TEXT] ID
1080 osc request revoke [-m TEXT] ID
1082 osc request checkout/co ID
1083 osc review accept [-m TEXT] ID
1084 osc review decline [-m TEXT] ID
1088 args = slash_split(args)
1090 if opts.all and opts.state:
1091 raise oscerr.WrongOptions('Sorry, the options --all and --state ' \
1092 'are mutually exclusive.')
1093 if opts.mine and opts.user:
1094 raise oscerr.WrongOptions('Sorry, the options --user and --mine ' \
1095 'are mutually exclusive.')
1100 if opts.state == '':
1103 if opts.state == '':
1106 cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co', 'help']
1107 if not args or args[0] not in cmds:
1108 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1109 % (args[0],', '.join(cmds)))
1115 return self.do_help(['help', 'request'])
1118 min_args, max_args = 1, 1
1119 elif cmd in ['list']:
1120 min_args, max_args = 0, 2
1122 min_args, max_args = 1, 1
1123 if len(args) < min_args:
1124 raise oscerr.WrongArgs('Too few arguments.')
1125 if len(args) > max_args:
1126 raise oscerr.WrongArgs('Too many arguments.')
1128 apiurl = conf.config['apiurl']
1135 elif not opts.mine and not opts.user:
1137 project = store_read_project(os.curdir)
1138 apiurl = store_read_apiurl(os.curdir)
1139 package = store_read_package(os.curdir)
1140 except oscerr.NoWorkingCopy:
1145 elif cmd in ['log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1150 states = ('new', 'accepted', 'revoked', 'declined')
1151 state_list = opts.state.split(',')
1152 if opts.state == 'all':
1153 state_list = ['all']
1155 for s in state_list:
1157 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1160 who = conf.get_apiurl_usr(apiurl)
1164 state_list = ['all']
1166 ## FIXME -B not implemented!
1168 if (self.options.debug):
1169 print 'list: option --bugowner ignored: not impl.'
1171 results = get_request_list(apiurl,
1172 project, package, who, state_list, opts.type)
1173 results.sort(reverse=True)
1175 days = opts.days or conf.config['request_list_days']
1182 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1185 ## bs has received 2009-09-20 a new xquery compare() function
1186 ## which allows us to limit the list inside of get_request_list
1187 ## That would be much faster for coolo. But counting the remainder
1188 ## would not be possible with current xquery implementation.
1189 ## Workaround: fetch all, and filter on client side.
1191 ## FIXME: date filtering should become implemented on server side
1192 for result in results:
1193 if days == 0 or result.state.when > since or result.state.name == 'new':
1194 print result.list_view()
1198 print "There are %d requests older than %s days.\n" % (skipped, days)
1201 for l in get_request_log(conf.config['apiurl'], reqid):
1206 r = get_request(conf.config['apiurl'], reqid)
1209 elif opts.interactive or conf.config['request_show_interactive']:
1210 return request_interactive_review(conf.config['apiurl'], r)
1213 # fixme: will inevitably fail if the given target doesn't exist
1216 print server_diff(conf.config['apiurl'],
1217 r.actions[0].dst_project, r.actions[0].dst_package, None,
1218 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified)
1219 except urllib2.HTTPError, e:
1220 e.osc_msg = 'Diff not possible'
1224 elif cmd == 'checkout' or cmd == 'co':
1225 r = get_request(conf.config['apiurl'], reqid)
1226 submits = [ i for i in r.actions if i.type == 'submit' ]
1227 if not len(submits):
1228 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1229 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1230 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1233 if not opts.message:
1234 opts.message = edit_message()
1235 state_map = {'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1236 # Change review state only
1237 if subcmd == 'review':
1238 if cmd in ['accept', 'decline']:
1239 r = change_review_state(conf.config['apiurl'],
1240 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1242 # Change state of entire request
1243 elif cmd in ['accept', 'decline', 'wipe', 'revoke']:
1244 r = change_request_state(conf.config['apiurl'],
1245 reqid, state_map[cmd], opts.message or '')
1248 # editmeta and its aliases are all depracated
1249 @cmdln.alias("editprj")
1250 @cmdln.alias("createprj")
1251 @cmdln.alias("editpac")
1252 @cmdln.alias("createpac")
1253 @cmdln.alias("edituser")
1254 @cmdln.alias("usermeta")
1256 def do_editmeta(self, subcmd, opts, *args):
1259 Obsolete command to edit metadata. Use 'meta' now.
1261 See the help output of 'meta'.
1265 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1266 print >>sys.stderr, 'See \'osc help meta\'.'
1267 #self.do_help([None, 'meta'])
1271 @cmdln.option('-r', '--revision', metavar='rev',
1272 help='use the specified revision.')
1273 @cmdln.option('-u', '--unset', action='store_true',
1274 help='remove revision in link, it will point always to latest revision')
1275 def do_setlinkrev(self, subcmd, opts, *args):
1276 """${cmd_name}: Updates a revision number in a source link.
1278 This command adds or updates a specified revision number in a source link.
1279 The current revision of the source is used, if no revision number is specified.
1283 osc setlinkrev PROJECT [PACKAGE]
1287 args = slash_split(args)
1288 apiurl = conf.config['apiurl']
1291 p = findpacs(os.curdir)[0]
1296 sys.exit('Local directory is no checked out source link package, aborting')
1297 elif len(args) == 2:
1300 elif len(args) == 1:
1303 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1304 + self.get_cmd_help('setlinkrev'))
1307 packages = [ package ]
1309 packages = meta_get_packagelist(apiurl, project)
1312 print "setting revision for package", p
1316 rev, dummy = parseRevisionOption(opts.revision)
1317 set_link_rev(apiurl, project, p, rev)
1320 def do_linktobranch(self, subcmd, opts, *args):
1321 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1323 This command tells the server to convert a _link with or without a project.diff
1324 to a branch. This is a full copy with a _link file pointing to the branched place.
1327 osc linktobranch # can be used in checked out package
1328 osc linktobranch PROJECT PACKAGE
1332 args = slash_split(args)
1335 project = store_read_project(wd)
1336 package = store_read_package(wd)
1337 apiurl = store_read_apiurl(wd)
1338 update_local_dir = True
1340 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1342 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1344 apiurl = conf.config['apiurl']
1347 update_local_dir = False
1350 link_to_branch(apiurl, project, package)
1351 if update_local_dir:
1353 pac.update(rev=pac.latest_rev())
1356 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1357 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1358 @cmdln.option('-c', '--current', action='store_true',
1359 help='link fixed against current revision.')
1360 @cmdln.option('-r', '--revision', metavar='rev',
1361 help='link the specified revision.')
1362 @cmdln.option('-f', '--force', action='store_true',
1363 help='overwrite an existing link file if it is there.')
1364 @cmdln.option('-d', '--disable-publish', action='store_true',
1365 help='disable publishing of the linked package')
1366 def do_linkpac(self, subcmd, opts, *args):
1367 """${cmd_name}: "Link" a package to another package
1369 A linked package is a clone of another package, but plus local
1370 modifications. It can be cross-project.
1372 The DESTPAC name is optional; the source packages' name will be used if
1375 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1377 To add a patch, add the patch as file and add it to the _link file.
1378 You can also specify text which will be inserted at the top of the spec file.
1380 See the examples in the _link file.
1383 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1387 args = slash_split(args)
1389 if not args or len(args) < 3:
1390 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1391 + self.get_cmd_help('linkpac'))
1393 rev, dummy = parseRevisionOption(opts.revision)
1395 src_project = args[0]
1396 src_package = args[1]
1397 dst_project = args[2]
1399 dst_package = args[3]
1401 dst_package = src_package
1403 if src_project == dst_project and src_package == dst_package:
1404 raise oscerr.WrongArgs('Error: source and destination are the same.')
1406 if src_project == dst_project and not opts.cicount:
1407 # in this case, the user usually wants to build different spec
1408 # files from the same source
1409 opts.cicount = "copy"
1412 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1414 if rev and not checkRevision(src_project, src_package, rev):
1415 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1418 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1420 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1421 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1422 @cmdln.option('-d', '--disable-publish', action='store_true',
1423 help='disable publishing of the aggregated package')
1424 def do_aggregatepac(self, subcmd, opts, *args):
1425 """${cmd_name}: "Aggregate" a package to another package
1427 Aggregation of a package means that the build results (binaries) of a
1428 package are basically copied into another project.
1429 This can be used to make packages available from building that are
1430 needed in a project but available only in a different project. Note
1431 that this is done at the expense of disk space. See
1432 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1433 for more information.
1435 The DESTPAC name is optional; the source packages' name will be used if
1439 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1443 args = slash_split(args)
1445 if not args or len(args) < 3:
1446 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1447 + self.get_cmd_help('aggregatepac'))
1449 src_project = args[0]
1450 src_package = args[1]
1451 dst_project = args[2]
1453 dst_package = args[3]
1455 dst_package = src_package
1457 if src_project == dst_project and src_package == dst_package:
1458 raise oscerr.WrongArgs('Error: source and destination are the same.')
1462 for pair in opts.map_repo.split(','):
1463 src_tgt = pair.split('=')
1464 if len(src_tgt) != 2:
1465 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1466 repo_map[src_tgt[0]] = src_tgt[1]
1468 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1471 @cmdln.option('-c', '--client-side-copy', action='store_true',
1472 help='do a (slower) client-side copy')
1473 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1474 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1475 @cmdln.option('-d', '--keep-develproject', action='store_true',
1476 help='keep develproject tag in the package metadata')
1477 @cmdln.option('-r', '--revision', metavar='rev',
1478 help='link the specified revision.')
1479 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1480 help='URL of destination api server. Default is the source api server.')
1481 @cmdln.option('-m', '--message', metavar='TEXT',
1482 help='specify message TEXT')
1483 @cmdln.option('-e', '--expand', action='store_true',
1484 help='if the source package is a link then copy the expanded version of the link')
1485 def do_copypac(self, subcmd, opts, *args):
1486 """${cmd_name}: Copy a package
1488 A way to copy package to somewhere else.
1490 It can be done across buildservice instances, if the -t option is used.
1491 In that case, a client-side copy is implied.
1493 Using --client-side-copy always involves downloading all files, and
1494 uploading them to the target.
1496 The DESTPAC name is optional; the source packages' name will be used if
1500 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1504 args = slash_split(args)
1506 if not args or len(args) < 3:
1507 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1508 + self.get_cmd_help('copypac'))
1510 src_project = args[0]
1511 src_package = args[1]
1512 dst_project = args[2]
1514 dst_package = args[3]
1516 dst_package = src_package
1518 src_apiurl = conf.config['apiurl']
1520 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1522 dst_apiurl = src_apiurl
1524 if src_project == dst_project and \
1525 src_package == dst_package and \
1526 src_apiurl == dst_apiurl:
1527 raise oscerr.WrongArgs('Source and destination are the same.')
1529 if src_apiurl != dst_apiurl:
1530 opts.client_side_copy = True
1532 rev, dummy = parseRevisionOption(opts.revision)
1535 comment = opts.message
1538 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1539 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1541 r = copy_pac(src_apiurl, src_project, src_package,
1542 dst_apiurl, dst_project, dst_package,
1543 client_side_copy=opts.client_side_copy,
1544 keep_maintainers=opts.keep_maintainers,
1545 keep_develproject=opts.keep_develproject,
1552 @cmdln.option('-c', '--checkout', action='store_true',
1553 help='Checkout branched package afterwards ' \
1554 '(\'osc bco\' is a shorthand for this option)' )
1555 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
1556 help='Use this attribute to find affected packages (default is OBS:Maintained)')
1557 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
1558 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
1559 def do_mbranch(self, subcmd, opts, *args):
1560 """${cmd_name}: Multiple branch of a package
1562 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
1565 This command is used for creating multiple links of defined version of a package
1566 in one project. This is esp. used for maintenance updates.
1568 The branched package will live in
1569 home:USERNAME:branches:ATTRIBUTE:PACKAGE
1570 if nothing else specified.
1573 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
1576 args = slash_split(args)
1579 maintained_attribute = conf.config['maintained_attribute']
1580 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
1582 if not len(args) or len(args) > 2:
1583 raise oscerr.WrongArgs('Wrong number of arguments.')
1589 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
1593 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
1596 print "Project " + r + " created."
1599 init_project_dir(conf.config['apiurl'], r, r)
1600 print statfrmt('A', r)
1603 for package in meta_get_packagelist(conf.config['apiurl'], r):
1605 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
1607 print >>sys.stderr, 'Error while checkout package:\n', package
1609 if conf.config['verbose']:
1610 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1613 @cmdln.alias('branchco')
1615 @cmdln.alias('getpac')
1616 @cmdln.option('--nodevelproject', action='store_true',
1617 help='do not follow a defined devel project ' \
1618 '(primary project where a package is developed)')
1619 @cmdln.option('-c', '--checkout', action='store_true',
1620 help='Checkout branched package afterwards ' \
1621 '(\'osc bco\' is a shorthand for this option)' )
1622 @cmdln.option('-r', '--revision', metavar='rev',
1623 help='branch against a specific revision')
1624 def do_branch(self, subcmd, opts, *args):
1625 """${cmd_name}: Branch a package
1627 [See http://en.opensuse.org/Build_Service/Collaboration for information
1630 Create a source link from a package of an existing project to a new
1631 subproject of the requesters home project (home:branches:)
1633 The branched package will live in
1634 home:USERNAME:branches:PROJECT/PACKAGE
1635 if nothing else specified.
1637 With getpac or bco, the branched package will come from
1638 %(getpac_default_project)s
1639 if nothing else specified.
1642 osc branch SOURCEPROJECT SOURCEPACKAGE
1643 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
1644 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
1645 osc getpac SOURCEPACKAGE
1650 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
1651 args = slash_split(args)
1652 tproject = tpackage = None
1654 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
1655 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
1656 # python has no args.unshift ???
1657 args = [ conf.config['getpac_default_project'] , args[0] ]
1659 if len(args) < 2 or len(args) > 4:
1660 raise oscerr.WrongArgs('Wrong number of arguments.')
1661 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
1663 expected = tproject = args[2]
1667 exists, targetprj, targetpkg, srcprj, srcpkg = \
1668 branch_pkg(conf.config['apiurl'], args[0], args[1],
1669 nodevelproject=opts.nodevelproject, rev=opts.revision,
1670 target_project=tproject, target_package=tpackage,
1671 return_existing=opts.checkout)
1673 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
1676 if not exists and (srcprj is not None and srcprj != args[0] or \
1677 srcprj is None and targetprj != expected):
1678 devloc = srcprj or targetprj
1679 if not srcprj and 'branches:' in targetprj:
1680 devloc = targetprj.split('branches:')[1]
1681 print '\nNote: The branch has been created of a different project,\n' \
1683 ' which is the primary location of where development for\n' \
1684 ' that package takes place.\n' \
1685 ' That\'s also where you would normally make changes against.\n' \
1686 ' A direct branch of the specified package can be forced\n' \
1687 ' with the --nodevelproject option.\n' % devloc
1689 package = tpackage or args[1]
1691 checkout_package(conf.config['apiurl'], targetprj, package,
1692 expand_link=True, prj_dir=targetprj)
1693 if conf.config['verbose']:
1694 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1697 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
1698 apiopt = '-A %s ' % conf.config['apiurl']
1699 print 'A working copy of the branched package can be checked out with:\n\n' \
1701 % (apiopt, targetprj, package)
1702 print_request_list(conf.config['apiurl'], args[0], args[1])
1704 print_request_list(conf.config['apiurl'], devloc, args[1])
1708 @cmdln.option('-f', '--force', action='store_true',
1709 help='deletes a package or an empty project')
1710 def do_rdelete(self, subcmd, opts, *args):
1711 """${cmd_name}: Delete a project or packages on the server.
1713 As a safety measure, project must be empty (i.e., you need to delete all
1714 packages first). If you are sure that you want to remove this project and all
1715 its packages use \'--force\' switch.
1718 osc rdelete -f PROJECT
1719 osc rdelete PROJECT PACKAGE [PACKAGE ...]
1724 args = slash_split(args)
1726 raise oscerr.WrongArgs('Missing argument.')
1732 # careful: if pkg is an empty string, the package delete request results
1733 # into a project delete request - which works recursively...
1735 delete_package(conf.config['apiurl'], prj, pkg)
1736 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
1737 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
1738 'If you are sure that you want to remove this project and all its ' \
1739 'packages use the \'--force\' switch'
1742 delete_project(conf.config['apiurl'], prj)
1745 def do_deletepac(self, subcmd, opts, *args):
1746 print """${cmd_name} is obsolete !
1749 osc delete for checked out packages or projects
1751 osc rdelete for server side operations."""
1756 @cmdln.option('-f', '--force', action='store_true',
1757 help='deletes a project and its packages')
1758 def do_deleteprj(self, subcmd, opts, project):
1759 """${cmd_name} is obsolete !
1766 @cmdln.alias('metafromspec')
1767 @cmdln.option('', '--specfile', metavar='FILE',
1768 help='Path to specfile. (if you pass more than working copy this option is ignored)')
1769 def do_updatepacmetafromspec(self, subcmd, opts, *args):
1770 """${cmd_name}: Update package meta information from a specfile
1772 ARG, if specified, is a package working copy.
1778 args = parseargs(args)
1779 if opts.specfile and len(args) == 1:
1780 specfile = opts.specfile
1783 pacs = findpacs(args)
1785 p.read_meta_from_spec(specfile)
1786 p.update_package_meta()
1790 @cmdln.option('-c', '--change', metavar='rev',
1791 help='the change made by revision rev (like -r rev-1:rev).'
1792 'If rev is negative this is like -r rev:rev-1.')
1793 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
1794 help='If rev1 is specified it will compare your working copy against '
1795 'the revision (rev1) on the server. '
1796 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
1797 '(NOTE: changes in your working copy are ignored in this case)')
1798 @cmdln.option('-p', '--plain', action='store_true',
1799 help='output the diff in plain (not unified) diff format')
1800 def do_diff(self, subcmd, opts, *args):
1801 """${cmd_name}: Generates a diff
1803 Generates a diff, comparing local changes against the repository
1806 ARG, specified, is a filename to include in the diff.
1812 args = parseargs(args)
1813 pacs = findpacs(args)
1817 rev = int(opts.change)
1827 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1830 rev1, rev2 = parseRevisionOption(opts.revision)
1834 diff += ''.join(make_diff(pac, rev1))
1836 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
1837 pac.prjname, pac.name, rev2, not opts.plain)
1842 @cmdln.option('--oldprj', metavar='OLDPRJ',
1843 help='project to compare against'
1844 ' (deprecated, use 3 argument form)')
1845 @cmdln.option('--oldpkg', metavar='OLDPKG',
1846 help='package to compare against'
1847 ' (deprecated, use 3 argument form)')
1848 @cmdln.option('-r', '--revision', metavar='N[:M]',
1849 help='revision id, where N = old revision and M = new revision')
1850 @cmdln.option('-p', '--plain', action='store_true',
1851 help='output the diff in plain (not unified) diff format')
1852 @cmdln.option('-c', '--change', metavar='rev',
1853 help='the change made by revision rev (like -r rev-1:rev). '
1854 'If rev is negative this is like -r rev:rev-1.')
1855 def do_rdiff(self, subcmd, opts, *args):
1856 """${cmd_name}: Server-side "pretty" diff of two packages
1858 Compares two packages (three or four arguments) or shows the
1859 changes of a specified revision of a package (two arguments)
1861 If no revision is specified the latest revision is used.
1863 Note that this command doesn't return a normal diff (which could be
1864 applied as patch), but a "pretty" diff, which also compares the content
1869 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
1870 osc ${cmd_name} PROJECT PACKAGE
1874 args = slash_split(args)
1885 new_project = args[0]
1886 new_package = args[1]
1888 old_project = opts.oldprj
1890 old_package = opts.oldpkg
1891 elif len(args) == 3 or len(args) == 4:
1892 if opts.oldprj or opts.oldpkg:
1893 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
1894 old_project = args[0]
1895 new_package = old_package = args[1]
1896 new_project = args[2]
1898 new_package = args[3]
1900 raise oscerr.WrongArgs('Wrong number of arguments')
1905 rev = int(opts.change)
1915 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1919 rev1, rev2 = parseRevisionOption(opts.revision)
1921 rdiff = server_diff(conf.config['apiurl'],
1922 old_project, old_package, rev1,
1923 new_project, new_package, rev2, not opts.plain)
1929 def do_install(self, subcmd, opts, *args):
1930 """${cmd_name}: install a package after build via zypper in -r
1932 Not implemented yet. Use osc repourls,
1933 select the url you best like (standard),
1934 chop off after the last /, this should work with zypper.
1941 args = slash_split(args)
1942 args = expand_proj_pack(args)
1945 ## if there is only one argument, and it ends in .ymp
1946 ## then fetch it, Parse XML to get the first
1947 ## metapackage.group.repositories.repository.url
1948 ## and construct zypper cmd's for all
1949 ## metapackage.group.software.item.name
1951 ## if args[0] is already an url, the use it as is.
1953 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])
1954 print self.do_install.__doc__
1955 print "Example: \n" + cmd
1958 def do_repourls(self, subcmd, opts, *args):
1959 """${cmd_name}: Shows URLs of .repo files
1961 Shows URLs on which to access the project .repos files (yum-style
1962 metadata) on download.opensuse.org.
1965 osc repourls [PROJECT]
1970 apiurl = conf.config['apiurl']
1974 elif len(args) == 0:
1975 project = store_read_project('.')
1976 apiurl = store_read_apiurl('.')
1978 raise oscerr.WrongArgs('Wrong number of arguments')
1980 # XXX: API should somehow tell that
1981 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1982 repos = get_repositories_of_project(apiurl, project)
1984 print url_tmpl % (project.replace(':', ':/'), repo, project)
1987 @cmdln.option('-r', '--revision', metavar='rev',
1988 help='checkout the specified revision. '
1989 'NOTE: if you checkout the complete project '
1990 'this option is ignored!')
1991 @cmdln.option('-e', '--expand-link', action='store_true',
1992 help='if a package is a link, check out the expanded '
1993 'sources (no-op, since this became the default)')
1994 @cmdln.option('-u', '--unexpand-link', action='store_true',
1995 help='if a package is a link, check out the _link file ' \
1996 'instead of the expanded sources')
1997 @cmdln.option('-c', '--current-dir', action='store_true',
1998 help='place PACKAGE folder in the current directory' \
1999 'instead of a PROJECT/PACKAGE directory')
2000 @cmdln.option('-s', '--source-service-files', action='store_true',
2001 help='server side generated files of source services' \
2002 'gets downloaded as well' )
2004 def do_checkout(self, subcmd, opts, *args):
2005 """${cmd_name}: Check out content from the repository
2007 Check out content from the repository server, creating a local working
2010 When checking out a single package, the option --revision can be used
2011 to specify a revision of the package to be checked out.
2013 When a package is a source link, then it will be checked out in
2014 expanded form. If --unexpand-link option is used, the checkout will
2015 instead produce the raw _link file plus patches.
2018 osc co PROJECT [PACKAGE] [FILE]
2019 osc co PROJECT # entire project
2020 osc co PROJECT PACKAGE # a package
2021 osc co PROJECT PACKAGE FILE # single file -> to current dir
2023 while inside a project directory:
2024 osc co PACKAGE # check out PACKAGE from project
2029 if opts.unexpand_link:
2033 if opts.source_service_files:
2034 service_files = True
2036 service_files = False
2038 args = slash_split(args)
2039 project = package = filename = None
2040 apiurl = conf.config['apiurl']
2042 project = project_dir = args[0]
2048 if args and len(args) == 1:
2049 localdir = os.getcwd()
2050 if is_project_dir(localdir):
2051 project = store_read_project(localdir)
2052 project_dir = localdir
2054 apiurl = store_read_apiurl(localdir)
2056 rev, dummy = parseRevisionOption(opts.revision)
2060 if rev and rev != "latest" and not checkRevision(project, package, rev):
2061 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2065 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2068 if opts.current_dir:
2070 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2071 prj_dir=project_dir, service_files=service_files, progress_obj=self.download_progress)
2072 print_request_list(apiurl, project, package)
2076 if sys.platform[:3] == 'win':
2077 prj_dir = prj_dir.replace(':', ';')
2078 if os.path.exists(prj_dir):
2079 sys.exit('osc: project \'%s\' already exists' % project)
2081 # check if the project does exist (show_project_meta will throw an exception)
2082 show_project_meta(apiurl, project)
2084 init_project_dir(apiurl, prj_dir, project)
2085 print statfrmt('A', prj_dir)
2088 for package in meta_get_packagelist(apiurl, project):
2090 checkout_package(apiurl, project, package, expand_link = expand_link, \
2091 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2092 except oscerr.LinkExpandError, e:
2093 print >>sys.stderr, 'Link cannot be expanded:\n', e
2094 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2095 # check out in unexpanded form at least
2096 checkout_package(apiurl, project, package, expand_link = False, \
2097 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2098 print_request_list(apiurl, project)
2101 raise oscerr.WrongArgs('Missing argument.\n\n' \
2102 + self.get_cmd_help('checkout'))
2105 @cmdln.option('-q', '--quiet', action='store_true',
2106 help='print as little as possible')
2107 @cmdln.option('-v', '--verbose', action='store_true',
2108 help='print extra information')
2110 def do_status(self, subcmd, opts, *args):
2111 """${cmd_name}: Show status of files in working copy
2113 Show the status of files in a local working copy, indicating whether
2114 files have been changed locally, deleted, added, ...
2116 The first column in the output specifies the status and is one of the
2117 following characters:
2118 ' ' no modifications
2123 '?' item is not under version control
2124 '!' item is missing (removed by non-osc command) or incomplete
2129 osc st file1 file2 ...
2132 osc status [OPTS] [PATH...]
2136 args = parseargs(args)
2138 # storage for single Package() objects
2140 # storage for a project dir ( { prj_instance : [ package objects ] } )
2143 # when 'status' is run inside a project dir, it should
2144 # stat all packages existing in the wc
2145 if is_project_dir(arg):
2146 prj = Project(arg, False)
2148 if conf.config['do_package_tracking']:
2150 for pac in prj.pacs_have:
2151 # we cannot create package objects if the dir does not exist
2152 if not pac in prj.pacs_broken:
2153 prjpacs[prj].append(os.path.join(arg, pac))
2155 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2156 elif is_package_dir(arg):
2157 pacpaths.append(arg)
2158 elif os.path.isfile(arg):
2159 pacpaths.append(arg)
2161 msg = '\'%s\' is neither a project or a package directory' % arg
2162 raise oscerr.NoWorkingCopy, msg
2164 # process single packages
2165 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2166 # process project dirs
2167 for prj, pacs in prjpacs.iteritems():
2168 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2170 print '\n'.join(lines)
2173 def do_add(self, subcmd, opts, *args):
2174 """${cmd_name}: Mark files to be added upon the next commit
2177 osc add FILE [FILE...]
2181 raise oscerr.WrongArgs('Missing argument.\n\n' \
2182 + self.get_cmd_help('add'))
2184 filenames = parseargs(args)
2188 def do_mkpac(self, subcmd, opts, *args):
2189 """${cmd_name}: Create a new package under version control
2192 osc mkpac new_package
2195 if not conf.config['do_package_tracking']:
2196 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2197 "in the [general] section in the configuration file"
2201 raise oscerr.WrongArgs('Wrong number of arguments.')
2203 createPackageDir(args[0])
2205 @cmdln.option('-r', '--recursive', action='store_true',
2206 help='If CWD is a project dir then scan all package dirs as well')
2208 def do_addremove(self, subcmd, opts, *args):
2209 """${cmd_name}: Adds new files, removes disappeared files
2211 Adds all files new in the local copy, and removes all disappeared files.
2213 ARG, if specified, is a package working copy.
2219 args = parseargs(args)
2221 for arg in arg_list:
2222 if is_project_dir(arg) and conf.config['do_package_tracking']:
2223 prj = Project(arg, False)
2224 for pac in prj.pacs_unvers:
2225 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2226 if os.path.isdir(pac_dir):
2227 addFiles([pac_dir], prj)
2228 for pac in prj.pacs_broken:
2229 if prj.get_state(pac) != 'D':
2230 prj.set_state(pac, 'D')
2231 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
2233 for pac in prj.pacs_have:
2234 state = prj.get_state(pac)
2235 if state != None and state != 'D':
2236 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2237 args.append(pac_dir)
2239 prj.write_packages()
2240 elif is_project_dir(arg):
2241 print >>sys.stderr, 'osc: addremove is not supported in a project dir unless ' \
2242 '\'do_package_tracking\' is enabled in the configuration file'
2245 pacs = findpacs(args)
2247 p.todo = p.filenamelist + p.filenamelist_unvers
2249 for filename in p.todo:
2250 if os.path.isdir(filename):
2252 # ignore foo.rXX, foo.mine for files which are in 'C' state
2253 if os.path.splitext(filename)[0] in p.in_conflict:
2255 state = p.status(filename)
2258 # TODO: should ignore typical backup files suffix ~ or .orig
2260 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
2262 p.put_on_deletelist(filename)
2263 p.write_deletelist()
2264 os.unlink(os.path.join(p.storedir, filename))
2265 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
2270 @cmdln.alias('checkin')
2271 @cmdln.option('-m', '--message', metavar='TEXT',
2272 help='specify log message TEXT')
2273 @cmdln.option('-F', '--file', metavar='FILE',
2274 help='read log message from FILE')
2275 @cmdln.option('-f', '--force', default=False, action="store_true",
2276 help='force commit - do not tests a file list')
2277 def do_commit(self, subcmd, opts, *args):
2278 """${cmd_name}: Upload content to the repository server
2280 Upload content which is changed in your working copy, to the repository
2283 Optionally checks the state of a working copy, if found a file with
2284 unknown state, it requests an user input:
2285 * skip - don't change anything, just move to another file
2286 * remove - remove a file from dir
2287 * edit file list - edit filelist using EDITOR
2288 * commit - don't check anything and commit package
2289 * abort - abort commit - this is default value
2290 This can be supressed by check_filelist config item, or -f/--force
2291 command line option.
2294 osc ci # current dir
2296 osc ci file1 file2 ...
2302 args = parseargs(args)
2309 msg = open(opts.file).read()
2311 sys.exit('could not open file \'%s\'.' % opts.file)
2314 for arg in arg_list:
2315 if conf.config['do_package_tracking'] and is_project_dir(arg):
2316 Project(arg).commit(msg=msg)
2318 msg = edit_message()
2321 pacs = findpacs(args)
2323 if conf.config['check_filelist'] and not opts.force:
2324 check_filelist_before_commit(pacs)
2327 template = store_read_file(os.path.abspath('.'), '_commit_msg')
2328 # open editor for commit message
2329 # but first, produce status and diff to append to the template
2333 changed = getStatus([pac], quiet=True)
2336 diffs += ['\nDiff for working copy: %s' % pac.dir]
2337 diffs += make_diff(pac, 0)
2338 lines.extend(get_commit_message_template(pac))
2339 if template == None:
2340 template='\n'.join(lines)
2341 # if footer is empty, there is nothing to commit, and no edit needed.
2343 msg = edit_message(footer='\n'.join(footer), template=template)
2346 store_write_string(os.path.abspath('.'), '_commit_msg', msg)
2348 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2350 if conf.config['do_package_tracking'] and len(pacs) > 0:
2354 # it is possible to commit packages from different projects at the same
2355 # time: iterate over all pacs and put each pac to the right project in the dict
2357 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
2358 if is_project_dir(path):
2359 pac_path = os.path.basename(os.path.normpath(pac.absdir))
2360 prj_paths.setdefault(path, []).append(pac_path)
2361 files[pac_path] = pac.todo
2363 single_paths.append(pac.dir)
2364 for prj, packages in prj_paths.iteritems():
2365 Project(prj).commit(tuple(packages), msg, files)
2366 for pac in single_paths:
2367 Package(pac).commit(msg)
2372 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2374 @cmdln.option('-r', '--revision', metavar='REV',
2375 help='update to specified revision (this option will be ignored '
2376 'if you are going to update the complete project or more than '
2378 @cmdln.option('-u', '--unexpand-link', action='store_true',
2379 help='if a package is an expanded link, update to the raw _link file')
2380 @cmdln.option('-e', '--expand-link', action='store_true',
2381 help='if a package is a link, update to the expanded sources')
2382 @cmdln.option('-s', '--source-service-files', action='store_true',
2383 help='Use server side generated sources instead of local generation.' )
2385 def do_update(self, subcmd, opts, *args):
2386 """${cmd_name}: Update a working copy
2391 If the current working directory is a package, update it.
2392 If the directory is a project directory, update all contained
2393 packages, AND check out newly added packages.
2395 To update only checked out packages, without checking out new
2396 ones, you might want to use "osc up *" from within the project
2400 Update the packages specified by the path argument(s)
2402 When --expand-link is used with source link packages, the expanded
2403 sources will be checked out. Without this option, the _link file and
2404 patches will be checked out. The option --unexpand-link can be used to
2405 switch back to the "raw" source with a _link file plus patch(es).
2411 if (opts.expand_link and opts.unexpand_link) \
2412 or (opts.expand_link and opts.revision) \
2413 or (opts.unexpand_link and opts.revision):
2414 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
2415 '--revision are mutually exclusive.')
2417 if opts.source_service_files: service_files = True
2418 else: service_files = False
2420 args = parseargs(args)
2423 for arg in arg_list:
2424 if is_project_dir(arg):
2425 prj = Project(arg, progress_obj=self.download_progress)
2427 if conf.config['do_package_tracking']:
2428 prj.update(expand_link=opts.expand_link,
2429 unexpand_link=opts.unexpand_link)
2432 # if not tracking package, and 'update' is run inside a project dir,
2433 # it should do the following:
2434 # (a) update all packages
2435 args += prj.pacs_have
2436 # (b) fetch new packages
2437 prj.checkout_missing_pacs(expand_link = not opts.unexpand_link)
2439 print_request_list(prj.apiurl, prj.name)
2442 pacs = findpacs(args, progress_obj=self.download_progress)
2444 if opts.revision and len(args) == 1:
2445 rev, dummy = parseRevisionOption(opts.revision)
2446 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
2447 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2454 print 'Updating %s' % p.name
2456 # FIXME: ugly workaround for #399247
2457 if opts.expand_link or opts.unexpand_link:
2458 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' and p.status(i) != '?']:
2459 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
2460 'copy has local modifications.\nPlease revert/commit them ' \
2465 if opts.expand_link and p.islink() and not p.isexpanded():
2466 if p.haslinkerror():
2468 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev)
2470 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev, linkrev="base")
2473 rev = p.linkinfo.xsrcmd5
2474 print 'Expanding to rev', rev
2475 elif opts.unexpand_link and p.islink() and p.isexpanded():
2476 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
2477 rev = p.linkinfo.lsrcmd5
2478 elif p.islink() and p.isexpanded():
2479 rev = p.latest_rev()
2481 p.update(rev, service_files)
2482 if opts.unexpand_link:
2485 print_request_list(p.apiurl, p.prjname, p.name)
2488 @cmdln.option('-f', '--force', action='store_true',
2489 help='forces removal of entire package and its files')
2492 @cmdln.alias('remove')
2493 def do_delete(self, subcmd, opts, *args):
2494 """${cmd_name}: Mark files or package directories to be deleted upon the next 'checkin'
2497 cd .../PROJECT/PACKAGE
2498 osc delete FILE [...]
2500 osc delete PACKAGE [...]
2502 This command works on check out copies. Use "rdelete" for working on server
2503 side only. This is needed for removing the entire project.
2505 As a safety measure, projects must be empty (i.e., you need to delete all
2508 If you are sure that you want to remove a package and all
2509 its files use \'--force\' switch. Sometimes this also works without --force.
2515 raise oscerr.WrongArgs('Missing argument.\n\n' \
2516 + self.get_cmd_help('delete'))
2518 args = parseargs(args)
2519 # check if args contains a package which was removed by
2520 # a non-osc command and mark it with the 'D'-state
2523 if not os.path.exists(i):
2524 prj_dir, pac_dir = getPrjPacPaths(i)
2525 if is_project_dir(prj_dir):
2526 prj = Project(prj_dir, False)
2527 if i in prj.pacs_broken:
2528 if prj.get_state(i) != 'A':
2529 prj.set_state(pac_dir, 'D')
2531 prj.del_package_node(i)
2532 print statfrmt('D', getTransActPath(i))
2534 prj.write_packages()
2535 pacs = findpacs(args)
2539 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
2540 if is_project_dir(prj_dir):
2541 if conf.config['do_package_tracking']:
2542 prj = Project(prj_dir, False)
2543 prj.delPackage(p, opts.force)
2545 print "WARNING: package tracking is disabled, operation skipped !"
2547 pathn = getTransActPath(p.dir)
2548 for filename in p.todo:
2549 ret, state = p.delete_file(filename, opts.force)
2551 print statfrmt('D', os.path.join(pathn, filename))
2554 sys.exit('\'%s\' is not under version control' % filename)
2555 elif state in ['A', 'M'] and not opts.force:
2556 sys.exit('\'%s\' has local modifications (use --force to remove this file)' % filename)
2559 def do_resolved(self, subcmd, opts, *args):
2560 """${cmd_name}: Remove 'conflicted' state on working copy files
2562 If an upstream change can't be merged automatically, a file is put into
2563 in 'conflicted' ('C') state. Within the file, conflicts are marked with
2564 special <<<<<<< as well as ======== and >>>>>>> lines.
2566 After manually resolving all conflicting parts, use this command to
2567 remove the 'conflicted' state.
2569 Note: this subcommand does not semantically resolve conflicts or
2570 remove conflict markers; it merely removes the conflict-related
2571 artifact files and allows PATH to be committed again.
2574 osc resolved FILE [FILE...]
2579 raise oscerr.WrongArgs('Missing argument.\n\n' \
2580 + self.get_cmd_help('resolved'))
2582 args = parseargs(args)
2583 pacs = findpacs(args)
2586 for filename in p.todo:
2587 print 'Resolved conflicted state of "%s"' % filename
2588 p.clear_from_conflictlist(filename)
2591 @cmdln.alias('platforms')
2592 def do_repositories(self, subcmd, opts, *args):
2593 """${cmd_name}: Shows available repositories
2597 Shows all available repositories/build targets
2599 2. osc repositories <project>
2600 Shows the configured repositories/build targets of a project
2608 print '\n'.join(get_repositories_of_project(conf.config['apiurl'], project))
2610 print '\n'.join(get_repositories(conf.config['apiurl']))
2614 def do_results_meta(self, subcmd, opts, *args):
2615 print "Command results_meta is obsolete. Please use: osc results --xml"
2619 @cmdln.option('-l', '--last-build', action='store_true',
2620 help='show last build results (succeeded/failed/unknown)')
2621 @cmdln.option('-r', '--repo', action='append', default = [],
2622 help='Show results only for specified repo(s)')
2623 @cmdln.option('-a', '--arch', action='append', default = [],
2624 help='Show results only for specified architecture(s)')
2625 @cmdln.option('', '--xml', action='store_true',
2626 help='generate output in XML (former results_meta)')
2627 def do_rresults(self, subcmd, opts, *args):
2628 print "Command rresults is obsolete. Running 'osc results' instead"
2629 self.do_results('results', opts, *args)
2633 @cmdln.option('-f', '--force', action='store_true', default=False,
2634 help="Don't ask and delete files")
2635 def do_rremove(self, subcmd, opts, project, package, *files):
2636 """${cmd_name}: Remove source files from selected package
2643 if not '/' in project:
2644 raise oscerr.WrongArgs("Missing operand, type osc help rremove for help")
2647 project, package = project.split('/')
2651 resp = raw_input("rm: remove source file `%s' from `%s/%s'? (yY|nN) " % (file, project, package))
2652 if resp not in ('y', 'Y'):
2655 delete_files(conf.config['apiurl'], project, package, (file, ))
2656 except urllib2.HTTPError, e:
2658 print >>sys.stderr, e
2660 if e.code in [ 400, 403, 404, 500 ]:
2661 if '<summary>' in body:
2662 msg = body.split('<summary>')[1]
2663 msg = msg.split('</summary>')[0]
2664 print >>sys.stderr, msg
2669 @cmdln.option('-l', '--last-build', action='store_true',
2670 help='show last build results (succeeded/failed/unknown)')
2671 @cmdln.option('-r', '--repo', action='append', default = [],
2672 help='Show results only for specified repo(s)')
2673 @cmdln.option('-a', '--arch', action='append', default = [],
2674 help='Show results only for specified architecture(s)')
2675 @cmdln.option('', '--xml', action='store_true',
2676 help='generate output in XML (former results_meta)')
2677 def do_results(self, subcmd, opts, *args):
2678 """${cmd_name}: Shows the build results of a package
2681 osc results (inside working copy)
2682 osc results remote_project remote_package
2687 args = slash_split(args)
2689 apiurl = conf.config['apiurl']
2692 if is_project_dir(wd):
2696 opts.hide_legend = None
2697 opts.name_filter = None
2698 opts.status_filter = None
2699 opts.vertical = None
2700 self.do_prjresults('prjresults', opts, *args);
2703 project = store_read_project(wd)
2704 package = store_read_package(wd)
2705 apiurl = store_read_apiurl(wd)
2707 raise oscerr.WrongArgs('Too few arguments (required none or two)')
2709 raise oscerr.WrongArgs('Too many arguments (required none or two)')
2718 func = show_results_meta
2721 print delim.join(func(apiurl, project, package, opts.last_build, opts.repo, opts.arch))
2723 # WARNING: this function is also called by do_results. You need to set a default there
2724 # as well when adding a new option!
2725 @cmdln.option('-q', '--hide-legend', action='store_true',
2726 help='hide the legend')
2727 @cmdln.option('-c', '--csv', action='store_true',
2729 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2730 help='show only packages with buildstatus STATUS (see legend)')
2731 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2732 help='show only packages whose names match EXPR')
2733 @cmdln.option('-a', '--arch', metavar='ARCH',
2734 help='show results only for specified architecture(s)')
2735 @cmdln.option('-r', '--repo', metavar='REPO',
2736 help='show results only for specified repo(s)')
2737 @cmdln.option('-V', '--vertical', action='store_true',
2738 help='list packages vertically instead horizontally')
2740 def do_prjresults(self, subcmd, opts, *args):
2741 """${cmd_name}: Shows project-wide build results
2744 osc prjresults (inside working copy)
2745 osc prjresults PROJECT
2751 apiurl = conf.config['apiurl']
2755 raise oscerr.WrongArgs('Wrong number of arguments.')
2758 project = store_read_project(wd)
2759 apiurl = store_read_apiurl(wd)
2761 print '\n'.join(get_prj_results(apiurl, project, hide_legend=opts.hide_legend, csv=opts.csv, status_filter=opts.status_filter, name_filter=opts.name_filter, repo=opts.repo, arch=opts.arch, vertical=opts.vertical))
2764 @cmdln.option('-q', '--hide-legend', action='store_true',
2765 help='hide the legend')
2766 @cmdln.option('-c', '--csv', action='store_true',
2768 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2769 help='show only packages with buildstatus STATUS (see legend)')
2770 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2771 help='show only packages whose names match EXPR')
2774 def do_rprjresults(self, subcmd, opts, *args):
2775 print "Command rprjresults is obsolete. Please use 'osc prjresults'"
2779 @cmdln.option('-s', '--start', metavar='START',
2780 help='get log starting from the offset')
2781 def do_buildlog(self, subcmd, opts, *args):
2782 """${cmd_name}: Shows the build log of a package
2784 Shows the log file of the build of a package. Can be used to follow the
2785 log while it is being written.
2786 Needs to be called from within a package directory.
2788 The arguments REPOSITORY and ARCH are the first two columns in the 'osc
2789 results' output. If the buildlog url is used buildlog command has the
2790 same behavior as remotebuildlog.
2792 ${cmd_usage} [REPOSITORY ARCH | BUILDLOGURL]
2796 repository = arch = None
2798 if len(args) == 1 and args[0].startswith('http'):
2799 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2802 package = store_read_package(wd)
2803 project = store_read_project(wd)
2804 apiurl = store_read_apiurl(wd)
2808 offset = int(opts.start)
2810 if not repository or not arch:
2814 repository = args[0]
2817 print_buildlog(apiurl, project, package, repository, arch, offset)
2820 def print_repos(self):
2823 if is_package_dir(wd):
2826 elif is_project_dir(wd):
2831 print 'Valid arguments for this %s are:' % str
2833 self.do_repos(None, None)
2835 raise oscerr.WrongArgs('Missing arguments')
2838 @cmdln.alias('rbuildlog')
2839 @cmdln.option('-s', '--start', metavar='START',
2840 help='get log starting from the offset')
2841 def do_remotebuildlog(self, subcmd, opts, *args):
2842 """${cmd_name}: Shows the build log of a package
2844 Shows the log file of the build of a package. Can be used to follow the
2845 log while it is being written.
2848 osc remotebuildlog project package repository arch
2850 osc remotebuildlog project/package/repository/arch
2852 osc remotebuildlog buildlogurl
2855 if len(args) == 1 and args[0].startswith('http'):
2856 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2858 args = slash_split(args)
2859 apiurl = conf.config['apiurl']
2861 raise oscerr.WrongArgs('Too few arguments.')
2863 raise oscerr.WrongArgs('Too many arguments.')
2865 project, package, repository, arch = args
2869 offset = int(opts.start)
2871 print_buildlog(apiurl, project, package, repository, arch, offset)
2874 @cmdln.option('-s', '--start', metavar='START',
2875 help='get log starting from offset')
2876 def do_localbuildlog(self, subcmd, opts, *args):
2877 """${cmd_name}: Shows the build log of a local buildchroot
2880 osc lbl [REPOSITORY ARCH]
2881 osc lbl # show log of newest last local build
2885 if conf.config['build-type']:
2886 # FIXME: raise Exception instead
2887 print >>sys.stderr, 'Not implemented for VMs'
2891 package = store_read_package('.')
2893 files = glob.glob(os.path.join(os.getcwd(), store, "_buildinfo-*"))
2896 raise oscerr.WrongArgs('No buildconfig found, please specify repo and arch manually.')
2900 if os.stat(f).st_mtime > os.stat(cfg).st_mtime:
2902 root = ET.parse(cfg).getroot()
2903 project = root.get("project")
2904 repo = root.get("repository")
2905 arch = root.find("arch").text
2906 elif len(args) == 2:
2907 project = store_read_project('.')
2908 package = store_read_package('.')
2912 if is_package_dir(os.curdir):
2914 raise oscerr.WrongArgs('Wrong number of arguments.')
2916 buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root'])
2917 buildroot = buildroot % {'project': project, 'package': package,
2918 'repo': repo, 'arch': arch}
2921 offset = int(opts.start)
2922 logfile = os.path.join(buildroot, '.build.log')
2923 if not os.path.isfile(logfile):
2924 raise oscerr.OscIOError(None, 'logfile \'%s\' does not exist' % logfile)
2925 f = open(logfile, 'r')
2927 data = f.read(BUFSIZE)
2929 sys.stdout.write(data)
2930 data = f.read(BUFSIZE)
2934 def do_triggerreason(self, subcmd, opts, *args):
2935 """${cmd_name}: Show reason why a package got triggered to build
2937 The server decides when a package needs to get rebuild, this command
2938 shows the detailed reason for a package. A brief reason is also stored
2939 in the jobhistory, which can be accessed via "osc jobhistory".
2941 Trigger reasons might be:
2942 - new build (never build yet or rebuild manually forced)
2943 - source change (eg. on updating sources)
2944 - meta change (packages which are used for building have changed)
2945 - rebuild count sync (In case that it is configured to sync release numbers)
2947 usage in package or project directory:
2948 osc reason REPOSITORY ARCH
2949 osc reason PROJECT PACKAGE REPOSITORY ARCH
2954 args = slash_split(args)
2955 project = package = repository = arch = None
2960 if len(args) == 2: # 2
2961 if is_package_dir('.'):
2962 package = store_read_package(wd)
2964 raise oscerr.WrongArgs('package is not specified.')
2965 project = store_read_project(wd)
2966 apiurl = store_read_apiurl(wd)
2967 repository = args[0]
2969 elif len(args) == 4:
2970 apiurl = conf.config['apiurl']
2973 repository = args[2]
2976 raise oscerr.WrongArgs('Too many arguments.')
2978 print apiurl, project, package, repository, arch
2979 xml = show_package_trigger_reason(apiurl, project, package, repository, arch)
2980 root = ET.fromstring(xml)
2981 reason = root.find('explain').text
2983 if reason == "meta change":
2984 print "changed keys:"
2985 for package in root.findall('packagechange'):
2986 print " ", package.get('change'), package.get('key')
2989 # FIXME: the new osc syntax should allow to specify multiple packages
2990 # FIXME: the command should optionally use buildinfo data to show all dependencies
2991 @cmdln.alias('whatdependson')
2992 def do_dependson(self, subcmd, opts, *args):
2993 """${cmd_name}: Show the build dependencies
2995 The command dependson and whatdependson can be used to find out what
2996 will be triggered when a certain package changes.
2997 This is no guarantee, since the new build might have changed dependencies.
2999 dependson shows the build dependencies inside of a project, valid for a
3000 given repository and architecture.
3001 NOTE: to see all binary packages, which can trigger a build you need to
3002 refer the buildinfo, since this command shows only the dependencies
3003 inside of a project.
3005 The arguments REPOSITORY and ARCH can be taken from the first two columns
3006 of the 'osc repos' output.
3008 usage in package or project directory:
3009 osc dependson REPOSITORY ARCH
3010 osc whatdependson REPOSITORY ARCH
3013 osc dependson PROJECT [PACKAGE] REPOSITORY ARCH
3014 osc whatdependson PROJECT [PACKAGE] REPOSITORY ARCH
3019 args = slash_split(args)
3020 project = packages = repository = arch = reverse = None
3022 if len(args) < 2 and (is_package_dir('.') or is_project_dir('.')):
3026 raise oscerr.WrongArgs('Too many arguments.')
3028 if len(args) < 3: # 2
3029 if is_package_dir('.'):
3030 packages = [store_read_package(wd)]
3031 elif not is_project_dir('.'):
3032 raise oscerr.WrongArgs('Project and package is not specified.')
3033 project = store_read_project(wd)
3034 apiurl = store_read_apiurl(wd)
3035 repository = args[0]
3039 apiurl = conf.config['apiurl']
3041 repository = args[1]
3045 apiurl = conf.config['apiurl']
3047 packages = [args[1]]
3048 repository = args[2]
3051 if subcmd == 'whatdependson':
3054 xml = get_dependson(apiurl, project, repository, arch, packages, reverse)
3056 root = ET.fromstring(xml)
3057 for package in root.findall('package'):
3058 print package.get('name'), ":"
3059 for dep in package.findall('pkgdep'):
3063 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
3064 help='Add this package when computing the buildinfo')
3065 def do_buildinfo(self, subcmd, opts, *args):
3066 """${cmd_name}: Shows the build info
3068 Shows the build "info" which is used in building a package.
3069 This command is mostly used internally by the 'build' subcommand.
3070 It needs to be called from within a package directory.
3072 The BUILD_DESCR argument is optional. BUILD_DESCR is a local RPM specfile
3073 or Debian "dsc" file. If specified, it is sent to the server, and the
3074 buildinfo will be based on it. If the argument is not supplied, the
3075 buildinfo is derived from the specfile which is currently on the source
3078 The returned data is XML and contains a list of the packages used in
3079 building, their source, and the expanded BuildRequires.
3081 The arguments REPOSITORY and ARCH can be taken from the first two columns
3082 of the 'osc repos' output.
3085 osc buildinfo REPOSITORY ARCH [BUILD_DESCR] (in pkg dir)
3086 osc buildinfo PROJECT PACKAGE REPOSITORY ARCH [BUILD_DESCR]
3091 args = slash_split(args)
3093 if len(args) < 2 and is_package_dir('.'):
3097 raise oscerr.WrongArgs('Too many arguments.')
3099 if len(args) < 4: # 2 or 3
3100 package = store_read_package(wd)
3101 project = store_read_project(wd)
3102 apiurl = store_read_apiurl(wd)
3103 repository = args[0]
3106 if len(args) > 3 and len(args) < 6: # 4 or 5
3107 apiurl = conf.config['apiurl']