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://www.opensuse.org/Build_Service_Tutorial
32 * http://www.opensuse.org/Build_Service/CLI
34 You can modify osc commands, or roll you own, via the plugin API:
35 * http://www.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://www.opensuse.org/Build_Service_Tutorial
52 * http://www.opensuse.org/Build_Service/CLI
54 You can modify osc commands, or roll you own, via the plugin API:
55 * http://www.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.apiurl:
121 config['apiurl'] = self.options.apiurl
123 conf.write_initial_config(e.file, config)
124 print >>sys.stderr, 'done'
125 if try_again: self.postoptparse(try_again = False)
126 except oscerr.ConfigMissingApiurl, e:
127 print >>sys.stderr, e.msg
129 user = raw_input('Username: ')
130 passwd = getpass.getpass()
131 conf.add_section(e.file, e.url, user, passwd)
132 if try_again: self.postoptparse(try_again = False)
134 self.options.verbose = conf.config['verbose']
135 self.download_progress = None
136 if conf.config.get('show_download_progress', False):
137 from meter import TextMeter
138 self.download_progress = TextMeter()
141 def get_cmd_help(self, cmdname):
142 doc = self._get_cmd_handler(cmdname).__doc__
143 doc = self._help_reindent(doc)
144 doc = self._help_preprocess(doc, cmdname)
145 doc = doc.rstrip() + '\n' # trim down trailing space
146 return self._str(doc)
149 # overridden from class Cmdln() to use config variables in help texts
150 def _help_preprocess(self, help, cmdname):
151 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
152 return help % conf.config
155 def do_init(self, subcmd, opts, project, package=None):
156 """${cmd_name}: Initialize a directory as working copy
158 Initialize an existing directory to be a working copy of an
159 (already existing) buildservice project/package.
161 (This is the same as checking out a package and then copying sources
162 into the directory. It does NOT create a new package. To create a
163 package, use 'osc meta pkg ... ...')
165 You wouldn't normally use this command.
167 To get a working copy of a package (e.g. for building it or working on
168 it, you would normally use the checkout command. Use "osc help
169 checkout" to get help for it.
178 init_project_dir(conf.config['apiurl'], os.curdir, project)
179 print 'Initializing %s (Project: %s)' % (os.curdir, project)
181 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
182 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
188 @cmdln.option('-a', '--arch', metavar='ARCH',
189 help='specify architecture (only for binaries)')
190 @cmdln.option('-r', '--repo', metavar='REPO',
191 help='specify repository (only for binaries)')
192 @cmdln.option('-b', '--binaries', action='store_true',
193 help='list built binaries instead of sources')
194 @cmdln.option('-R', '--revision', metavar='REVISION',
195 help='specify revision (only for sources)')
196 @cmdln.option('-e', '--expand', action='store_true',
197 help='expand linked package (only for sources)')
198 @cmdln.option('-v', '--verbose', action='store_true',
199 help='print extra information')
200 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
201 help='print extra information')
202 def do_list(self, subcmd, opts, *args):
203 """${cmd_name}: List sources or binaries on the server
205 Examples for listing sources:
206 ls # list all projects
207 ls PROJECT # list packages in a project
208 ls PROJECT PACKAGE # list source files of package of a project
209 ls PROJECT PACKAGE <file> # list <file> if this file exists
210 ls -v PROJECT PACKAGE # verbosely list source files of package
211 ls -l PROJECT PACKAGE # verbosely list source files of package
212 ll PROJECT PACKAGE # verbosely list source files of package
213 LL PROJECT PACKAGE # verbosely list source files of expanded link
215 With --verbose, the following fields will be shown for each item:
217 Revision number of the last commit
219 Date and time of the last commit
221 Examples for listing binaries:
222 ls -b PROJECT # list all binaries of a project
223 ls -b PROJECT -a ARCH # list ARCH binaries of a project
224 ls -b PROJECT -r REPO # list binaries in REPO
225 ls -b PROJECT PACKAGE REPO ARCH
228 ${cmd_name} [PROJECT [PACKAGE]]
229 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
233 apiurl = conf.config['apiurl']
234 args = slash_split(args)
237 if subcmd == 'lL' or subcmd == 'LL':
251 if opts.repo != args[2]:
252 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
259 if not opts.binaries:
260 raise oscerr.WrongArgs('Too many arguments')
262 if opts.arch != args[3]:
263 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
268 if opts.binaries and opts.expand:
269 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
273 # ls -b toplevel doesn't make sense, so use info from
274 # current dir if available
277 if is_project_dir(dir):
278 project = store_read_project(dir)
279 apiurl = store_read_apiurl(dir)
280 elif is_package_dir(dir):
281 project = store_read_project(dir)
282 package = store_read_package(dir)
283 apiurl = store_read_apiurl(dir)
286 raise oscerr.WrongArgs('There are no binaries to list above project level.')
288 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
292 if opts.repo and opts.arch:
293 repos.append(Repo(opts.repo, opts.arch))
294 elif opts.repo and not opts.arch:
295 for repo in get_repos_of_project(apiurl, project):
296 if repo.name == opts.repo:
298 elif opts.arch and not opts.repo:
299 for repo in get_repos_of_project(apiurl, project):
300 if repo.arch == opts.arch:
303 repos = get_repos_of_project(apiurl, project)
307 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
309 for result in results:
312 print '%s/%s' % (result[0].name, result[0].arch)
317 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
323 elif not opts.binaries:
325 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
329 if self.options.verbose:
330 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
332 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
334 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
336 elif len(args) == 2 or len(args) == 3:
337 l = meta_get_filelist(conf.config['apiurl'],
340 verbose=opts.verbose,
342 revision=opts.revision)
344 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
345 for i in l if not fname or fname == i.name ]
348 print 'file \'%s\' does not exist' % fname
356 print 'file \'%s\' does not exist' % fname
361 @cmdln.option('-f', '--force', action='store_true',
362 help='force generation of new patchinfo file')
363 @cmdln.option('--force-update', action='store_true',
364 help='drops away collected packages from an already built patch and let it collect again')
365 def do_patchinfo(self, subcmd, opts, *args):
366 """${cmd_name}: Generate and edit a patchinfo file.
368 A patchinfo file describes the packages for an update and the kind of
373 osc patchinfo PATCH_NAME
377 project_dir = localdir = os.getcwd()
378 if is_project_dir(localdir):
379 project = store_read_project(localdir)
380 apiurl = store_read_apiurl(localdir)
382 sys.exit('This command must be called in a checked out project.')
384 for p in meta_get_packagelist(apiurl, project):
385 if p.startswith("_patchinfo:"):
388 if opts.force or not patchinfo:
389 print "Creating initial patchinfo..."
390 query='cmd=createpatchinfo'
392 query += "&name=" + args[0]
393 url = makeurl(apiurl, ['source', project], query=query)
395 for p in meta_get_packagelist(apiurl, project):
396 if p.startswith("_patchinfo:"):
399 if not os.path.exists(project_dir + "/" + patchinfo):
400 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
402 if sys.platform[:3] != 'win':
403 editor = os.getenv('EDITOR', default='vim')
405 editor = os.getenv('EDITOR', default='notepad')
406 subprocess.call('%s %s' % (editor, project_dir + "/" + patchinfo + "/_patchinfo"), shell=True)
409 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
410 help='affect only a given attribute')
411 @cmdln.option('--attribute-defaults', action='store_true',
412 help='include defined attribute defaults')
413 @cmdln.option('--attribute-project', action='store_true',
414 help='include project values, if missing in packages ')
415 @cmdln.option('-F', '--file', metavar='FILE',
416 help='read metadata from FILE, instead of opening an editor. '
417 '\'-\' denotes standard input. ')
418 @cmdln.option('-e', '--edit', action='store_true',
419 help='edit metadata')
420 @cmdln.option('-c', '--create', action='store_true',
421 help='create attribute without values')
422 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
423 help='set attribute values')
424 @cmdln.option('--delete', action='store_true',
425 help='delete a pattern or attribute')
426 def do_meta(self, subcmd, opts, *args):
427 """${cmd_name}: Show meta information, or edit it
429 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
431 This command displays metadata on buildservice objects like projects,
432 packages, or users. The type of metadata is specified by the word after
433 "meta", like e.g. "meta prj".
435 prj denotes metadata of a buildservice project.
436 prjconf denotes the (build) configuration of a project.
437 pkg denotes metadata of a buildservice package.
438 user denotes the metadata of a user.
439 pattern denotes installation patterns defined for a project.
441 To list patterns, use 'osc meta pattern PRJ'. An additional argument
442 will be the pattern file to view or edit.
444 With the --edit switch, the metadata can be edited. Per default, osc
445 opens the program specified by the environmental variable EDITOR with a
446 temporary file. Alternatively, content to be saved can be supplied via
447 the --file switch. If the argument is '-', input is taken from stdin:
448 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
450 When trying to edit a non-existing resource, it is created implicitly.
456 osc meta pkg PRJ PKG -e
457 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
460 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
461 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
462 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
463 osc meta pattern --delete PRJ PATTERN
467 args = slash_split(args)
469 if not args or args[0] not in metatypes.keys():
470 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
471 % ', '.join(metatypes))
477 min_args, max_args = 2, 2
478 elif cmd in ['pattern']:
479 min_args, max_args = 1, 2
480 elif cmd in ['attribute']:
481 min_args, max_args = 1, 3
483 min_args, max_args = 1, 1
484 if len(args) < min_args:
485 raise oscerr.WrongArgs('Too few arguments.')
486 if len(args) > max_args:
487 raise oscerr.WrongArgs('Too many arguments.')
494 project, package = args[0:2]
495 elif cmd == 'attribute':
501 if opts.attribute_project:
502 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
507 attributepath.append('source')
508 attributepath.append(project)
510 attributepath.append(package)
512 attributepath.append(subpackage)
513 attributepath.append('_attribute')
514 elif cmd == 'prjconf':
518 elif cmd == 'pattern':
524 # enforce pattern argument if needed
525 if opts.edit or opts.file:
526 raise oscerr.WrongArgs('A pattern file argument is required.')
529 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
531 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
533 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
534 elif cmd == 'attribute':
535 sys.stdout.write(''.join(show_attribute_meta(conf.config['apiurl'], project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
536 elif cmd == 'prjconf':
537 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
539 r = get_user_meta(conf.config['apiurl'], user)
541 sys.stdout.write(''.join(r))
542 elif cmd == 'pattern':
544 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
546 sys.stdout.write(''.join(r))
548 r = show_pattern_metalist(conf.config['apiurl'], project)
550 sys.stdout.write('\n'.join(r) + '\n')
553 if opts.edit and not opts.file:
555 edit_meta(metatype='prj',
557 path_args=quote_plus(project),
560 'user': conf.config['user']}))
562 edit_meta(metatype='pkg',
564 path_args=(quote_plus(project), quote_plus(package)),
567 'user': conf.config['user']}))
568 elif cmd == 'prjconf':
569 edit_meta(metatype='prjconf',
571 path_args=quote_plus(project),
574 edit_meta(metatype='user',
576 path_args=(quote_plus(user)),
577 template_args=({'user': user}))
578 elif cmd == 'pattern':
579 edit_meta(metatype='pattern',
581 path_args=(project, pattern),
584 # create attribute entry
585 if (opts.create or opts.set) and cmd == 'attribute':
586 if not opts.attribute:
587 raise oscerr.WrongOptions('no attribute given to create')
590 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
591 for i in opts.set.split(','):
592 values += '<value>%s</value>' % i
593 aname = opts.attribute.split(":")
594 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
595 url = makeurl(conf.config['apiurl'], attributepath)
596 for data in streamfile(url, http_POST, data=d):
597 sys.stdout.write(data)
606 f = open(opts.file).read()
608 sys.exit('could not open file \'%s\'.' % opts.file)
611 edit_meta(metatype='prj',
614 path_args=quote_plus(project))
616 edit_meta(metatype='pkg',
619 path_args=(quote_plus(project), quote_plus(package)))
620 elif cmd == 'prjconf':
621 edit_meta(metatype='prjconf',
624 path_args=quote_plus(project))
626 edit_meta(metatype='user',
629 path_args=(quote_plus(user)))
630 elif cmd == 'pattern':
631 edit_meta(metatype='pattern',
634 path_args=(project, pattern))
639 path = metatypes[cmd]['path']
641 path = path % (project, pattern)
642 u = makeurl(conf.config['apiurl'], [path])
644 elif cmd == 'attribute':
645 if not opts.attribute:
646 raise oscerr.WrongOptions('no attribute given to create')
647 attributepath.append(opts.attribute)
648 u = makeurl(conf.config['apiurl'], attributepath)
649 for data in streamfile(u, http_DELETE):
650 sys.stdout.write(data)
652 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
655 @cmdln.option('-m', '--message', metavar='TEXT',
656 help='specify message TEXT')
657 @cmdln.option('-r', '--revision', metavar='REV',
658 help='for "create", specify a certain source revision ID (the md5 sum)')
659 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
660 help='Superseding another request by this one')
661 @cmdln.option('--nodevelproject', action='store_true',
662 help='do not follow a defined devel project ' \
663 '(primary project where a package is developed)')
664 @cmdln.option('--cleanup', action='store_true',
665 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
666 @cmdln.option('--no-cleanup', action='store_true',
667 help='never remove source package on accept, but update its content')
668 @cmdln.option('--no-update', action='store_true',
669 help='never touch source package on accept (will break source links)')
670 @cmdln.option('-d', '--diff', action='store_true',
671 help='show diff only instead of creating the actual request')
672 @cmdln.option('--yes', action='store_true',
673 help='proceed without asking.')
675 @cmdln.alias("submitreq")
676 @cmdln.alias("submitpac")
677 def do_submitrequest(self, subcmd, opts, *args):
678 """${cmd_name}: Create request to submit source into another Project
680 [See http://en.opensuse.org/Build_Service/Collaboration for information
683 See the "request" command for showing and modifing existing requests.
686 osc submitreq [OPTIONS]
687 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
688 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
692 src_update = conf.config['submitrequest_on_accept_action'] or None
693 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
695 src_update = "cleanup"
696 elif opts.no_cleanup:
697 src_update = "update"
699 src_update = "noupdate"
701 args = slash_split(args)
703 # remove this block later again
704 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
705 if args and args[0] in oldcmds:
706 print "************************************************************************"
707 print "* WARNING: It looks that you are using this command with a *"
708 print "* deprecated syntax. *"
709 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
710 print "* to see the new syntax. *"
711 print "************************************************************************"
712 if args[0] == 'create':
718 raise oscerr.WrongArgs('Too many arguments.')
720 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
721 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
723 apiurl = conf.config['apiurl']
725 if len(args) == 0 and is_project_dir(os.getcwd()):
727 # submit requests for multiple packages are currently handled via multiple requests
728 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
729 project = store_read_project(os.curdir)
730 apiurl = store_read_apiurl(os.curdir)
736 # loop via all packages for checking their state
737 for p in meta_get_packagelist(apiurl, project):
738 if p.startswith("_patchinfo:"):
741 # get _link info from server, who knows about the local state ...
742 u = makeurl(apiurl, ['source', project, p])
744 root = ET.parse(f).getroot()
745 linkinfo = root.find('linkinfo')
747 print "Package ", p, " is not a source link."
748 sys.exit("This is currently not supported.")
749 if linkinfo.get('error'):
750 print "Package ", p, " is a broken source link."
751 sys.exit("Please fix this first")
752 t = linkinfo.get('project')
754 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
755 # Real fix is to ask the api if sources are modificated
756 # but there is no such call yet.
757 targetprojects.append(t)
759 print "Submitting package ", p
761 print " Skipping package ", p
763 print "Skipping package ", p, " since it is a source link pointing inside to the project."
767 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
768 print "\nEverything fine? Can we create the requests ? [y/n]"
769 if sys.stdin.read(1) != "y":
770 sys.exit("Aborted...")
772 # loop via all packages to do the action
774 result = create_submit_request(apiurl, project, p)
777 sys.exit("submit request creation failed")
778 sr_ids.append(result)
780 # create submit requests for all found patchinfos
784 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
787 for t in targetprojects:
788 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
789 (project, p, t, p, options_block)
793 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
794 (actionxml, cgi.escape(opts.message or ""))
795 u = makeurl(apiurl, ['request'], query='cmd=create')
796 f = http_POST(u, data=xml)
798 root = ET.parse(f).getroot()
799 sr_ids.append(root.get('id'))
801 print "Requests created: ",
804 sys.exit('Successfull finished')
807 # try using the working copy at hand
808 p = findpacs(os.curdir)[0]
809 src_project = p.prjname
812 if len(args) == 0 and p.islink():
813 dst_project = p.linkinfo.project
814 dst_package = p.linkinfo.package
816 dst_project = args[0]
818 dst_package = args[1]
820 dst_package = src_package
822 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
823 'Please provide it the target via commandline arguments.' % p.name)
825 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
826 if len(modified) > 0:
827 print 'Your working copy has local modifications.'
828 repl = raw_input('Proceed without committing the local changes? (y|N) ')
832 # get the arguments from the commandline
833 src_project, src_package, dst_project = args[0:3]
835 dst_package = args[3]
837 dst_package = src_package
839 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
840 + self.get_cmd_help('request'))
842 if not opts.nodevelproject:
845 devloc = show_develproject(apiurl, dst_project, dst_package)
846 except urllib2.HTTPError:
847 print >>sys.stderr, """\
848 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
849 % (dst_project, dst_package)
853 dst_project != devloc and \
854 src_project != devloc:
856 A different project, %s, is defined as the place where development
857 of the package %s primarily takes place.
858 Please submit there instead, or use --nodevelproject to force direct submission.""" \
859 % (devloc, dst_package)
864 if opts.diff or not opts.message:
866 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
867 rdiff += server_diff(apiurl,
868 dst_project, dst_package, opts.revision,
869 src_project, src_package, None, True)
875 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
876 user = conf.get_apiurl_usr(apiurl)
877 myreqs = [ i for i in reqs if i.state.who == user ]
880 print 'You already created the following submit request: %s.' % \
881 ', '.join([str(i.reqid) for i in myreqs ])
882 repl = raw_input('Supersede the old requests? (y/n/c) ')
883 if repl.lower() == 'c':
884 print >>sys.stderr, 'Aborting'
890 changes_re = re.compile(r'^--- .*\.changes ')
891 for line in rdiff.split('\n'):
892 if line.startswith('--- '):
893 if changes_re.match(line):
898 difflines.append(line)
899 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
901 result = create_submit_request(apiurl,
902 src_project, src_package,
903 dst_project, dst_package,
904 opts.message, orev=opts.revision, src_update=src_update)
905 if repl.lower() == 'y':
907 change_request_state(apiurl, str(req.reqid), 'superseded',
908 'superseded by %s' % result, result)
911 r = change_request_state(conf.config['apiurl'],
912 opts.supersede, 'superseded', opts.message or '', result)
914 print 'created request id', result
917 @cmdln.option('-m', '--message', metavar='TEXT',
918 help='specify message TEXT')
920 @cmdln.alias("deletereq")
921 def do_deleterequest(self, subcmd, opts, *args):
922 """${cmd_name}: Create request to delete a package or project
926 osc deletereq [-m TEXT] PROJECT [PACKAGE]
930 args = slash_split(args)
933 raise oscerr.WrongArgs('Please specify at least a project.')
935 raise oscerr.WrongArgs('Too many arguments.')
937 apiurl = conf.config['apiurl']
945 opts.message = edit_message()
947 result = create_delete_request(apiurl, project, package, opts.message)
951 @cmdln.option('-m', '--message', metavar='TEXT',
952 help='specify message TEXT')
954 @cmdln.alias("changedevelreq")
955 def do_changedevelrequest(self, subcmd, opts, *args):
956 """${cmd_name}: Create request to change the devel package definition.
958 [See http://en.opensuse.org/Build_Service/Collaboration for information
961 See the "request" command for showing and modifing existing requests.
963 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
967 raise oscerr.WrongArgs('Too many arguments.')
969 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
971 devel_project = store_read_project(wd)
972 devel_package = package = store_read_package(wd)
973 apiurl = store_read_apiurl(wd)
974 project = conf.config['getpac_default_project']
977 raise oscerr.WrongArgs('Too few arguments.')
979 apiurl = conf.config['apiurl']
981 devel_project = args[2]
984 devel_package = package
986 devel_package = args[3]
990 footer=textwrap.TextWrapper(width = 66).fill(
991 'please explain why you like to change the devel project of %s/%s to %s/%s'
992 % (project,package,devel_project,devel_package))
993 opts.message = edit_message(footer)
995 result = create_change_devel_request(apiurl,
996 devel_project, devel_package,
1002 @cmdln.option('-d', '--diff', action='store_true',
1003 help='generate a diff')
1004 @cmdln.option('-u', '--unified', action='store_true',
1005 help='output the diff in the unified diff format')
1006 @cmdln.option('-m', '--message', metavar='TEXT',
1007 help='specify message TEXT')
1008 @cmdln.option('-t', '--type', metavar='TYPE',
1009 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1010 @cmdln.option('-a', '--all', action='store_true',
1011 help='all states. Same as\'-s all\'')
1012 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1013 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]')
1014 @cmdln.option('-D', '--days', metavar='DAYS',
1015 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1016 @cmdln.option('-U', '--user', metavar='USER',
1017 help='same as -M, but for the specified USER')
1018 @cmdln.option('-b', '--brief', action='store_true', default=False,
1019 help='print output in list view as list subcommand')
1020 @cmdln.option('-M', '--mine', action='store_true',
1021 help='only show requests created by yourself')
1022 @cmdln.option('-B', '--bugowner', action='store_true',
1023 help='also show requests about packages where I am bugowner')
1024 @cmdln.option('-i', '--interactive', action='store_true',
1025 help='interactive review of request')
1027 @cmdln.alias("review")
1028 def do_request(self, subcmd, opts, *args):
1029 """${cmd_name}: Show and modify requests
1031 [See http://en.opensuse.org/Build_Service/Collaboration for information
1034 This command shows and modifies existing requests. To create new requests
1035 you need to call one of the following:
1038 osc changedevelrequest
1039 To send low level requests to the buildservice API, use:
1042 This command has the following sub commands:
1044 "list" lists open requests attached to a project or package or person.
1045 Uses the project/package of the current directory if none of
1046 -M, -U USER, project/package are given.
1048 "log" will show the history of the given ID
1050 "show" will show the request itself, and generate a diff for review, if
1051 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1053 "decline" will change the request state to "declined" and append a
1054 message that you specify with the --message option.
1056 "wipe" will permanently delete a request.
1058 "revoke" will set the request state to "revoked" and append a
1059 message that you specify with the --message option.
1061 "accept" will change the request state to "accepted" and will trigger
1062 the actual submit process. That would normally be a server-side copy of
1063 the source package to the target package.
1065 "checkout" will checkout the request's source package. This only works for "submit" requests.
1068 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1070 osc request [show] [-d] [-b] ID
1071 osc request accept [-m TEXT] ID
1072 osc request decline [-m TEXT] ID
1073 osc request revoke [-m TEXT] ID
1075 osc request checkout/co ID
1076 osc review accept [-m TEXT] ID
1077 osc review decline [-m TEXT] ID
1081 args = slash_split(args)
1083 if opts.all and opts.state:
1084 raise oscerr.WrongOptions('Sorry, the options --all and --state ' \
1085 'are mutually exclusive.')
1086 if opts.mine and opts.user:
1087 raise oscerr.WrongOptions('Sorry, the options --user and --mine ' \
1088 'are mutually exclusive.')
1093 if opts.state == '':
1096 if opts.state == '':
1099 cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co', 'help']
1100 if not args or args[0] not in cmds:
1101 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1102 % (args[0],', '.join(cmds)))
1108 return self.do_help(['help', 'request'])
1111 min_args, max_args = 1, 1
1112 elif cmd in ['list']:
1113 min_args, max_args = 0, 2
1115 min_args, max_args = 1, 1
1116 if len(args) < min_args:
1117 raise oscerr.WrongArgs('Too few arguments.')
1118 if len(args) > max_args:
1119 raise oscerr.WrongArgs('Too many arguments.')
1121 apiurl = conf.config['apiurl']
1128 elif not opts.mine and not opts.user:
1130 project = store_read_project(os.curdir)
1131 apiurl = store_read_apiurl(os.curdir)
1132 package = store_read_package(os.curdir)
1133 except oscerr.NoWorkingCopy:
1138 elif cmd in ['log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1143 states = ('new', 'accepted', 'revoked', 'declined')
1144 state_list = opts.state.split(',')
1145 if opts.state == 'all':
1146 state_list = ['all']
1148 for s in state_list:
1150 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1153 who = conf.get_apiurl_usr(apiurl)
1157 state_list = ['all']
1159 ## FIXME -B not implemented!
1161 if (self.options.debug):
1162 print 'list: option --bugowner ignored: not impl.'
1164 results = get_request_list(apiurl,
1165 project, package, who, state_list, opts.type)
1166 results.sort(reverse=True)
1168 days = opts.days or conf.config['request_list_days']
1175 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1178 ## bs has received 2009-09-20 a new xquery compare() function
1179 ## which allows us to limit the list inside of get_request_list
1180 ## That would be much faster for coolo. But counting the remainder
1181 ## would not be possible with current xquery implementation.
1182 ## Workaround: fetch all, and filter on client side.
1184 ## FIXME: date filtering should become implemented on server side
1185 for result in results:
1186 if days == 0 or result.state.when > since or result.state.name == 'new':
1187 print result.list_view()
1191 print "There are %d requests older than %s days.\n" % (skipped, days)
1194 for l in get_request_log(conf.config['apiurl'], reqid):
1199 r = get_request(conf.config['apiurl'], reqid)
1202 elif opts.interactive or conf.config['request_show_interactive']:
1203 return request_interactive_review(conf.config['apiurl'], r)
1206 # fixme: will inevitably fail if the given target doesn't exist
1209 print server_diff(conf.config['apiurl'],
1210 r.actions[0].dst_project, r.actions[0].dst_package, None,
1211 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified)
1212 except urllib2.HTTPError, e:
1213 e.osc_msg = 'Diff not possible'
1217 elif cmd == 'checkout' or cmd == 'co':
1218 r = get_request(conf.config['apiurl'], reqid)
1219 submits = [ i for i in r.actions if i.type == 'submit' ]
1220 if not len(submits):
1221 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1222 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1223 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1226 if not opts.message:
1227 opts.message = edit_message()
1228 state_map = {'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1229 # Change review state only
1230 if subcmd == 'review':
1231 if cmd in ['accept', 'decline']:
1232 r = change_review_state(conf.config['apiurl'],
1233 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1235 # Change state of entire request
1236 elif cmd in ['accept', 'decline', 'wipe', 'revoke']:
1237 r = change_request_state(conf.config['apiurl'],
1238 reqid, state_map[cmd], opts.message or '')
1241 # editmeta and its aliases are all depracated
1242 @cmdln.alias("editprj")
1243 @cmdln.alias("createprj")
1244 @cmdln.alias("editpac")
1245 @cmdln.alias("createpac")
1246 @cmdln.alias("edituser")
1247 @cmdln.alias("usermeta")
1249 def do_editmeta(self, subcmd, opts, *args):
1252 Obsolete command to edit metadata. Use 'meta' now.
1254 See the help output of 'meta'.
1258 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1259 print >>sys.stderr, 'See \'osc help meta\'.'
1260 #self.do_help([None, 'meta'])
1264 @cmdln.option('-r', '--revision', metavar='rev',
1265 help='use the specified revision.')
1266 @cmdln.option('-u', '--unset', action='store_true',
1267 help='remove revision in link, it will point always to latest revision')
1268 def do_setlinkrev(self, subcmd, opts, *args):
1269 """${cmd_name}: Updates a revision number in a source link.
1271 This command adds or updates a specified revision number in a source link.
1272 The current revision of the source is used, if no revision number is specified.
1276 osc setlinkrev PROJECT [PACKAGE]
1280 args = slash_split(args)
1281 apiurl = conf.config['apiurl']
1284 p = findpacs(os.curdir)[0]
1289 sys.exit('Local directory is no checked out source link package, aborting')
1290 elif len(args) == 2:
1293 elif len(args) == 1:
1296 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1297 + self.get_cmd_help('setlinkrev'))
1300 packages = [ package ]
1302 packages = meta_get_packagelist(apiurl, project)
1305 print "setting revision for package", p
1309 rev, dummy = parseRevisionOption(opts.revision)
1310 set_link_rev(apiurl, project, p, rev)
1313 def do_linktobranch(self, subcmd, opts, *args):
1314 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1316 This command tells the server to convert a _link with or without a project.diff
1317 to a branch. This is a full copy with a _link file pointing to the branched place.
1320 osc linktobranch # can be used in checked out package
1321 osc linktobranch PROJECT PACKAGE
1325 args = slash_split(args)
1328 project = store_read_project(wd)
1329 package = store_read_package(wd)
1330 apiurl = store_read_apiurl(wd)
1331 update_local_dir = True
1333 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1335 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1337 apiurl = conf.config['apiurl']
1340 update_local_dir = False
1343 link_to_branch(apiurl, project, package)
1344 if update_local_dir:
1346 pac.update(rev=pac.latest_rev())
1349 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1350 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1351 @cmdln.option('-c', '--current', action='store_true',
1352 help='link fixed against current revision.')
1353 @cmdln.option('-r', '--revision', metavar='rev',
1354 help='link the specified revision.')
1355 @cmdln.option('-f', '--force', action='store_true',
1356 help='overwrite an existing link file if it is there.')
1357 @cmdln.option('-d', '--disable-publish', action='store_true',
1358 help='disable publishing of the linked package')
1359 def do_linkpac(self, subcmd, opts, *args):
1360 """${cmd_name}: "Link" a package to another package
1362 A linked package is a clone of another package, but plus local
1363 modifications. It can be cross-project.
1365 The DESTPAC name is optional; the source packages' name will be used if
1368 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1370 To add a patch, add the patch as file and add it to the _link file.
1371 You can also specify text which will be inserted at the top of the spec file.
1373 See the examples in the _link file.
1376 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1380 args = slash_split(args)
1382 if not args or len(args) < 3:
1383 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1384 + self.get_cmd_help('linkpac'))
1386 rev, dummy = parseRevisionOption(opts.revision)
1388 src_project = args[0]
1389 src_package = args[1]
1390 dst_project = args[2]
1392 dst_package = args[3]
1394 dst_package = src_package
1396 if src_project == dst_project and src_package == dst_package:
1397 raise oscerr.WrongArgs('Error: source and destination are the same.')
1399 if src_project == dst_project and not opts.cicount:
1400 # in this case, the user usually wants to build different spec
1401 # files from the same source
1402 opts.cicount = "copy"
1405 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1407 if rev and not checkRevision(src_project, src_package, rev):
1408 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1411 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1413 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1414 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1415 @cmdln.option('-d', '--disable-publish', action='store_true',
1416 help='disable publishing of the aggregated package')
1417 def do_aggregatepac(self, subcmd, opts, *args):
1418 """${cmd_name}: "Aggregate" a package to another package
1420 Aggregation of a package means that the build results (binaries) of a
1421 package are basically copied into another project.
1422 This can be used to make packages available from building that are
1423 needed in a project but available only in a different project. Note
1424 that this is done at the expense of disk space. See
1425 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1426 for more information.
1428 The DESTPAC name is optional; the source packages' name will be used if
1432 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1436 args = slash_split(args)
1438 if not args or len(args) < 3:
1439 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1440 + self.get_cmd_help('aggregatepac'))
1442 src_project = args[0]
1443 src_package = args[1]
1444 dst_project = args[2]
1446 dst_package = args[3]
1448 dst_package = src_package
1450 if src_project == dst_project and src_package == dst_package:
1451 raise oscerr.WrongArgs('Error: source and destination are the same.')
1455 for pair in opts.map_repo.split(','):
1456 src_tgt = pair.split('=')
1457 if len(src_tgt) != 2:
1458 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1459 repo_map[src_tgt[0]] = src_tgt[1]
1461 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1464 @cmdln.option('-c', '--client-side-copy', action='store_true',
1465 help='do a (slower) client-side copy')
1466 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1467 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1468 @cmdln.option('-d', '--keep-develproject', action='store_true',
1469 help='keep develproject tag in the package metadata')
1470 @cmdln.option('-r', '--revision', metavar='rev',
1471 help='link the specified revision.')
1472 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1473 help='URL of destination api server. Default is the source api server.')
1474 @cmdln.option('-m', '--message', metavar='TEXT',
1475 help='specify message TEXT')
1476 @cmdln.option('-e', '--expand', action='store_true',
1477 help='if the source package is a link then copy the expanded version of the link')
1478 def do_copypac(self, subcmd, opts, *args):
1479 """${cmd_name}: Copy a package
1481 A way to copy package to somewhere else.
1483 It can be done across buildservice instances, if the -t option is used.
1484 In that case, a client-side copy is implied.
1486 Using --client-side-copy always involves downloading all files, and
1487 uploading them to the target.
1489 The DESTPAC name is optional; the source packages' name will be used if
1493 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1497 args = slash_split(args)
1499 if not args or len(args) < 3:
1500 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1501 + self.get_cmd_help('copypac'))
1503 src_project = args[0]
1504 src_package = args[1]
1505 dst_project = args[2]
1507 dst_package = args[3]
1509 dst_package = src_package
1511 src_apiurl = conf.config['apiurl']
1513 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1515 dst_apiurl = src_apiurl
1517 if src_project == dst_project and \
1518 src_package == dst_package and \
1519 src_apiurl == dst_apiurl:
1520 raise oscerr.WrongArgs('Source and destination are the same.')
1522 if src_apiurl != dst_apiurl:
1523 opts.client_side_copy = True
1525 rev, dummy = parseRevisionOption(opts.revision)
1528 comment = opts.message
1531 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1532 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1534 r = copy_pac(src_apiurl, src_project, src_package,
1535 dst_apiurl, dst_project, dst_package,
1536 client_side_copy=opts.client_side_copy,
1537 keep_maintainers=opts.keep_maintainers,
1538 keep_develproject=opts.keep_develproject,
1545 @cmdln.option('-c', '--checkout', action='store_true',
1546 help='Checkout branched package afterwards ' \
1547 '(\'osc bco\' is a shorthand for this option)' )
1548 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
1549 help='Use this attribute to find affected packages (default is OBS:Maintained)')
1550 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
1551 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
1552 def do_mbranch(self, subcmd, opts, *args):
1553 """${cmd_name}: Multiple branch of a package
1555 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
1558 This command is used for creating multiple links of defined version of a package
1559 in one project. This is esp. used for maintenance updates.
1561 The branched package will live in
1562 home:USERNAME:branches:ATTRIBUTE:PACKAGE
1563 if nothing else specified.
1566 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
1569 args = slash_split(args)
1572 maintained_attribute = conf.config['maintained_attribute']
1573 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
1575 if not len(args) or len(args) > 2:
1576 raise oscerr.WrongArgs('Wrong number of arguments.')
1582 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
1586 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
1589 print "Project " + r + " created."
1592 init_project_dir(conf.config['apiurl'], r, r)
1593 print statfrmt('A', r)
1596 for package in meta_get_packagelist(conf.config['apiurl'], r):
1598 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
1600 print >>sys.stderr, 'Error while checkout package:\n', package
1602 if conf.config['verbose']:
1603 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1606 @cmdln.alias('branchco')
1608 @cmdln.alias('getpac')
1609 @cmdln.option('--nodevelproject', action='store_true',
1610 help='do not follow a defined devel project ' \
1611 '(primary project where a package is developed)')
1612 @cmdln.option('-c', '--checkout', action='store_true',
1613 help='Checkout branched package afterwards ' \
1614 '(\'osc bco\' is a shorthand for this option)' )
1615 @cmdln.option('-r', '--revision', metavar='rev',
1616 help='branch against a specific revision')
1617 def do_branch(self, subcmd, opts, *args):
1618 """${cmd_name}: Branch a package
1620 [See http://en.opensuse.org/Build_Service/Collaboration for information
1623 Create a source link from a package of an existing project to a new
1624 subproject of the requesters home project (home:branches:)
1626 The branched package will live in
1627 home:USERNAME:branches:PROJECT/PACKAGE
1628 if nothing else specified.
1630 With getpac or bco, the branched package will come from
1631 %(getpac_default_project)s
1632 if nothing else specified.
1635 osc branch SOURCEPROJECT SOURCEPACKAGE
1636 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
1637 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
1638 osc getpac SOURCEPACKAGE
1643 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
1644 args = slash_split(args)
1645 tproject = tpackage = None
1647 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
1648 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
1649 # python has no args.unshift ???
1650 args = [ conf.config['getpac_default_project'] , args[0] ]
1652 if len(args) < 2 or len(args) > 4:
1653 raise oscerr.WrongArgs('Wrong number of arguments.')
1654 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
1656 expected = tproject = args[2]
1660 exists, targetprj, targetpkg, srcprj, srcpkg = \
1661 branch_pkg(conf.config['apiurl'], args[0], args[1],
1662 nodevelproject=opts.nodevelproject, rev=opts.revision,
1663 target_project=tproject, target_package=tpackage,
1664 return_existing=opts.checkout)
1666 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
1669 if not exists and (srcprj is not None and srcprj != args[0] or \
1670 srcprj is None and targetprj != expected):
1671 devloc = srcprj or targetprj
1672 if not srcprj and 'branches:' in targetprj:
1673 devloc = targetprj.split('branches:')[1]
1674 print '\nNote: The branch has been created of a different project,\n' \
1676 ' which is the primary location of where development for\n' \
1677 ' that package takes place.\n' \
1678 ' That\'s also where you would normally make changes against.\n' \
1679 ' A direct branch of the specified package can be forced\n' \
1680 ' with the --nodevelproject option.\n' % devloc
1682 package = tpackage or args[1]
1684 checkout_package(conf.config['apiurl'], targetprj, package,
1685 expand_link=True, prj_dir=targetprj)
1686 if conf.config['verbose']:
1687 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1690 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
1691 apiopt = '-A %s ' % conf.config['apiurl']
1692 print 'A working copy of the branched package can be checked out with:\n\n' \
1694 % (apiopt, targetprj, package)
1695 print_request_list(conf.config['apiurl'], args[0], args[1])
1697 print_request_list(conf.config['apiurl'], devloc, args[1])
1701 @cmdln.option('-f', '--force', action='store_true',
1702 help='deletes a package or an empty project')
1703 def do_rdelete(self, subcmd, opts, *args):
1704 """${cmd_name}: Delete a project or packages on the server.
1706 As a safety measure, project must be empty (i.e., you need to delete all
1707 packages first). If you are sure that you want to remove this project and all
1708 its packages use \'--force\' switch.
1711 osc rdelete -f PROJECT
1712 osc rdelete PROJECT PACKAGE [PACKAGE ...]
1717 args = slash_split(args)
1719 raise oscerr.WrongArgs('Missing argument.')
1725 # careful: if pkg is an empty string, the package delete request results
1726 # into a project delete request - which works recursively...
1728 delete_package(conf.config['apiurl'], prj, pkg)
1729 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
1730 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
1731 'If you are sure that you want to remove this project and all its ' \
1732 'packages use the \'--force\' switch'
1735 delete_project(conf.config['apiurl'], prj)
1738 def do_deletepac(self, subcmd, opts, *args):
1739 print """${cmd_name} is obsolete !
1742 osc delete for checked out packages or projects
1744 osc rdelete for server side operations."""
1749 @cmdln.option('-f', '--force', action='store_true',
1750 help='deletes a project and its packages')
1751 def do_deleteprj(self, subcmd, opts, project):
1752 """${cmd_name} is obsolete !
1759 @cmdln.alias('metafromspec')
1760 @cmdln.option('', '--specfile', metavar='FILE',
1761 help='Path to specfile. (if you pass more than working copy this option is ignored)')
1762 def do_updatepacmetafromspec(self, subcmd, opts, *args):
1763 """${cmd_name}: Update package meta information from a specfile
1765 ARG, if specified, is a package working copy.
1771 args = parseargs(args)
1772 if opts.specfile and len(args) == 1:
1773 specfile = opts.specfile
1776 pacs = findpacs(args)
1778 p.read_meta_from_spec(specfile)
1779 p.update_package_meta()
1783 @cmdln.option('-c', '--change', metavar='rev',
1784 help='the change made by revision rev (like -r rev-1:rev).'
1785 'If rev is negative this is like -r rev:rev-1.')
1786 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
1787 help='If rev1 is specified it will compare your working copy against '
1788 'the revision (rev1) on the server. '
1789 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
1790 '(NOTE: changes in your working copy are ignored in this case)')
1791 @cmdln.option('-p', '--plain', action='store_true',
1792 help='output the diff in plain (not unified) diff format')
1793 def do_diff(self, subcmd, opts, *args):
1794 """${cmd_name}: Generates a diff
1796 Generates a diff, comparing local changes against the repository
1799 ARG, specified, is a filename to include in the diff.
1805 args = parseargs(args)
1806 pacs = findpacs(args)
1810 rev = int(opts.change)
1820 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1823 rev1, rev2 = parseRevisionOption(opts.revision)
1827 diff += ''.join(make_diff(pac, rev1))
1829 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
1830 pac.prjname, pac.name, rev2, not opts.plain)
1835 @cmdln.option('--oldprj', metavar='OLDPRJ',
1836 help='project to compare against'
1837 ' (deprecated, use 3 argument form)')
1838 @cmdln.option('--oldpkg', metavar='OLDPKG',
1839 help='package to compare against'
1840 ' (deprecated, use 3 argument form)')
1841 @cmdln.option('-r', '--revision', metavar='N[:M]',
1842 help='revision id, where N = old revision and M = new revision')
1843 @cmdln.option('-p', '--plain', action='store_true',
1844 help='output the diff in plain (not unified) diff format')
1845 @cmdln.option('-c', '--change', metavar='rev',
1846 help='the change made by revision rev (like -r rev-1:rev). '
1847 'If rev is negative this is like -r rev:rev-1.')
1848 def do_rdiff(self, subcmd, opts, *args):
1849 """${cmd_name}: Server-side "pretty" diff of two packages
1851 Compares two packages (three or four arguments) or shows the
1852 changes of a specified revision of a package (two arguments)
1854 If no revision is specified the latest revision is used.
1856 Note that this command doesn't return a normal diff (which could be
1857 applied as patch), but a "pretty" diff, which also compares the content
1862 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
1863 osc ${cmd_name} PROJECT PACKAGE
1867 args = slash_split(args)
1878 new_project = args[0]
1879 new_package = args[1]
1881 old_project = opts.oldprj
1883 old_package = opts.oldpkg
1884 elif len(args) == 3 or len(args) == 4:
1885 if opts.oldprj or opts.oldpkg:
1886 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
1887 old_project = args[0]
1888 new_package = old_package = args[1]
1889 new_project = args[2]
1891 new_package = args[3]
1893 raise oscerr.WrongArgs('Wrong number of arguments')
1898 rev = int(opts.change)
1908 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1912 rev1, rev2 = parseRevisionOption(opts.revision)
1914 rdiff = server_diff(conf.config['apiurl'],
1915 old_project, old_package, rev1,
1916 new_project, new_package, rev2, not opts.plain)
1922 def do_install(self, subcmd, opts, *args):
1923 """${cmd_name}: install a package after build via zypper in -r
1925 Not implemented yet. Use osc repourls,
1926 select the url you best like (standard),
1927 chop off after the last /, this should work with zypper.
1934 args = slash_split(args)
1935 args = expand_proj_pack(args)
1938 ## if there is only one argument, and it ends in .ymp
1939 ## then fetch it, Parse XML to get the first
1940 ## metapackage.group.repositories.repository.url
1941 ## and construct zypper cmd's for all
1942 ## metapackage.group.software.item.name
1944 ## if args[0] is already an url, the use it as is.
1946 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])
1947 print self.do_install.__doc__
1948 print "Example: \n" + cmd
1951 def do_repourls(self, subcmd, opts, *args):
1952 """${cmd_name}: Shows URLs of .repo files
1954 Shows URLs on which to access the project .repos files (yum-style
1955 metadata) on download.opensuse.org.
1958 osc repourls [PROJECT]
1963 apiurl = conf.config['apiurl']
1967 elif len(args) == 0:
1968 project = store_read_project('.')
1969 apiurl = store_read_apiurl('.')
1971 raise oscerr.WrongArgs('Wrong number of arguments')
1973 # XXX: API should somehow tell that
1974 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1975 repos = get_repositories_of_project(apiurl, project)
1977 print url_tmpl % (project.replace(':', ':/'), repo, project)
1980 @cmdln.option('-r', '--revision', metavar='rev',
1981 help='checkout the specified revision. '
1982 'NOTE: if you checkout the complete project '
1983 'this option is ignored!')
1984 @cmdln.option('-e', '--expand-link', action='store_true',
1985 help='if a package is a link, check out the expanded '
1986 'sources (no-op, since this became the default)')
1987 @cmdln.option('-u', '--unexpand-link', action='store_true',
1988 help='if a package is a link, check out the _link file ' \
1989 'instead of the expanded sources')
1990 @cmdln.option('-c', '--current-dir', action='store_true',
1991 help='place PACKAGE folder in the current directory' \
1992 'instead of a PROJECT/PACKAGE directory')
1993 @cmdln.option('-s', '--source-service-files', action='store_true',
1994 help='server side generated files of source services' \
1995 'gets downloaded as well' )
1997 def do_checkout(self, subcmd, opts, *args):
1998 """${cmd_name}: Check out content from the repository
2000 Check out content from the repository server, creating a local working
2003 When checking out a single package, the option --revision can be used
2004 to specify a revision of the package to be checked out.
2006 When a package is a source link, then it will be checked out in
2007 expanded form. If --unexpand-link option is used, the checkout will
2008 instead produce the raw _link file plus patches.
2011 osc co PROJECT [PACKAGE] [FILE]
2012 osc co PROJECT # entire project
2013 osc co PROJECT PACKAGE # a package
2014 osc co PROJECT PACKAGE FILE # single file -> to current dir
2016 while inside a project directory:
2017 osc co PACKAGE # check out PACKAGE from project
2022 if opts.unexpand_link:
2026 if opts.source_service_files:
2027 service_files = True
2029 service_files = False
2031 args = slash_split(args)
2032 project = package = filename = None
2033 apiurl = conf.config['apiurl']
2035 project = project_dir = args[0]
2041 if args and len(args) == 1:
2042 localdir = os.getcwd()
2043 if is_project_dir(localdir):
2044 project = store_read_project(localdir)
2045 project_dir = localdir
2047 apiurl = store_read_apiurl(localdir)
2049 rev, dummy = parseRevisionOption(opts.revision)
2053 if rev and rev != "latest" and not checkRevision(project, package, rev):
2054 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2058 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2061 if opts.current_dir:
2063 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2064 prj_dir=project_dir, service_files=service_files, progress_obj=self.download_progress)
2065 print_request_list(apiurl, project, package)
2069 if sys.platform[:3] == 'win':
2070 prj_dir = prj_dir.replace(':', ';')
2071 if os.path.exists(prj_dir):
2072 sys.exit('osc: project \'%s\' already exists' % project)
2074 # check if the project does exist (show_project_meta will throw an exception)
2075 show_project_meta(apiurl, project)
2077 init_project_dir(apiurl, prj_dir, project)
2078 print statfrmt('A', prj_dir)
2081 for package in meta_get_packagelist(apiurl, project):
2083 checkout_package(apiurl, project, package, expand_link = expand_link, \
2084 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2085 except oscerr.LinkExpandError, e:
2086 print >>sys.stderr, 'Link cannot be expanded:\n', e
2087 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2088 # check out in unexpanded form at least
2089 checkout_package(apiurl, project, package, expand_link = False, \
2090 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2091 print_request_list(apiurl, project)
2094 raise oscerr.WrongArgs('Missing argument.\n\n' \
2095 + self.get_cmd_help('checkout'))
2098 @cmdln.option('-q', '--quiet', action='store_true',
2099 help='print as little as possible')
2100 @cmdln.option('-v', '--verbose', action='store_true',
2101 help='print extra information')
2103 def do_status(self, subcmd, opts, *args):
2104 """${cmd_name}: Show status of files in working copy
2106 Show the status of files in a local working copy, indicating whether
2107 files have been changed locally, deleted, added, ...
2109 The first column in the output specifies the status and is one of the
2110 following characters:
2111 ' ' no modifications
2116 '?' item is not under version control
2117 '!' item is missing (removed by non-osc command) or incomplete
2122 osc st file1 file2 ...
2125 osc status [OPTS] [PATH...]
2129 args = parseargs(args)
2131 # storage for single Package() objects
2133 # storage for a project dir ( { prj_instance : [ package objects ] } )
2136 # when 'status' is run inside a project dir, it should
2137 # stat all packages existing in the wc
2138 if is_project_dir(arg):
2139 prj = Project(arg, False)
2141 if conf.config['do_package_tracking']:
2143 for pac in prj.pacs_have:
2144 # we cannot create package objects if the dir does not exist
2145 if not pac in prj.pacs_broken:
2146 prjpacs[prj].append(os.path.join(arg, pac))
2148 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2149 elif is_package_dir(arg):
2150 pacpaths.append(arg)
2151 elif os.path.isfile(arg):
2152 pacpaths.append(arg)
2154 msg = '\'%s\' is neither a project or a package directory' % arg
2155 raise oscerr.NoWorkingCopy, msg
2157 # process single packages
2158 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2159 # process project dirs
2160 for prj, pacs in prjpacs.iteritems():
2161 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2163 print '\n'.join(lines)
2166 def do_add(self, subcmd, opts, *args):
2167 """${cmd_name}: Mark files to be added upon the next commit
2170 osc add FILE [FILE...]
2174 raise oscerr.WrongArgs('Missing argument.\n\n' \
2175 + self.get_cmd_help('add'))
2177 filenames = parseargs(args)
2181 def do_mkpac(self, subcmd, opts, *args):
2182 """${cmd_name}: Create a new package under version control
2185 osc mkpac new_package
2188 if not conf.config['do_package_tracking']:
2189 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2190 "in the [general] section in the configuration file"
2194 raise oscerr.WrongArgs('Wrong number of arguments.')
2196 createPackageDir(args[0])
2198 @cmdln.option('-r', '--recursive', action='store_true',
2199 help='If CWD is a project dir then scan all package dirs as well')
2201 def do_addremove(self, subcmd, opts, *args):
2202 """${cmd_name}: Adds new files, removes disappeared files
2204 Adds all files new in the local copy, and removes all disappeared files.
2206 ARG, if specified, is a package working copy.
2212 args = parseargs(args)
2214 for arg in arg_list:
2215 if is_project_dir(arg) and conf.config['do_package_tracking']:
2216 prj = Project(arg, False)
2217 for pac in prj.pacs_unvers:
2218 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2219 if os.path.isdir(pac_dir):
2220 addFiles([pac_dir], prj)
2221 for pac in prj.pacs_broken:
2222 if prj.get_state(pac) != 'D':
2223 prj.set_state(pac, 'D')
2224 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
2226 for pac in prj.pacs_have:
2227 state = prj.get_state(pac)
2228 if state != None and state != 'D':
2229 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2230 args.append(pac_dir)
2232 prj.write_packages()
2233 elif is_project_dir(arg):
2234 print >>sys.stderr, 'osc: addremove is not supported in a project dir unless ' \
2235 '\'do_package_tracking\' is enabled in the configuration file'
2238 pacs = findpacs(args)
2240 p.todo = p.filenamelist + p.filenamelist_unvers
2242 for filename in p.todo:
2243 if os.path.isdir(filename):
2245 # ignore foo.rXX, foo.mine for files which are in 'C' state
2246 if os.path.splitext(filename)[0] in p.in_conflict:
2248 state = p.status(filename)
2251 # TODO: should ignore typical backup files suffix ~ or .orig
2253 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
2255 p.put_on_deletelist(filename)
2256 p.write_deletelist()
2257 os.unlink(os.path.join(p.storedir, filename))
2258 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
2263 @cmdln.alias('checkin')
2264 @cmdln.option('-m', '--message', metavar='TEXT',
2265 help='specify log message TEXT')
2266 @cmdln.option('-F', '--file', metavar='FILE',
2267 help='read log message from FILE')
2268 @cmdln.option('-f', '--force', default=False, action="store_true",
2269 help='force commit - do not tests a file list')
2270 def do_commit(self, subcmd, opts, *args):
2271 """${cmd_name}: Upload content to the repository server
2273 Upload content which is changed in your working copy, to the repository
2276 Optionally checks the state of a working copy, if found a file with
2277 unknown state, it requests an user input:
2278 * skip - don't change anything, just move to another file
2279 * remove - remove a file from dir
2280 * edit file list - edit filelist using EDITOR
2281 * commit - don't check anything and commit package
2282 * abort - abort commit - this is default value
2283 This can be supressed by check_filelist config item, or -f/--force
2284 command line option.
2287 osc ci # current dir
2289 osc ci file1 file2 ...
2295 args = parseargs(args)
2302 msg = open(opts.file).read()
2304 sys.exit('could not open file \'%s\'.' % opts.file)
2307 for arg in arg_list:
2308 if conf.config['do_package_tracking'] and is_project_dir(arg):
2309 Project(arg).commit(msg=msg)
2311 msg = edit_message()
2314 pacs = findpacs(args)
2316 if conf.config['check_filelist'] and not opts.force:
2317 check_filelist_before_commit(pacs)
2320 template = store_read_file(os.path.abspath('.'), '_commit_msg')
2321 # open editor for commit message
2322 # but first, produce status and diff to append to the template
2326 changed = getStatus([pac], quiet=True)
2329 diffs += ['\nDiff for working copy: %s' % pac.dir]
2330 diffs += make_diff(pac, 0)
2331 lines.extend(get_commit_message_template(pac))
2332 if template == None:
2333 template='\n'.join(lines)
2334 # if footer is empty, there is nothing to commit, and no edit needed.
2336 msg = edit_message(footer='\n'.join(footer), template=template)
2339 store_write_string(os.path.abspath('.'), '_commit_msg', msg)
2341 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2343 if conf.config['do_package_tracking'] and len(pacs) > 0:
2347 # it is possible to commit packages from different projects at the same
2348 # time: iterate over all pacs and put each pac to the right project in the dict
2350 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
2351 if is_project_dir(path):
2352 pac_path = os.path.basename(os.path.normpath(pac.absdir))
2353 prj_paths.setdefault(path, []).append(pac_path)
2354 files[pac_path] = pac.todo
2356 single_paths.append(pac.dir)
2357 for prj, packages in prj_paths.iteritems():
2358 Project(prj).commit(tuple(packages), msg, files)
2359 for pac in single_paths:
2360 Package(pac).commit(msg)
2365 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2367 @cmdln.option('-r', '--revision', metavar='REV',
2368 help='update to specified revision (this option will be ignored '
2369 'if you are going to update the complete project or more than '
2371 @cmdln.option('-u', '--unexpand-link', action='store_true',
2372 help='if a package is an expanded link, update to the raw _link file')
2373 @cmdln.option('-e', '--expand-link', action='store_true',
2374 help='if a package is a link, update to the expanded sources')
2375 @cmdln.option('-s', '--source-service-files', action='store_true',
2376 help='Use server side generated sources instead of local generation.' )
2378 def do_update(self, subcmd, opts, *args):
2379 """${cmd_name}: Update a working copy
2384 If the current working directory is a package, update it.
2385 If the directory is a project directory, update all contained
2386 packages, AND check out newly added packages.
2388 To update only checked out packages, without checking out new
2389 ones, you might want to use "osc up *" from within the project
2393 Update the packages specified by the path argument(s)
2395 When --expand-link is used with source link packages, the expanded
2396 sources will be checked out. Without this option, the _link file and
2397 patches will be checked out. The option --unexpand-link can be used to
2398 switch back to the "raw" source with a _link file plus patch(es).
2404 if (opts.expand_link and opts.unexpand_link) \
2405 or (opts.expand_link and opts.revision) \
2406 or (opts.unexpand_link and opts.revision):
2407 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
2408 '--revision are mutually exclusive.')
2410 if opts.source_service_files: service_files = True
2411 else: service_files = False
2413 args = parseargs(args)
2416 for arg in arg_list:
2417 if is_project_dir(arg):
2418 prj = Project(arg, progress_obj=self.download_progress)
2420 if conf.config['do_package_tracking']:
2421 prj.update(expand_link=opts.expand_link,
2422 unexpand_link=opts.unexpand_link)
2425 # if not tracking package, and 'update' is run inside a project dir,
2426 # it should do the following:
2427 # (a) update all packages
2428 args += prj.pacs_have
2429 # (b) fetch new packages
2430 prj.checkout_missing_pacs(expand_link = not opts.unexpand_link)
2432 print_request_list(prj.apiurl, prj.name)
2435 pacs = findpacs(args, progress_obj=self.download_progress)
2437 if opts.revision and len(args) == 1:
2438 rev, dummy = parseRevisionOption(opts.revision)
2439 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
2440 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2447 print 'Updating %s' % p.name
2449 # FIXME: ugly workaround for #399247
2450 if opts.expand_link or opts.unexpand_link:
2451 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' and p.status(i) != '?']:
2452 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
2453 'copy has local modifications.\nPlease revert/commit them ' \
2458 if opts.expand_link and p.islink() and not p.isexpanded():
2459 if p.haslinkerror():
2461 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev)
2463 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev, linkrev="base")
2466 rev = p.linkinfo.xsrcmd5
2467 print 'Expanding to rev', rev
2468 elif opts.unexpand_link and p.islink() and p.isexpanded():
2469 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
2470 rev = p.linkinfo.lsrcmd5
2471 elif p.islink() and p.isexpanded():
2472 rev = p.latest_rev()
2474 p.update(rev, service_files)
2475 if opts.unexpand_link:
2478 print_request_list(p.apiurl, p.prjname, p.name)
2481 @cmdln.option('-f', '--force', action='store_true',
2482 help='forces removal of entire package and its files')
2485 @cmdln.alias('remove')
2486 def do_delete(self, subcmd, opts, *args):
2487 """${cmd_name}: Mark files or package directories to be deleted upon the next 'checkin'
2490 cd .../PROJECT/PACKAGE
2491 osc delete FILE [...]
2493 osc delete PACKAGE [...]
2495 This command works on check out copies. Use "rdelete" for working on server
2496 side only. This is needed for removing the entire project.
2498 As a safety measure, projects must be empty (i.e., you need to delete all
2501 If you are sure that you want to remove a package and all
2502 its files use \'--force\' switch. Sometimes this also works without --force.
2508 raise oscerr.WrongArgs('Missing argument.\n\n' \
2509 + self.get_cmd_help('delete'))
2511 args = parseargs(args)
2512 # check if args contains a package which was removed by
2513 # a non-osc command and mark it with the 'D'-state
2516 if not os.path.exists(i):
2517 prj_dir, pac_dir = getPrjPacPaths(i)
2518 if is_project_dir(prj_dir):
2519 prj = Project(prj_dir, False)
2520 if i in prj.pacs_broken:
2521 if prj.get_state(i) != 'A':
2522 prj.set_state(pac_dir, 'D')
2524 prj.del_package_node(i)
2525 print statfrmt('D', getTransActPath(i))
2527 prj.write_packages()
2528 pacs = findpacs(args)
2532 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
2533 if is_project_dir(prj_dir):
2534 if conf.config['do_package_tracking']:
2535 prj = Project(prj_dir, False)
2536 prj.delPackage(p, opts.force)
2538 print "WARNING: package tracking is disabled, operation skipped !"
2540 pathn = getTransActPath(p.dir)
2541 for filename in p.todo:
2542 ret, state = p.delete_file(filename, opts.force)
2544 print statfrmt('D', os.path.join(pathn, filename))
2547 sys.exit('\'%s\' is not under version control' % filename)
2548 elif state in ['A', 'M'] and not opts.force:
2549 sys.exit('\'%s\' has local modifications (use --force to remove this file)' % filename)
2552 def do_resolved(self, subcmd, opts, *args):
2553 """${cmd_name}: Remove 'conflicted' state on working copy files
2555 If an upstream change can't be merged automatically, a file is put into
2556 in 'conflicted' ('C') state. Within the file, conflicts are marked with
2557 special <<<<<<< as well as ======== and >>>>>>> lines.
2559 After manually resolving all conflicting parts, use this command to
2560 remove the 'conflicted' state.
2562 Note: this subcommand does not semantically resolve conflicts or
2563 remove conflict markers; it merely removes the conflict-related
2564 artifact files and allows PATH to be committed again.
2567 osc resolved FILE [FILE...]
2572 raise oscerr.WrongArgs('Missing argument.\n\n' \
2573 + self.get_cmd_help('resolved'))
2575 args = parseargs(args)
2576 pacs = findpacs(args)
2579 for filename in p.todo:
2580 print 'Resolved conflicted state of "%s"' % filename
2581 p.clear_from_conflictlist(filename)
2584 @cmdln.alias('platforms')
2585 def do_repositories(self, subcmd, opts, *args):
2586 """${cmd_name}: Shows available repositories
2590 Shows all available repositories/build targets
2592 2. osc repositories <project>
2593 Shows the configured repositories/build targets of a project
2601 print '\n'.join(get_repositories_of_project(conf.config['apiurl'], project))
2603 print '\n'.join(get_repositories(conf.config['apiurl']))
2607 def do_results_meta(self, subcmd, opts, *args):
2608 print "Command results_meta is obsolete. Please use: osc results --xml"
2612 @cmdln.option('-l', '--last-build', action='store_true',
2613 help='show last build results (succeeded/failed/unknown)')
2614 @cmdln.option('-r', '--repo', action='append', default = [],
2615 help='Show results only for specified repo(s)')
2616 @cmdln.option('-a', '--arch', action='append', default = [],
2617 help='Show results only for specified architecture(s)')
2618 @cmdln.option('', '--xml', action='store_true',
2619 help='generate output in XML (former results_meta)')
2620 def do_rresults(self, subcmd, opts, *args):
2621 print "Command rresults is obsolete. Running 'osc results' instead"
2622 self.do_results('results', opts, *args)
2626 @cmdln.option('-f', '--force', action='store_true', default=False,
2627 help="Don't ask and delete files")
2628 def do_rremove(self, subcmd, opts, project, package, *files):
2629 """${cmd_name}: Remove source files from selected package
2636 if not '/' in project:
2637 raise oscerr.WrongArgs("Missing operand, type osc help rremove for help")
2640 project, package = project.split('/')
2644 resp = raw_input("rm: remove source file `%s' from `%s/%s'? (yY|nN) " % (file, project, package))
2645 if resp not in ('y', 'Y'):
2648 delete_files(conf.config['apiurl'], project, package, (file, ))
2649 except urllib2.HTTPError, e:
2651 print >>sys.stderr, e
2653 if e.code in [ 400, 403, 404, 500 ]:
2654 if '<summary>' in body:
2655 msg = body.split('<summary>')[1]
2656 msg = msg.split('</summary>')[0]
2657 print >>sys.stderr, msg
2662 @cmdln.option('-l', '--last-build', action='store_true',
2663 help='show last build results (succeeded/failed/unknown)')
2664 @cmdln.option('-r', '--repo', action='append', default = [],
2665 help='Show results only for specified repo(s)')
2666 @cmdln.option('-a', '--arch', action='append', default = [],
2667 help='Show results only for specified architecture(s)')
2668 @cmdln.option('', '--xml', action='store_true',
2669 help='generate output in XML (former results_meta)')
2670 def do_results(self, subcmd, opts, *args):
2671 """${cmd_name}: Shows the build results of a package
2674 osc results (inside working copy)
2675 osc results remote_project remote_package
2680 args = slash_split(args)
2682 apiurl = conf.config['apiurl']
2685 if is_project_dir(wd):
2689 opts.hide_legend = None
2690 opts.name_filter = None
2691 opts.status_filter = None
2692 opts.vertical = None
2693 self.do_prjresults('prjresults', opts, *args);
2696 project = store_read_project(wd)
2697 package = store_read_package(wd)
2698 apiurl = store_read_apiurl(wd)
2700 raise oscerr.WrongArgs('Too few arguments (required none or two)')
2702 raise oscerr.WrongArgs('Too many arguments (required none or two)')
2711 func = show_results_meta
2714 print delim.join(func(apiurl, project, package, opts.last_build, opts.repo, opts.arch))
2716 # WARNING: this function is also called by do_results. You need to set a default there
2717 # as well when adding a new option!
2718 @cmdln.option('-q', '--hide-legend', action='store_true',
2719 help='hide the legend')
2720 @cmdln.option('-c', '--csv', action='store_true',
2722 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2723 help='show only packages with buildstatus STATUS (see legend)')
2724 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2725 help='show only packages whose names match EXPR')
2726 @cmdln.option('-a', '--arch', metavar='ARCH',
2727 help='show results only for specified architecture(s)')
2728 @cmdln.option('-r', '--repo', metavar='REPO',
2729 help='show results only for specified repo(s)')
2730 @cmdln.option('-V', '--vertical', action='store_true',
2731 help='list packages vertically instead horizontally')
2733 def do_prjresults(self, subcmd, opts, *args):
2734 """${cmd_name}: Shows project-wide build results
2737 osc prjresults (inside working copy)
2738 osc prjresults PROJECT
2744 apiurl = conf.config['apiurl']
2748 raise oscerr.WrongArgs('Wrong number of arguments.')
2751 project = store_read_project(wd)
2752 apiurl = store_read_apiurl(wd)
2754 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))
2757 @cmdln.option('-q', '--hide-legend', action='store_true',
2758 help='hide the legend')
2759 @cmdln.option('-c', '--csv', action='store_true',
2761 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2762 help='show only packages with buildstatus STATUS (see legend)')
2763 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2764 help='show only packages whose names match EXPR')
2767 def do_rprjresults(self, subcmd, opts, *args):
2768 print "Command rprjresults is obsolete. Please use 'osc prjresults'"
2772 @cmdln.option('-s', '--start', metavar='START',
2773 help='get log starting from the offset')
2774 def do_buildlog(self, subcmd, opts, *args):
2775 """${cmd_name}: Shows the build log of a package
2777 Shows the log file of the build of a package. Can be used to follow the
2778 log while it is being written.
2779 Needs to be called from within a package directory.
2781 The arguments REPOSITORY and ARCH are the first two columns in the 'osc
2782 results' output. If the buildlog url is used buildlog command has the
2783 same behavior as remotebuildlog.
2785 ${cmd_usage} [REPOSITORY ARCH | BUILDLOGURL]
2789 repository = arch = None
2791 if len(args) == 1 and args[0].startswith('http'):
2792 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2795 package = store_read_package(wd)
2796 project = store_read_project(wd)
2797 apiurl = store_read_apiurl(wd)
2801 offset = int(opts.start)
2803 if not repository or not arch:
2807 repository = args[0]
2810 print_buildlog(apiurl, project, package, repository, arch, offset)
2813 def print_repos(self):
2816 if is_package_dir(wd):
2819 elif is_project_dir(wd):
2824 print 'Valid arguments for this %s are:' % str
2826 self.do_repos(None, None)
2828 raise oscerr.WrongArgs('Missing arguments')
2831 @cmdln.alias('rbuildlog')
2832 @cmdln.option('-s', '--start', metavar='START',
2833 help='get log starting from the offset')
2834 def do_remotebuildlog(self, subcmd, opts, *args):
2835 """${cmd_name}: Shows the build log of a package
2837 Shows the log file of the build of a package. Can be used to follow the
2838 log while it is being written.
2841 osc remotebuildlog project package repository arch
2843 osc remotebuildlog project/package/repository/arch
2845 osc remotebuildlog buildlogurl
2848 if len(args) == 1 and args[0].startswith('http'):
2849 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2851 args = slash_split(args)
2852 apiurl = conf.config['apiurl']
2854 raise oscerr.WrongArgs('Too few arguments.')
2856 raise oscerr.WrongArgs('Too many arguments.')
2858 project, package, repository, arch = args
2862 offset = int(opts.start)
2864 print_buildlog(apiurl, project, package, repository, arch, offset)
2867 @cmdln.option('-s', '--start', metavar='START',
2868 help='get log starting from offset')
2869 def do_localbuildlog(self, subcmd, opts, *args):
2870 """${cmd_name}: Shows the build log of a local buildchroot
2873 osc lbl [REPOSITORY ARCH]
2874 osc lbl # show log of newest last local build
2878 if conf.config['build-type']:
2879 # FIXME: raise Exception instead
2880 print >>sys.stderr, 'Not implemented for VMs'
2884 package = store_read_package('.')
2886 files = glob.glob(os.path.join(os.getcwd(), store, "_buildinfo-*"))
2889 raise oscerr.WrongArgs('No buildconfig found, please specify repo and arch manually.')
2893 if os.stat(f).st_mtime > os.stat(cfg).st_mtime:
2895 root = ET.parse(cfg).getroot()
2896 project = root.get("project")
2897 repo = root.get("repository")
2898 arch = root.find("arch").text
2899 elif len(args) == 2:
2900 project = store_read_project('.')
2901 package = store_read_package('.')
2905 if is_package_dir(os.curdir):
2907 raise oscerr.WrongArgs('Wrong number of arguments.')
2909 buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root'])
2910 buildroot = buildroot % {'project': project, 'package': package,
2911 'repo': repo, 'arch': arch}
2914 offset = int(opts.start)
2915 logfile = os.path.join(buildroot, '.build.log')
2916 if not os.path.isfile(logfile):
2917 raise oscerr.OscIOError(None, 'logfile \'%s\' does not exist' % logfile)
2918 f = open(logfile, 'r')
2920 data = f.read(BUFSIZE)
2922 sys.stdout.write(data)
2923 data = f.read(BUFSIZE)
2927 def do_triggerreason(self, subcmd, opts, *args):
2928 """${cmd_name}: Show reason why a package got triggered to build
2930 The server decides when a package needs to get rebuild, this command
2931 shows the detailed reason for a package. A brief reason is also stored
2932 in the jobhistory, which can be accessed via "osc jobhistory".
2934 Trigger reasons might be:
2935 - new build (never build yet or rebuild manually forced)
2936 - source change (eg. on updating sources)
2937 - meta change (packages which are used for building have changed)
2938 - rebuild count sync (In case that it is configured to sync release numbers)
2940 usage in package or project directory:
2941 osc reason REPOSITORY ARCH
2942 osc reason PROJECT PACKAGE REPOSITORY ARCH
2947 args = slash_split(args)
2948 project = package = repository = arch = None
2953 if len(args) == 2: # 2
2954 if is_package_dir('.'):
2955 package = store_read_package(wd)
2957 raise oscerr.WrongArgs('package is not specified.')
2958 project = store_read_project(wd)
2959 apiurl = store_read_apiurl(wd)
2960 repository = args[0]
2962 elif len(args) == 4:
2963 apiurl = conf.config['apiurl']
2966 repository = args[2]
2969 raise oscerr.WrongArgs('Too many arguments.')
2971 print apiurl, project, package, repository, arch
2972 xml = show_package_trigger_reason(apiurl, project, package, repository, arch)
2973 root = ET.fromstring(xml)
2974 reason = root.find('explain').text
2976 if reason == "meta change":
2977 print "changed keys:"
2978 for package in root.findall('packagechange'):
2979 print " ", package.get('change'), package.get('key')
2982 # FIXME: the new osc syntax should allow to specify multiple packages
2983 # FIXME: the command should optionally use buildinfo data to show all dependencies
2984 @cmdln.alias('whatdependson')
2985 def do_dependson(self, subcmd, opts, *args):
2986 """${cmd_name}: Show the build dependencies
2988 The command dependson and whatdependson can be used to find out what
2989 will be triggered when a certain package changes.
2990 This is no guarantee, since the new build might have changed dependencies.
2992 dependson shows the build dependencies inside of a project, valid for a
2993 given repository and architecture.
2994 NOTE: to see all binary packages, which can trigger a build you need to
2995 refer the buildinfo, since this command shows only the dependencies
2996 inside of a project.
2998 The arguments REPOSITORY and ARCH can be taken from the first two columns
2999 of the 'osc repos' output.
3001 usage in package or project directory:
3002 osc dependson REPOSITORY ARCH
3003 osc whatdependson REPOSITORY ARCH
3006 osc dependson PROJECT [PACKAGE] REPOSITORY ARCH
3007 osc whatdependson PROJECT [PACKAGE] REPOSITORY ARCH
3012 args = slash_split(args)
3013 project = packages = repository = arch = reverse = None
3015 if len(args) < 2 and (is_package_dir('.') or is_project_dir('.')):
3019 raise oscerr.WrongArgs('Too many arguments.')
3021 if len(args) < 3: # 2
3022 if is_package_dir('.'):
3023 packages = [store_read_package(wd)]
3024 elif not is_project_dir('.'):
3025 raise oscerr.WrongArgs('Project and package is not specified.')
3026 project = store_read_project(wd)
3027 apiurl = store_read_apiurl(wd)
3028 repository = args[0]
3032 apiurl = conf.config['apiurl']
3034 repository = args[1]
3038 apiurl = conf.config['apiurl']
3040 packages = [args[1]]
3041 repository = args[2]
3044 if subcmd == 'whatdependson':
3047 xml = get_dependson(apiurl, project, repository, arch, packages, reverse)
3049 root = ET.fromstring(xml)
3050 for package in root.findall('package'):
3051 print package.get('name'), ":"
3052 for dep in package.findall('pkgdep'):
3056 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
3057 help='Add this package when computing the buildinfo')
3058 def do_buildinfo(self, subcmd, opts, *args):
3059 """${cmd_name}: Shows the build info
3061 Shows the build "info" which is used in building a package.
3062 This command is mostly used internally by the 'build' subcommand.
3063 It needs to be called from within a package directory.
3065 The BUILD_DESCR argument is optional. BUILD_DESCR is a local RPM specfile
3066 or Debian "dsc" file. If specified, it is sent to the server, and the
3067 buildinfo will be based on it. If the argument is not supplied, the
3068 buildinfo is derived from the specfile which is currently on the source
3071 The returned data is XML and contains a list of the packages used in
3072 building, their source, and the expanded BuildRequires.
3074 The arguments REPOSITORY and ARCH can be taken from the first two columns
3075 of the 'osc repos' output.
3078 osc buildinfo REPOSITORY ARCH [BUILD_DESCR] (in pkg dir)
3079 osc buildinfo PROJECT PACKAGE REPOSITORY ARCH [BUILD_DESCR]
3084 args = slash_split(args)
3086 if len(args) < 2 and is_package_dir('.'):
3090 raise oscerr.WrongArgs('Too many arguments.')
3092 if len(args) < 4: # 2 or 3
3093 package = store_read_package(wd)
3094 project = store_read_project(wd)
3095 apiurl = store_read_apiurl(wd)
3096 repository = args[0]
3099 if len(args) > 3 and len(args) < 6: # 4 or 5
3100 apiurl = conf.config['apiurl']
3103 repository = args[2]
3105 # for following specfile detection ...