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.no_keyring:
121 config['use_keyring'] = '0'
122 if self.options.no_gnome_keyring:
123 config['gnome_keyring'] = '0'
124 if self.options.apiurl:
125 config['apiurl'] = self.options.apiurl
127 conf.write_initial_config(e.file, config)
128 print >>sys.stderr, 'done'
129 if try_again: self.postoptparse(try_again = False)
130 except oscerr.ConfigMissingApiurl, e:
131 print >>sys.stderr, e.msg
133 user = raw_input('Username: ')
134 passwd = getpass.getpass()
135 conf.add_section(e.file, e.url, user, passwd)
136 if try_again: self.postoptparse(try_again = False)
138 self.options.verbose = conf.config['verbose']
139 self.download_progress = None
140 if conf.config.get('show_download_progress', False):
141 from meter import TextMeter
142 self.download_progress = TextMeter()
145 def get_cmd_help(self, cmdname):
146 doc = self._get_cmd_handler(cmdname).__doc__
147 doc = self._help_reindent(doc)
148 doc = self._help_preprocess(doc, cmdname)
149 doc = doc.rstrip() + '\n' # trim down trailing space
150 return self._str(doc)
153 # overridden from class Cmdln() to use config variables in help texts
154 def _help_preprocess(self, help, cmdname):
155 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
156 return help % conf.config
159 def do_init(self, subcmd, opts, project, package=None):
160 """${cmd_name}: Initialize a directory as working copy
162 Initialize an existing directory to be a working copy of an
163 (already existing) buildservice project/package.
165 (This is the same as checking out a package and then copying sources
166 into the directory. It does NOT create a new package. To create a
167 package, use 'osc meta pkg ... ...')
169 You wouldn't normally use this command.
171 To get a working copy of a package (e.g. for building it or working on
172 it, you would normally use the checkout command. Use "osc help
173 checkout" to get help for it.
182 init_project_dir(conf.config['apiurl'], os.curdir, project)
183 print 'Initializing %s (Project: %s)' % (os.curdir, project)
185 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
186 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
192 @cmdln.option('-a', '--arch', metavar='ARCH',
193 help='specify architecture (only for binaries)')
194 @cmdln.option('-r', '--repo', metavar='REPO',
195 help='specify repository (only for binaries)')
196 @cmdln.option('-b', '--binaries', action='store_true',
197 help='list built binaries instead of sources')
198 @cmdln.option('-R', '--revision', metavar='REVISION',
199 help='specify revision (only for sources)')
200 @cmdln.option('-e', '--expand', action='store_true',
201 help='expand linked package (only for sources)')
202 @cmdln.option('-v', '--verbose', action='store_true',
203 help='print extra information')
204 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
205 help='print extra information')
206 def do_list(self, subcmd, opts, *args):
207 """${cmd_name}: List sources or binaries on the server
209 Examples for listing sources:
210 ls # list all projects
211 ls PROJECT # list packages in a project
212 ls PROJECT PACKAGE # list source files of package of a project
213 ls PROJECT PACKAGE <file> # list <file> if this file exists
214 ls -v PROJECT PACKAGE # verbosely list source files of package
215 ls -l PROJECT PACKAGE # verbosely list source files of package
216 ll PROJECT PACKAGE # verbosely list source files of package
217 LL PROJECT PACKAGE # verbosely list source files of expanded link
219 With --verbose, the following fields will be shown for each item:
221 Revision number of the last commit
223 Date and time of the last commit
225 Examples for listing binaries:
226 ls -b PROJECT # list all binaries of a project
227 ls -b PROJECT -a ARCH # list ARCH binaries of a project
228 ls -b PROJECT -r REPO # list binaries in REPO
229 ls -b PROJECT PACKAGE REPO ARCH
232 ${cmd_name} [PROJECT [PACKAGE]]
233 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
237 apiurl = conf.config['apiurl']
238 args = slash_split(args)
241 if subcmd == 'lL' or subcmd == 'LL':
255 if opts.repo != args[2]:
256 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
263 if not opts.binaries:
264 raise oscerr.WrongArgs('Too many arguments')
266 if opts.arch != args[3]:
267 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
272 if opts.binaries and opts.expand:
273 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
277 # ls -b toplevel doesn't make sense, so use info from
278 # current dir if available
281 if is_project_dir(dir):
282 project = store_read_project(dir)
283 apiurl = store_read_apiurl(dir)
284 elif is_package_dir(dir):
285 project = store_read_project(dir)
286 package = store_read_package(dir)
287 apiurl = store_read_apiurl(dir)
290 raise oscerr.WrongArgs('There are no binaries to list above project level.')
292 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
296 if opts.repo and opts.arch:
297 repos.append(Repo(opts.repo, opts.arch))
298 elif opts.repo and not opts.arch:
299 for repo in get_repos_of_project(apiurl, project):
300 if repo.name == opts.repo:
302 elif opts.arch and not opts.repo:
303 for repo in get_repos_of_project(apiurl, project):
304 if repo.arch == opts.arch:
307 repos = get_repos_of_project(apiurl, project)
311 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
313 for result in results:
316 print '%s/%s' % (result[0].name, result[0].arch)
321 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
327 elif not opts.binaries:
329 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
333 if self.options.verbose:
334 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
336 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
338 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
340 elif len(args) == 2 or len(args) == 3:
341 l = meta_get_filelist(conf.config['apiurl'],
344 verbose=opts.verbose,
346 revision=opts.revision)
348 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
349 for i in l if not fname or fname == i.name ]
352 print 'file \'%s\' does not exist' % fname
360 print 'file \'%s\' does not exist' % fname
365 @cmdln.option('-f', '--force', action='store_true',
366 help='force generation of new patchinfo file')
367 @cmdln.option('--force-update', action='store_true',
368 help='drops away collected packages from an already built patch and let it collect again')
369 def do_patchinfo(self, subcmd, opts, *args):
370 """${cmd_name}: Generate and edit a patchinfo file.
372 A patchinfo file describes the packages for an update and the kind of
377 osc patchinfo PATCH_NAME
381 project_dir = localdir = os.getcwd()
382 if is_project_dir(localdir):
383 project = store_read_project(localdir)
384 apiurl = store_read_apiurl(localdir)
386 sys.exit('This command must be called in a checked out project.')
388 for p in meta_get_packagelist(apiurl, project):
389 if p.startswith("_patchinfo:"):
392 if opts.force or not patchinfo:
393 print "Creating initial patchinfo..."
394 query='cmd=createpatchinfo'
396 query += "&name=" + args[0]
397 url = makeurl(apiurl, ['source', project], query=query)
399 for p in meta_get_packagelist(apiurl, project):
400 if p.startswith("_patchinfo:"):
403 if not os.path.exists(project_dir + "/" + patchinfo):
404 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
406 if sys.platform[:3] != 'win':
407 editor = os.getenv('EDITOR', default='vim')
409 editor = os.getenv('EDITOR', default='notepad')
410 subprocess.call('%s %s' % (editor, project_dir + "/" + patchinfo + "/_patchinfo"), shell=True)
413 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
414 help='affect only a given attribute')
415 @cmdln.option('--attribute-defaults', action='store_true',
416 help='include defined attribute defaults')
417 @cmdln.option('--attribute-project', action='store_true',
418 help='include project values, if missing in packages ')
419 @cmdln.option('-F', '--file', metavar='FILE',
420 help='read metadata from FILE, instead of opening an editor. '
421 '\'-\' denotes standard input. ')
422 @cmdln.option('-e', '--edit', action='store_true',
423 help='edit metadata')
424 @cmdln.option('-c', '--create', action='store_true',
425 help='create attribute without values')
426 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
427 help='set attribute values')
428 @cmdln.option('--delete', action='store_true',
429 help='delete a pattern or attribute')
430 def do_meta(self, subcmd, opts, *args):
431 """${cmd_name}: Show meta information, or edit it
433 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
435 This command displays metadata on buildservice objects like projects,
436 packages, or users. The type of metadata is specified by the word after
437 "meta", like e.g. "meta prj".
439 prj denotes metadata of a buildservice project.
440 prjconf denotes the (build) configuration of a project.
441 pkg denotes metadata of a buildservice package.
442 user denotes the metadata of a user.
443 pattern denotes installation patterns defined for a project.
445 To list patterns, use 'osc meta pattern PRJ'. An additional argument
446 will be the pattern file to view or edit.
448 With the --edit switch, the metadata can be edited. Per default, osc
449 opens the program specified by the environmental variable EDITOR with a
450 temporary file. Alternatively, content to be saved can be supplied via
451 the --file switch. If the argument is '-', input is taken from stdin:
452 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
454 When trying to edit a non-existing resource, it is created implicitly.
460 osc meta pkg PRJ PKG -e
461 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
464 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
465 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
466 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
467 osc meta pattern --delete PRJ PATTERN
471 args = slash_split(args)
473 if not args or args[0] not in metatypes.keys():
474 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
475 % ', '.join(metatypes))
481 min_args, max_args = 2, 2
482 elif cmd in ['pattern']:
483 min_args, max_args = 1, 2
484 elif cmd in ['attribute']:
485 min_args, max_args = 1, 3
487 min_args, max_args = 1, 1
488 if len(args) < min_args:
489 raise oscerr.WrongArgs('Too few arguments.')
490 if len(args) > max_args:
491 raise oscerr.WrongArgs('Too many arguments.')
498 project, package = args[0:2]
499 elif cmd == 'attribute':
505 if opts.attribute_project:
506 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
511 attributepath.append('source')
512 attributepath.append(project)
514 attributepath.append(package)
516 attributepath.append(subpackage)
517 attributepath.append('_attribute')
518 elif cmd == 'prjconf':
522 elif cmd == 'pattern':
528 # enforce pattern argument if needed
529 if opts.edit or opts.file:
530 raise oscerr.WrongArgs('A pattern file argument is required.')
533 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
535 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
537 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
538 elif cmd == 'attribute':
539 sys.stdout.write(''.join(show_attribute_meta(conf.config['apiurl'], project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
540 elif cmd == 'prjconf':
541 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
543 r = get_user_meta(conf.config['apiurl'], user)
545 sys.stdout.write(''.join(r))
546 elif cmd == 'pattern':
548 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
550 sys.stdout.write(''.join(r))
552 r = show_pattern_metalist(conf.config['apiurl'], project)
554 sys.stdout.write('\n'.join(r) + '\n')
557 if opts.edit and not opts.file:
559 edit_meta(metatype='prj',
561 path_args=quote_plus(project),
564 'user': conf.config['user']}))
566 edit_meta(metatype='pkg',
568 path_args=(quote_plus(project), quote_plus(package)),
571 'user': conf.config['user']}))
572 elif cmd == 'prjconf':
573 edit_meta(metatype='prjconf',
575 path_args=quote_plus(project),
578 edit_meta(metatype='user',
580 path_args=(quote_plus(user)),
581 template_args=({'user': user}))
582 elif cmd == 'pattern':
583 edit_meta(metatype='pattern',
585 path_args=(project, pattern),
588 # create attribute entry
589 if (opts.create or opts.set) and cmd == 'attribute':
590 if not opts.attribute:
591 raise oscerr.WrongOptions('no attribute given to create')
594 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
595 for i in opts.set.split(','):
596 values += '<value>%s</value>' % i
597 aname = opts.attribute.split(":")
598 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
599 url = makeurl(conf.config['apiurl'], attributepath)
600 for data in streamfile(url, http_POST, data=d):
601 sys.stdout.write(data)
610 f = open(opts.file).read()
612 sys.exit('could not open file \'%s\'.' % opts.file)
615 edit_meta(metatype='prj',
618 path_args=quote_plus(project))
620 edit_meta(metatype='pkg',
623 path_args=(quote_plus(project), quote_plus(package)))
624 elif cmd == 'prjconf':
625 edit_meta(metatype='prjconf',
628 path_args=quote_plus(project))
630 edit_meta(metatype='user',
633 path_args=(quote_plus(user)))
634 elif cmd == 'pattern':
635 edit_meta(metatype='pattern',
638 path_args=(project, pattern))
643 path = metatypes[cmd]['path']
645 path = path % (project, pattern)
646 u = makeurl(conf.config['apiurl'], [path])
648 elif cmd == 'attribute':
649 if not opts.attribute:
650 raise oscerr.WrongOptions('no attribute given to create')
651 attributepath.append(opts.attribute)
652 u = makeurl(conf.config['apiurl'], attributepath)
653 for data in streamfile(u, http_DELETE):
654 sys.stdout.write(data)
656 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
659 @cmdln.option('-m', '--message', metavar='TEXT',
660 help='specify message TEXT')
661 @cmdln.option('-r', '--revision', metavar='REV',
662 help='for "create", specify a certain source revision ID (the md5 sum)')
663 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
664 help='Superseding another request by this one')
665 @cmdln.option('--nodevelproject', action='store_true',
666 help='do not follow a defined devel project ' \
667 '(primary project where a package is developed)')
668 @cmdln.option('--cleanup', action='store_true',
669 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
670 @cmdln.option('--no-cleanup', action='store_true',
671 help='never remove source package on accept, but update its content')
672 @cmdln.option('--no-update', action='store_true',
673 help='never touch source package on accept (will break source links)')
674 @cmdln.option('-d', '--diff', action='store_true',
675 help='show diff only instead of creating the actual request')
676 @cmdln.option('--yes', action='store_true',
677 help='proceed without asking.')
679 @cmdln.alias("submitreq")
680 @cmdln.alias("submitpac")
681 def do_submitrequest(self, subcmd, opts, *args):
682 """${cmd_name}: Create request to submit source into another Project
684 [See http://en.opensuse.org/Build_Service/Collaboration for information
687 See the "request" command for showing and modifing existing requests.
690 osc submitreq [OPTIONS]
691 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
692 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
696 src_update = conf.config['submitrequest_on_accept_action'] or None
697 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
699 src_update = "cleanup"
700 elif opts.no_cleanup:
701 src_update = "update"
703 src_update = "noupdate"
705 args = slash_split(args)
707 # remove this block later again
708 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
709 if args and args[0] in oldcmds:
710 print "************************************************************************"
711 print "* WARNING: It looks that you are using this command with a *"
712 print "* deprecated syntax. *"
713 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
714 print "* to see the new syntax. *"
715 print "************************************************************************"
716 if args[0] == 'create':
722 raise oscerr.WrongArgs('Too many arguments.')
724 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
725 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
727 apiurl = conf.config['apiurl']
729 if len(args) == 0 and is_project_dir(os.getcwd()):
731 # submit requests for multiple packages are currently handled via multiple requests
732 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
733 project = store_read_project(os.curdir)
734 apiurl = store_read_apiurl(os.curdir)
740 # loop via all packages for checking their state
741 for p in meta_get_packagelist(apiurl, project):
742 if p.startswith("_patchinfo:"):
745 # get _link info from server, who knows about the local state ...
746 u = makeurl(apiurl, ['source', project, p])
748 root = ET.parse(f).getroot()
749 linkinfo = root.find('linkinfo')
751 print "Package ", p, " is not a source link."
752 sys.exit("This is currently not supported.")
753 if linkinfo.get('error'):
754 print "Package ", p, " is a broken source link."
755 sys.exit("Please fix this first")
756 t = linkinfo.get('project')
758 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
759 # Real fix is to ask the api if sources are modificated
760 # but there is no such call yet.
761 targetprojects.append(t)
763 print "Submitting package ", p
765 print " Skipping package ", p
767 print "Skipping package ", p, " since it is a source link pointing inside to the project."
771 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
772 print "\nEverything fine? Can we create the requests ? [y/n]"
773 if sys.stdin.read(1) != "y":
774 sys.exit("Aborted...")
776 # loop via all packages to do the action
778 result = create_submit_request(apiurl, project, p)
781 sys.exit("submit request creation failed")
782 sr_ids.append(result)
784 # create submit requests for all found patchinfos
788 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
791 for t in targetprojects:
792 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
793 (project, p, t, p, options_block)
797 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
798 (actionxml, cgi.escape(opts.message or ""))
799 u = makeurl(apiurl, ['request'], query='cmd=create')
800 f = http_POST(u, data=xml)
802 root = ET.parse(f).getroot()
803 sr_ids.append(root.get('id'))
805 print "Requests created: ",
808 sys.exit('Successfull finished')
811 # try using the working copy at hand
812 p = findpacs(os.curdir)[0]
813 src_project = p.prjname
816 if len(args) == 0 and p.islink():
817 dst_project = p.linkinfo.project
818 dst_package = p.linkinfo.package
820 dst_project = args[0]
822 dst_package = args[1]
824 dst_package = src_package
826 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
827 'Please provide it the target via commandline arguments.' % p.name)
829 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
830 if len(modified) > 0:
831 print 'Your working copy has local modifications.'
832 repl = raw_input('Proceed without committing the local changes? (y|N) ')
836 # get the arguments from the commandline
837 src_project, src_package, dst_project = args[0:3]
839 dst_package = args[3]
841 dst_package = src_package
843 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
844 + self.get_cmd_help('request'))
846 if not opts.nodevelproject:
849 devloc = show_develproject(apiurl, dst_project, dst_package)
850 except urllib2.HTTPError:
851 print >>sys.stderr, """\
852 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
853 % (dst_project, dst_package)
857 dst_project != devloc and \
858 src_project != devloc:
860 A different project, %s, is defined as the place where development
861 of the package %s primarily takes place.
862 Please submit there instead, or use --nodevelproject to force direct submission.""" \
863 % (devloc, dst_package)
868 if opts.diff or not opts.message:
870 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
871 rdiff += server_diff(apiurl,
872 dst_project, dst_package, opts.revision,
873 src_project, src_package, None, True)
879 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
880 user = conf.get_apiurl_usr(apiurl)
881 myreqs = [ i for i in reqs if i.state.who == user ]
884 print 'You already created the following submit request: %s.' % \
885 ', '.join([str(i.reqid) for i in myreqs ])
886 repl = raw_input('Supersede the old requests? (y/n/c) ')
887 if repl.lower() == 'c':
888 print >>sys.stderr, 'Aborting'
894 changes_re = re.compile(r'^--- .*\.changes ')
895 for line in rdiff.split('\n'):
896 if line.startswith('--- '):
897 if changes_re.match(line):
902 difflines.append(line)
903 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
905 result = create_submit_request(apiurl,
906 src_project, src_package,
907 dst_project, dst_package,
908 opts.message, orev=opts.revision, src_update=src_update)
909 if repl.lower() == 'y':
911 change_request_state(apiurl, str(req.reqid), 'superseded',
912 'superseded by %s' % result, result)
915 r = change_request_state(conf.config['apiurl'],
916 opts.supersede, 'superseded', opts.message or '', result)
918 print 'created request id', result
921 @cmdln.option('-m', '--message', metavar='TEXT',
922 help='specify message TEXT')
924 @cmdln.alias("deletereq")
925 def do_deleterequest(self, subcmd, opts, *args):
926 """${cmd_name}: Create request to delete a package or project
930 osc deletereq [-m TEXT] PROJECT [PACKAGE]
934 args = slash_split(args)
937 raise oscerr.WrongArgs('Please specify at least a project.')
939 raise oscerr.WrongArgs('Too many arguments.')
941 apiurl = conf.config['apiurl']
949 opts.message = edit_message()
951 result = create_delete_request(apiurl, project, package, opts.message)
955 @cmdln.option('-m', '--message', metavar='TEXT',
956 help='specify message TEXT')
958 @cmdln.alias("changedevelreq")
959 def do_changedevelrequest(self, subcmd, opts, *args):
960 """${cmd_name}: Create request to change the devel package definition.
962 [See http://en.opensuse.org/Build_Service/Collaboration for information
965 See the "request" command for showing and modifing existing requests.
967 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
971 raise oscerr.WrongArgs('Too many arguments.')
973 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
975 devel_project = store_read_project(wd)
976 devel_package = package = store_read_package(wd)
977 apiurl = store_read_apiurl(wd)
978 project = conf.config['getpac_default_project']
981 raise oscerr.WrongArgs('Too few arguments.')
983 apiurl = conf.config['apiurl']
985 devel_project = args[2]
988 devel_package = package
990 devel_package = args[3]
994 footer=textwrap.TextWrapper(width = 66).fill(
995 'please explain why you like to change the devel project of %s/%s to %s/%s'
996 % (project,package,devel_project,devel_package))
997 opts.message = edit_message(footer)
999 result = create_change_devel_request(apiurl,
1000 devel_project, devel_package,
1006 @cmdln.option('-d', '--diff', action='store_true',
1007 help='generate a diff')
1008 @cmdln.option('-u', '--unified', action='store_true',
1009 help='output the diff in the unified diff format')
1010 @cmdln.option('-m', '--message', metavar='TEXT',
1011 help='specify message TEXT')
1012 @cmdln.option('-t', '--type', metavar='TYPE',
1013 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1014 @cmdln.option('-a', '--all', action='store_true',
1015 help='all states. Same as\'-s all\'')
1016 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1017 help='only list requests in one of the comma separated given states (new/accepted/revoked/declined) or "all" [default=new, or all, if no args given]')
1018 @cmdln.option('-D', '--days', metavar='DAYS',
1019 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1020 @cmdln.option('-U', '--user', metavar='USER',
1021 help='same as -M, but for the specified USER')
1022 @cmdln.option('-b', '--brief', action='store_true', default=False,
1023 help='print output in list view as list subcommand')
1024 @cmdln.option('-M', '--mine', action='store_true',
1025 help='only show requests created by yourself')
1026 @cmdln.option('-B', '--bugowner', action='store_true',
1027 help='also show requests about packages where I am bugowner')
1028 @cmdln.option('-i', '--interactive', action='store_true',
1029 help='interactive review of request')
1031 @cmdln.alias("review")
1032 def do_request(self, subcmd, opts, *args):
1033 """${cmd_name}: Show and modify requests
1035 [See http://en.opensuse.org/Build_Service/Collaboration for information
1038 This command shows and modifies existing requests. To create new requests
1039 you need to call one of the following:
1042 osc changedevelrequest
1043 To send low level requests to the buildservice API, use:
1046 This command has the following sub commands:
1048 "list" lists open requests attached to a project or package or person.
1049 Uses the project/package of the current directory if none of
1050 -M, -U USER, project/package are given.
1052 "log" will show the history of the given ID
1054 "show" will show the request itself, and generate a diff for review, if
1055 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1057 "decline" will change the request state to "declined" and append a
1058 message that you specify with the --message option.
1060 "wipe" will permanently delete a request.
1062 "revoke" will set the request state to "revoked" and append a
1063 message that you specify with the --message option.
1065 "accept" will change the request state to "accepted" and will trigger
1066 the actual submit process. That would normally be a server-side copy of
1067 the source package to the target package.
1069 "checkout" will checkout the request's source package. This only works for "submit" requests.
1072 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1074 osc request [show] [-d] [-b] ID
1075 osc request accept [-m TEXT] ID
1076 osc request decline [-m TEXT] ID
1077 osc request revoke [-m TEXT] ID
1079 osc request checkout/co ID
1080 osc review accept [-m TEXT] ID
1081 osc review decline [-m TEXT] ID
1085 args = slash_split(args)
1087 if opts.all and opts.state:
1088 raise oscerr.WrongOptions('Sorry, the options --all and --state ' \
1089 'are mutually exclusive.')
1090 if opts.mine and opts.user:
1091 raise oscerr.WrongOptions('Sorry, the options --user and --mine ' \
1092 'are mutually exclusive.')
1097 if opts.state == '':
1100 if opts.state == '':
1103 cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co', 'help']
1104 if not args or args[0] not in cmds:
1105 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1106 % (args[0],', '.join(cmds)))
1112 return self.do_help(['help', 'request'])
1115 min_args, max_args = 1, 1
1116 elif cmd in ['list']:
1117 min_args, max_args = 0, 2
1119 min_args, max_args = 1, 1
1120 if len(args) < min_args:
1121 raise oscerr.WrongArgs('Too few arguments.')
1122 if len(args) > max_args:
1123 raise oscerr.WrongArgs('Too many arguments.')
1125 apiurl = conf.config['apiurl']
1132 elif not opts.mine and not opts.user:
1134 project = store_read_project(os.curdir)
1135 apiurl = store_read_apiurl(os.curdir)
1136 package = store_read_package(os.curdir)
1137 except oscerr.NoWorkingCopy:
1142 elif cmd in ['log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1147 states = ('new', 'accepted', 'revoked', 'declined')
1148 state_list = opts.state.split(',')
1149 if opts.state == 'all':
1150 state_list = ['all']
1152 for s in state_list:
1154 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1157 who = conf.get_apiurl_usr(apiurl)
1161 state_list = ['all']
1163 ## FIXME -B not implemented!
1165 if (self.options.debug):
1166 print 'list: option --bugowner ignored: not impl.'
1168 results = get_request_list(apiurl,
1169 project, package, who, state_list, opts.type)
1170 results.sort(reverse=True)
1172 days = opts.days or conf.config['request_list_days']
1179 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1182 ## bs has received 2009-09-20 a new xquery compare() function
1183 ## which allows us to limit the list inside of get_request_list
1184 ## That would be much faster for coolo. But counting the remainder
1185 ## would not be possible with current xquery implementation.
1186 ## Workaround: fetch all, and filter on client side.
1188 ## FIXME: date filtering should become implemented on server side
1189 for result in results:
1190 if days == 0 or result.state.when > since or result.state.name == 'new':
1191 print result.list_view()
1195 print "There are %d requests older than %s days.\n" % (skipped, days)
1198 for l in get_request_log(conf.config['apiurl'], reqid):
1203 r = get_request(conf.config['apiurl'], reqid)
1206 elif opts.interactive or conf.config['request_show_interactive']:
1207 return request_interactive_review(conf.config['apiurl'], r)
1210 # fixme: will inevitably fail if the given target doesn't exist
1213 print server_diff(conf.config['apiurl'],
1214 r.actions[0].dst_project, r.actions[0].dst_package, None,
1215 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified)
1216 except urllib2.HTTPError, e:
1217 e.osc_msg = 'Diff not possible'
1221 elif cmd == 'checkout' or cmd == 'co':
1222 r = get_request(conf.config['apiurl'], reqid)
1223 submits = [ i for i in r.actions if i.type == 'submit' ]
1224 if not len(submits):
1225 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1226 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1227 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1230 if not opts.message:
1231 opts.message = edit_message()
1232 state_map = {'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1233 # Change review state only
1234 if subcmd == 'review':
1235 if cmd in ['accept', 'decline']:
1236 r = change_review_state(conf.config['apiurl'],
1237 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1239 # Change state of entire request
1240 elif cmd in ['accept', 'decline', 'wipe', 'revoke']:
1241 r = change_request_state(conf.config['apiurl'],
1242 reqid, state_map[cmd], opts.message or '')
1245 # editmeta and its aliases are all depracated
1246 @cmdln.alias("editprj")
1247 @cmdln.alias("createprj")
1248 @cmdln.alias("editpac")
1249 @cmdln.alias("createpac")
1250 @cmdln.alias("edituser")
1251 @cmdln.alias("usermeta")
1253 def do_editmeta(self, subcmd, opts, *args):
1256 Obsolete command to edit metadata. Use 'meta' now.
1258 See the help output of 'meta'.
1262 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1263 print >>sys.stderr, 'See \'osc help meta\'.'
1264 #self.do_help([None, 'meta'])
1268 @cmdln.option('-r', '--revision', metavar='rev',
1269 help='use the specified revision.')
1270 @cmdln.option('-u', '--unset', action='store_true',
1271 help='remove revision in link, it will point always to latest revision')
1272 def do_setlinkrev(self, subcmd, opts, *args):
1273 """${cmd_name}: Updates a revision number in a source link.
1275 This command adds or updates a specified revision number in a source link.
1276 The current revision of the source is used, if no revision number is specified.
1280 osc setlinkrev PROJECT [PACKAGE]
1284 args = slash_split(args)
1285 apiurl = conf.config['apiurl']
1288 p = findpacs(os.curdir)[0]
1293 sys.exit('Local directory is no checked out source link package, aborting')
1294 elif len(args) == 2:
1297 elif len(args) == 1:
1300 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1301 + self.get_cmd_help('setlinkrev'))
1304 packages = [ package ]
1306 packages = meta_get_packagelist(apiurl, project)
1309 print "setting revision for package", p
1313 rev, dummy = parseRevisionOption(opts.revision)
1314 set_link_rev(apiurl, project, p, rev)
1317 def do_linktobranch(self, subcmd, opts, *args):
1318 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1320 This command tells the server to convert a _link with or without a project.diff
1321 to a branch. This is a full copy with a _link file pointing to the branched place.
1324 osc linktobranch # can be used in checked out package
1325 osc linktobranch PROJECT PACKAGE
1329 args = slash_split(args)
1332 project = store_read_project(wd)
1333 package = store_read_package(wd)
1334 apiurl = store_read_apiurl(wd)
1335 update_local_dir = True
1337 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1339 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1341 apiurl = conf.config['apiurl']
1344 update_local_dir = False
1347 link_to_branch(apiurl, project, package)
1348 if update_local_dir:
1350 pac.update(rev=pac.latest_rev())
1353 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1354 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1355 @cmdln.option('-c', '--current', action='store_true',
1356 help='link fixed against current revision.')
1357 @cmdln.option('-r', '--revision', metavar='rev',
1358 help='link the specified revision.')
1359 @cmdln.option('-f', '--force', action='store_true',
1360 help='overwrite an existing link file if it is there.')
1361 @cmdln.option('-d', '--disable-publish', action='store_true',
1362 help='disable publishing of the linked package')
1363 def do_linkpac(self, subcmd, opts, *args):
1364 """${cmd_name}: "Link" a package to another package
1366 A linked package is a clone of another package, but plus local
1367 modifications. It can be cross-project.
1369 The DESTPAC name is optional; the source packages' name will be used if
1372 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1374 To add a patch, add the patch as file and add it to the _link file.
1375 You can also specify text which will be inserted at the top of the spec file.
1377 See the examples in the _link file.
1380 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1384 args = slash_split(args)
1386 if not args or len(args) < 3:
1387 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1388 + self.get_cmd_help('linkpac'))
1390 rev, dummy = parseRevisionOption(opts.revision)
1392 src_project = args[0]
1393 src_package = args[1]
1394 dst_project = args[2]
1396 dst_package = args[3]
1398 dst_package = src_package
1400 if src_project == dst_project and src_package == dst_package:
1401 raise oscerr.WrongArgs('Error: source and destination are the same.')
1403 if src_project == dst_project and not opts.cicount:
1404 # in this case, the user usually wants to build different spec
1405 # files from the same source
1406 opts.cicount = "copy"
1409 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1411 if rev and not checkRevision(src_project, src_package, rev):
1412 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1415 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1417 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1418 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1419 @cmdln.option('-d', '--disable-publish', action='store_true',
1420 help='disable publishing of the aggregated package')
1421 def do_aggregatepac(self, subcmd, opts, *args):
1422 """${cmd_name}: "Aggregate" a package to another package
1424 Aggregation of a package means that the build results (binaries) of a
1425 package are basically copied into another project.
1426 This can be used to make packages available from building that are
1427 needed in a project but available only in a different project. Note
1428 that this is done at the expense of disk space. See
1429 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1430 for more information.
1432 The DESTPAC name is optional; the source packages' name will be used if
1436 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1440 args = slash_split(args)
1442 if not args or len(args) < 3:
1443 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1444 + self.get_cmd_help('aggregatepac'))
1446 src_project = args[0]
1447 src_package = args[1]
1448 dst_project = args[2]
1450 dst_package = args[3]
1452 dst_package = src_package
1454 if src_project == dst_project and src_package == dst_package:
1455 raise oscerr.WrongArgs('Error: source and destination are the same.')
1459 for pair in opts.map_repo.split(','):
1460 src_tgt = pair.split('=')
1461 if len(src_tgt) != 2:
1462 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1463 repo_map[src_tgt[0]] = src_tgt[1]
1465 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1468 @cmdln.option('-c', '--client-side-copy', action='store_true',
1469 help='do a (slower) client-side copy')
1470 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1471 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1472 @cmdln.option('-d', '--keep-develproject', action='store_true',
1473 help='keep develproject tag in the package metadata')
1474 @cmdln.option('-r', '--revision', metavar='rev',
1475 help='link the specified revision.')
1476 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1477 help='URL of destination api server. Default is the source api server.')
1478 @cmdln.option('-m', '--message', metavar='TEXT',
1479 help='specify message TEXT')
1480 @cmdln.option('-e', '--expand', action='store_true',
1481 help='if the source package is a link then copy the expanded version of the link')
1482 def do_copypac(self, subcmd, opts, *args):
1483 """${cmd_name}: Copy a package
1485 A way to copy package to somewhere else.
1487 It can be done across buildservice instances, if the -t option is used.
1488 In that case, a client-side copy is implied.
1490 Using --client-side-copy always involves downloading all files, and
1491 uploading them to the target.
1493 The DESTPAC name is optional; the source packages' name will be used if
1497 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1501 args = slash_split(args)
1503 if not args or len(args) < 3:
1504 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1505 + self.get_cmd_help('copypac'))
1507 src_project = args[0]
1508 src_package = args[1]
1509 dst_project = args[2]
1511 dst_package = args[3]
1513 dst_package = src_package
1515 src_apiurl = conf.config['apiurl']
1517 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1519 dst_apiurl = src_apiurl
1521 if src_project == dst_project and \
1522 src_package == dst_package and \
1523 src_apiurl == dst_apiurl:
1524 raise oscerr.WrongArgs('Source and destination are the same.')
1526 if src_apiurl != dst_apiurl:
1527 opts.client_side_copy = True
1529 rev, dummy = parseRevisionOption(opts.revision)
1532 comment = opts.message
1535 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1536 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1538 r = copy_pac(src_apiurl, src_project, src_package,
1539 dst_apiurl, dst_project, dst_package,
1540 client_side_copy=opts.client_side_copy,
1541 keep_maintainers=opts.keep_maintainers,
1542 keep_develproject=opts.keep_develproject,
1549 @cmdln.option('-c', '--checkout', action='store_true',
1550 help='Checkout branched package afterwards ' \
1551 '(\'osc bco\' is a shorthand for this option)' )
1552 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
1553 help='Use this attribute to find affected packages (default is OBS:Maintained)')
1554 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
1555 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
1556 def do_mbranch(self, subcmd, opts, *args):
1557 """${cmd_name}: Multiple branch of a package
1559 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
1562 This command is used for creating multiple links of defined version of a package
1563 in one project. This is esp. used for maintenance updates.
1565 The branched package will live in
1566 home:USERNAME:branches:ATTRIBUTE:PACKAGE
1567 if nothing else specified.
1570 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
1573 args = slash_split(args)
1576 maintained_attribute = conf.config['maintained_attribute']
1577 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
1579 if not len(args) or len(args) > 2:
1580 raise oscerr.WrongArgs('Wrong number of arguments.')
1586 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
1590 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
1593 print "Project " + r + " created."
1596 init_project_dir(conf.config['apiurl'], r, r)
1597 print statfrmt('A', r)
1600 for package in meta_get_packagelist(conf.config['apiurl'], r):
1602 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
1604 print >>sys.stderr, 'Error while checkout package:\n', package
1606 if conf.config['verbose']:
1607 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1610 @cmdln.alias('branchco')
1612 @cmdln.alias('getpac')
1613 @cmdln.option('--nodevelproject', action='store_true',
1614 help='do not follow a defined devel project ' \
1615 '(primary project where a package is developed)')
1616 @cmdln.option('-c', '--checkout', action='store_true',
1617 help='Checkout branched package afterwards ' \
1618 '(\'osc bco\' is a shorthand for this option)' )
1619 @cmdln.option('-r', '--revision', metavar='rev',
1620 help='branch against a specific revision')
1621 def do_branch(self, subcmd, opts, *args):
1622 """${cmd_name}: Branch a package
1624 [See http://en.opensuse.org/Build_Service/Collaboration for information
1627 Create a source link from a package of an existing project to a new
1628 subproject of the requesters home project (home:branches:)
1630 The branched package will live in
1631 home:USERNAME:branches:PROJECT/PACKAGE
1632 if nothing else specified.
1634 With getpac or bco, the branched package will come from
1635 %(getpac_default_project)s
1636 if nothing else specified.
1639 osc branch SOURCEPROJECT SOURCEPACKAGE
1640 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
1641 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
1642 osc getpac SOURCEPACKAGE
1647 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
1648 args = slash_split(args)
1649 tproject = tpackage = None
1651 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
1652 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
1653 # python has no args.unshift ???
1654 args = [ conf.config['getpac_default_project'] , args[0] ]
1656 if len(args) < 2 or len(args) > 4:
1657 raise oscerr.WrongArgs('Wrong number of arguments.')
1658 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
1660 expected = tproject = args[2]
1664 exists, targetprj, targetpkg, srcprj, srcpkg = \
1665 branch_pkg(conf.config['apiurl'], args[0], args[1],
1666 nodevelproject=opts.nodevelproject, rev=opts.revision,
1667 target_project=tproject, target_package=tpackage,
1668 return_existing=opts.checkout)
1670 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
1673 if not exists and (srcprj is not None and srcprj != args[0] or \
1674 srcprj is None and targetprj != expected):
1675 devloc = srcprj or targetprj
1676 if not srcprj and 'branches:' in targetprj:
1677 devloc = targetprj.split('branches:')[1]
1678 print '\nNote: The branch has been created of a different project,\n' \
1680 ' which is the primary location of where development for\n' \
1681 ' that package takes place.\n' \
1682 ' That\'s also where you would normally make changes against.\n' \
1683 ' A direct branch of the specified package can be forced\n' \
1684 ' with the --nodevelproject option.\n' % devloc
1686 package = tpackage or args[1]
1688 checkout_package(conf.config['apiurl'], targetprj, package,
1689 expand_link=True, prj_dir=targetprj)
1690 if conf.config['verbose']:
1691 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1694 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
1695 apiopt = '-A %s ' % conf.config['apiurl']
1696 print 'A working copy of the branched package can be checked out with:\n\n' \
1698 % (apiopt, targetprj, package)
1699 print_request_list(conf.config['apiurl'], args[0], args[1])
1701 print_request_list(conf.config['apiurl'], devloc, args[1])
1705 @cmdln.option('-f', '--force', action='store_true',
1706 help='deletes a package or an empty project')
1707 def do_rdelete(self, subcmd, opts, *args):
1708 """${cmd_name}: Delete a project or packages on the server.
1710 As a safety measure, project must be empty (i.e., you need to delete all
1711 packages first). If you are sure that you want to remove this project and all
1712 its packages use \'--force\' switch.
1715 osc rdelete -f PROJECT
1716 osc rdelete PROJECT PACKAGE [PACKAGE ...]
1721 args = slash_split(args)
1723 raise oscerr.WrongArgs('Missing argument.')
1729 # careful: if pkg is an empty string, the package delete request results
1730 # into a project delete request - which works recursively...
1732 delete_package(conf.config['apiurl'], prj, pkg)
1733 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
1734 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
1735 'If you are sure that you want to remove this project and all its ' \
1736 'packages use the \'--force\' switch'
1739 delete_project(conf.config['apiurl'], prj)
1742 def do_deletepac(self, subcmd, opts, *args):
1743 print """${cmd_name} is obsolete !
1746 osc delete for checked out packages or projects
1748 osc rdelete for server side operations."""
1753 @cmdln.option('-f', '--force', action='store_true',
1754 help='deletes a project and its packages')
1755 def do_deleteprj(self, subcmd, opts, project):
1756 """${cmd_name} is obsolete !
1763 @cmdln.alias('metafromspec')
1764 @cmdln.option('', '--specfile', metavar='FILE',
1765 help='Path to specfile. (if you pass more than working copy this option is ignored)')
1766 def do_updatepacmetafromspec(self, subcmd, opts, *args):
1767 """${cmd_name}: Update package meta information from a specfile
1769 ARG, if specified, is a package working copy.
1775 args = parseargs(args)
1776 if opts.specfile and len(args) == 1:
1777 specfile = opts.specfile
1780 pacs = findpacs(args)
1782 p.read_meta_from_spec(specfile)
1783 p.update_package_meta()
1787 @cmdln.option('-c', '--change', metavar='rev',
1788 help='the change made by revision rev (like -r rev-1:rev).'
1789 'If rev is negative this is like -r rev:rev-1.')
1790 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
1791 help='If rev1 is specified it will compare your working copy against '
1792 'the revision (rev1) on the server. '
1793 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
1794 '(NOTE: changes in your working copy are ignored in this case)')
1795 @cmdln.option('-p', '--plain', action='store_true',
1796 help='output the diff in plain (not unified) diff format')
1797 def do_diff(self, subcmd, opts, *args):
1798 """${cmd_name}: Generates a diff
1800 Generates a diff, comparing local changes against the repository
1803 ARG, specified, is a filename to include in the diff.
1809 args = parseargs(args)
1810 pacs = findpacs(args)
1814 rev = int(opts.change)
1824 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1827 rev1, rev2 = parseRevisionOption(opts.revision)
1831 diff += ''.join(make_diff(pac, rev1))
1833 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
1834 pac.prjname, pac.name, rev2, not opts.plain)
1839 @cmdln.option('--oldprj', metavar='OLDPRJ',
1840 help='project to compare against'
1841 ' (deprecated, use 3 argument form)')
1842 @cmdln.option('--oldpkg', metavar='OLDPKG',
1843 help='package to compare against'
1844 ' (deprecated, use 3 argument form)')
1845 @cmdln.option('-r', '--revision', metavar='N[:M]',
1846 help='revision id, where N = old revision and M = new revision')
1847 @cmdln.option('-p', '--plain', action='store_true',
1848 help='output the diff in plain (not unified) diff format')
1849 @cmdln.option('-c', '--change', metavar='rev',
1850 help='the change made by revision rev (like -r rev-1:rev). '
1851 'If rev is negative this is like -r rev:rev-1.')
1852 def do_rdiff(self, subcmd, opts, *args):
1853 """${cmd_name}: Server-side "pretty" diff of two packages
1855 Compares two packages (three or four arguments) or shows the
1856 changes of a specified revision of a package (two arguments)
1858 If no revision is specified the latest revision is used.
1860 Note that this command doesn't return a normal diff (which could be
1861 applied as patch), but a "pretty" diff, which also compares the content
1866 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
1867 osc ${cmd_name} PROJECT PACKAGE
1871 args = slash_split(args)
1882 new_project = args[0]
1883 new_package = args[1]
1885 old_project = opts.oldprj
1887 old_package = opts.oldpkg
1888 elif len(args) == 3 or len(args) == 4:
1889 if opts.oldprj or opts.oldpkg:
1890 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
1891 old_project = args[0]
1892 new_package = old_package = args[1]
1893 new_project = args[2]
1895 new_package = args[3]
1897 raise oscerr.WrongArgs('Wrong number of arguments')
1902 rev = int(opts.change)
1912 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1916 rev1, rev2 = parseRevisionOption(opts.revision)
1918 rdiff = server_diff(conf.config['apiurl'],
1919 old_project, old_package, rev1,
1920 new_project, new_package, rev2, not opts.plain)
1926 def do_install(self, subcmd, opts, *args):
1927 """${cmd_name}: install a package after build via zypper in -r
1929 Not implemented yet. Use osc repourls,
1930 select the url you best like (standard),
1931 chop off after the last /, this should work with zypper.
1938 args = slash_split(args)
1939 args = expand_proj_pack(args)
1942 ## if there is only one argument, and it ends in .ymp
1943 ## then fetch it, Parse XML to get the first
1944 ## metapackage.group.repositories.repository.url
1945 ## and construct zypper cmd's for all
1946 ## metapackage.group.software.item.name
1948 ## if args[0] is already an url, the use it as is.
1950 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])
1951 print self.do_install.__doc__
1952 print "Example: \n" + cmd
1955 def do_repourls(self, subcmd, opts, *args):
1956 """${cmd_name}: Shows URLs of .repo files
1958 Shows URLs on which to access the project .repos files (yum-style
1959 metadata) on download.opensuse.org.
1962 osc repourls [PROJECT]
1967 apiurl = conf.config['apiurl']
1971 elif len(args) == 0:
1972 project = store_read_project('.')
1973 apiurl = store_read_apiurl('.')
1975 raise oscerr.WrongArgs('Wrong number of arguments')
1977 # XXX: API should somehow tell that
1978 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1979 repos = get_repositories_of_project(apiurl, project)
1981 print url_tmpl % (project.replace(':', ':/'), repo, project)
1984 @cmdln.option('-r', '--revision', metavar='rev',
1985 help='checkout the specified revision. '
1986 'NOTE: if you checkout the complete project '
1987 'this option is ignored!')
1988 @cmdln.option('-e', '--expand-link', action='store_true',
1989 help='if a package is a link, check out the expanded '
1990 'sources (no-op, since this became the default)')
1991 @cmdln.option('-u', '--unexpand-link', action='store_true',
1992 help='if a package is a link, check out the _link file ' \
1993 'instead of the expanded sources')
1994 @cmdln.option('-c', '--current-dir', action='store_true',
1995 help='place PACKAGE folder in the current directory' \
1996 'instead of a PROJECT/PACKAGE directory')
1997 @cmdln.option('-s', '--source-service-files', action='store_true',
1998 help='server side generated files of source services' \
1999 'gets downloaded as well' )
2001 def do_checkout(self, subcmd, opts, *args):
2002 """${cmd_name}: Check out content from the repository
2004 Check out content from the repository server, creating a local working
2007 When checking out a single package, the option --revision can be used
2008 to specify a revision of the package to be checked out.
2010 When a package is a source link, then it will be checked out in
2011 expanded form. If --unexpand-link option is used, the checkout will
2012 instead produce the raw _link file plus patches.
2015 osc co PROJECT [PACKAGE] [FILE]
2016 osc co PROJECT # entire project
2017 osc co PROJECT PACKAGE # a package
2018 osc co PROJECT PACKAGE FILE # single file -> to current dir
2020 while inside a project directory:
2021 osc co PACKAGE # check out PACKAGE from project
2026 if opts.unexpand_link:
2030 if opts.source_service_files:
2031 service_files = True
2033 service_files = False
2035 args = slash_split(args)
2036 project = package = filename = None
2037 apiurl = conf.config['apiurl']
2039 project = project_dir = args[0]
2045 if args and len(args) == 1:
2046 localdir = os.getcwd()
2047 if is_project_dir(localdir):
2048 project = store_read_project(localdir)
2049 project_dir = localdir
2051 apiurl = store_read_apiurl(localdir)
2053 rev, dummy = parseRevisionOption(opts.revision)
2057 if rev and rev != "latest" and not checkRevision(project, package, rev):
2058 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2062 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2065 if opts.current_dir:
2067 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2068 prj_dir=project_dir, service_files=service_files, progress_obj=self.download_progress)
2069 print_request_list(apiurl, project, package)
2073 if sys.platform[:3] == 'win':
2074 prj_dir = prj_dir.replace(':', ';')
2075 if os.path.exists(prj_dir):
2076 sys.exit('osc: project \'%s\' already exists' % project)
2078 # check if the project does exist (show_project_meta will throw an exception)
2079 show_project_meta(apiurl, project)
2081 init_project_dir(apiurl, prj_dir, project)
2082 print statfrmt('A', prj_dir)
2085 for package in meta_get_packagelist(apiurl, project):
2087 checkout_package(apiurl, project, package, expand_link = expand_link, \
2088 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2089 except oscerr.LinkExpandError, e:
2090 print >>sys.stderr, 'Link cannot be expanded:\n', e
2091 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2092 # check out in unexpanded form at least
2093 checkout_package(apiurl, project, package, expand_link = False, \
2094 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2095 print_request_list(apiurl, project)
2098 raise oscerr.WrongArgs('Missing argument.\n\n' \
2099 + self.get_cmd_help('checkout'))
2102 @cmdln.option('-q', '--quiet', action='store_true',
2103 help='print as little as possible')
2104 @cmdln.option('-v', '--verbose', action='store_true',
2105 help='print extra information')
2107 def do_status(self, subcmd, opts, *args):
2108 """${cmd_name}: Show status of files in working copy
2110 Show the status of files in a local working copy, indicating whether
2111 files have been changed locally, deleted, added, ...
2113 The first column in the output specifies the status and is one of the
2114 following characters:
2115 ' ' no modifications
2120 '?' item is not under version control
2121 '!' item is missing (removed by non-osc command) or incomplete
2126 osc st file1 file2 ...
2129 osc status [OPTS] [PATH...]
2133 args = parseargs(args)
2135 # storage for single Package() objects
2137 # storage for a project dir ( { prj_instance : [ package objects ] } )
2140 # when 'status' is run inside a project dir, it should
2141 # stat all packages existing in the wc
2142 if is_project_dir(arg):
2143 prj = Project(arg, False)
2145 if conf.config['do_package_tracking']:
2147 for pac in prj.pacs_have:
2148 # we cannot create package objects if the dir does not exist
2149 if not pac in prj.pacs_broken:
2150 prjpacs[prj].append(os.path.join(arg, pac))
2152 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2153 elif is_package_dir(arg):
2154 pacpaths.append(arg)
2155 elif os.path.isfile(arg):
2156 pacpaths.append(arg)
2158 msg = '\'%s\' is neither a project or a package directory' % arg
2159 raise oscerr.NoWorkingCopy, msg
2161 # process single packages
2162 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2163 # process project dirs
2164 for prj, pacs in prjpacs.iteritems():
2165 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2167 print '\n'.join(lines)
2170 def do_add(self, subcmd, opts, *args):
2171 """${cmd_name}: Mark files to be added upon the next commit
2174 osc add FILE [FILE...]
2178 raise oscerr.WrongArgs('Missing argument.\n\n' \
2179 + self.get_cmd_help('add'))
2181 filenames = parseargs(args)
2185 def do_mkpac(self, subcmd, opts, *args):
2186 """${cmd_name}: Create a new package under version control
2189 osc mkpac new_package
2192 if not conf.config['do_package_tracking']:
2193 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2194 "in the [general] section in the configuration file"
2198 raise oscerr.WrongArgs('Wrong number of arguments.')
2200 createPackageDir(args[0])
2202 @cmdln.option('-r', '--recursive', action='store_true',
2203 help='If CWD is a project dir then scan all package dirs as well')
2205 def do_addremove(self, subcmd, opts, *args):
2206 """${cmd_name}: Adds new files, removes disappeared files
2208 Adds all files new in the local copy, and removes all disappeared files.
2210 ARG, if specified, is a package working copy.
2216 args = parseargs(args)
2218 for arg in arg_list:
2219 if is_project_dir(arg) and conf.config['do_package_tracking']:
2220 prj = Project(arg, False)
2221 for pac in prj.pacs_unvers:
2222 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2223 if os.path.isdir(pac_dir):
2224 addFiles([pac_dir], prj)
2225 for pac in prj.pacs_broken:
2226 if prj.get_state(pac) != 'D':
2227 prj.set_state(pac, 'D')
2228 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
2230 for pac in prj.pacs_have:
2231 state = prj.get_state(pac)
2232 if state != None and state != 'D':
2233 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2234 args.append(pac_dir)
2236 prj.write_packages()
2237 elif is_project_dir(arg):
2238 print >>sys.stderr, 'osc: addremove is not supported in a project dir unless ' \
2239 '\'do_package_tracking\' is enabled in the configuration file'
2242 pacs = findpacs(args)
2244 p.todo = p.filenamelist + p.filenamelist_unvers
2246 for filename in p.todo:
2247 if os.path.isdir(filename):
2249 # ignore foo.rXX, foo.mine for files which are in 'C' state
2250 if os.path.splitext(filename)[0] in p.in_conflict:
2252 state = p.status(filename)
2255 # TODO: should ignore typical backup files suffix ~ or .orig
2257 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
2259 p.put_on_deletelist(filename)
2260 p.write_deletelist()
2261 os.unlink(os.path.join(p.storedir, filename))
2262 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
2267 @cmdln.alias('checkin')
2268 @cmdln.option('-m', '--message', metavar='TEXT',
2269 help='specify log message TEXT')
2270 @cmdln.option('-F', '--file', metavar='FILE',
2271 help='read log message from FILE')
2272 @cmdln.option('-f', '--force', default=False, action="store_true",
2273 help='force commit - do not tests a file list')
2274 def do_commit(self, subcmd, opts, *args):
2275 """${cmd_name}: Upload content to the repository server
2277 Upload content which is changed in your working copy, to the repository
2280 Optionally checks the state of a working copy, if found a file with
2281 unknown state, it requests an user input:
2282 * skip - don't change anything, just move to another file
2283 * remove - remove a file from dir
2284 * edit file list - edit filelist using EDITOR
2285 * commit - don't check anything and commit package
2286 * abort - abort commit - this is default value
2287 This can be supressed by check_filelist config item, or -f/--force
2288 command line option.
2291 osc ci # current dir
2293 osc ci file1 file2 ...
2299 args = parseargs(args)
2306 msg = open(opts.file).read()
2308 sys.exit('could not open file \'%s\'.' % opts.file)
2311 for arg in arg_list:
2312 if conf.config['do_package_tracking'] and is_project_dir(arg):
2313 Project(arg).commit(msg=msg)
2315 msg = edit_message()
2318 pacs = findpacs(args)
2320 if conf.config['check_filelist'] and not opts.force:
2321 check_filelist_before_commit(pacs)
2324 template = store_read_file(os.path.abspath('.'), '_commit_msg')
2325 # open editor for commit message
2326 # but first, produce status and diff to append to the template
2330 changed = getStatus([pac], quiet=True)
2333 diffs += ['\nDiff for working copy: %s' % pac.dir]
2334 diffs += make_diff(pac, 0)
2335 lines.extend(get_commit_message_template(pac))
2336 if template == None:
2337 template='\n'.join(lines)
2338 # if footer is empty, there is nothing to commit, and no edit needed.
2340 msg = edit_message(footer='\n'.join(footer), template=template)
2343 store_write_string(os.path.abspath('.'), '_commit_msg', msg)
2345 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2347 if conf.config['do_package_tracking'] and len(pacs) > 0:
2351 # it is possible to commit packages from different projects at the same
2352 # time: iterate over all pacs and put each pac to the right project in the dict
2354 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
2355 if is_project_dir(path):
2356 pac_path = os.path.basename(os.path.normpath(pac.absdir))
2357 prj_paths.setdefault(path, []).append(pac_path)
2358 files[pac_path] = pac.todo
2360 single_paths.append(pac.dir)
2361 for prj, packages in prj_paths.iteritems():
2362 Project(prj).commit(tuple(packages), msg, files)
2363 for pac in single_paths:
2364 Package(pac).commit(msg)
2369 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2371 @cmdln.option('-r', '--revision', metavar='REV',
2372 help='update to specified revision (this option will be ignored '
2373 'if you are going to update the complete project or more than '
2375 @cmdln.option('-u', '--unexpand-link', action='store_true',
2376 help='if a package is an expanded link, update to the raw _link file')
2377 @cmdln.option('-e', '--expand-link', action='store_true',
2378 help='if a package is a link, update to the expanded sources')
2379 @cmdln.option('-s', '--source-service-files', action='store_true',
2380 help='Use server side generated sources instead of local generation.' )
2382 def do_update(self, subcmd, opts, *args):
2383 """${cmd_name}: Update a working copy
2388 If the current working directory is a package, update it.
2389 If the directory is a project directory, update all contained
2390 packages, AND check out newly added packages.
2392 To update only checked out packages, without checking out new
2393 ones, you might want to use "osc up *" from within the project
2397 Update the packages specified by the path argument(s)
2399 When --expand-link is used with source link packages, the expanded
2400 sources will be checked out. Without this option, the _link file and
2401 patches will be checked out. The option --unexpand-link can be used to
2402 switch back to the "raw" source with a _link file plus patch(es).
2408 if (opts.expand_link and opts.unexpand_link) \
2409 or (opts.expand_link and opts.revision) \
2410 or (opts.unexpand_link and opts.revision):
2411 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
2412 '--revision are mutually exclusive.')
2414 if opts.source_service_files: service_files = True
2415 else: service_files = False
2417 args = parseargs(args)
2420 for arg in arg_list:
2421 if is_project_dir(arg):
2422 prj = Project(arg, progress_obj=self.download_progress)
2424 if conf.config['do_package_tracking']:
2425 prj.update(expand_link=opts.expand_link,
2426 unexpand_link=opts.unexpand_link)
2429 # if not tracking package, and 'update' is run inside a project dir,
2430 # it should do the following:
2431 # (a) update all packages
2432 args += prj.pacs_have
2433 # (b) fetch new packages
2434 prj.checkout_missing_pacs(expand_link = not opts.unexpand_link)
2436 print_request_list(prj.apiurl, prj.name)
2439 pacs = findpacs(args, progress_obj=self.download_progress)
2441 if opts.revision and len(args) == 1:
2442 rev, dummy = parseRevisionOption(opts.revision)
2443 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
2444 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2451 print 'Updating %s' % p.name
2453 # FIXME: ugly workaround for #399247
2454 if opts.expand_link or opts.unexpand_link:
2455 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' and p.status(i) != '?']:
2456 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
2457 'copy has local modifications.\nPlease revert/commit them ' \
2462 if opts.expand_link and p.islink() and not p.isexpanded():
2463 if p.haslinkerror():
2465 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev)
2467 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev, linkrev="base")
2470 rev = p.linkinfo.xsrcmd5
2471 print 'Expanding to rev', rev
2472 elif opts.unexpand_link and p.islink() and p.isexpanded():
2473 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
2474 rev = p.linkinfo.lsrcmd5
2475 elif p.islink() and p.isexpanded():
2476 rev = p.latest_rev()
2478 p.update(rev, service_files)
2479 if opts.unexpand_link:
2482 print_request_list(p.apiurl, p.prjname, p.name)
2485 @cmdln.option('-f', '--force', action='store_true',
2486 help='forces removal of entire package and its files')
2489 @cmdln.alias('remove')
2490 def do_delete(self, subcmd, opts, *args):
2491 """${cmd_name}: Mark files or package directories to be deleted upon the next 'checkin'
2494 cd .../PROJECT/PACKAGE
2495 osc delete FILE [...]
2497 osc delete PACKAGE [...]
2499 This command works on check out copies. Use "rdelete" for working on server
2500 side only. This is needed for removing the entire project.
2502 As a safety measure, projects must be empty (i.e., you need to delete all
2505 If you are sure that you want to remove a package and all
2506 its files use \'--force\' switch. Sometimes this also works without --force.
2512 raise oscerr.WrongArgs('Missing argument.\n\n' \
2513 + self.get_cmd_help('delete'))
2515 args = parseargs(args)
2516 # check if args contains a package which was removed by
2517 # a non-osc command and mark it with the 'D'-state
2520 if not os.path.exists(i):
2521 prj_dir, pac_dir = getPrjPacPaths(i)
2522 if is_project_dir(prj_dir):
2523 prj = Project(prj_dir, False)
2524 if i in prj.pacs_broken:
2525 if prj.get_state(i) != 'A':
2526 prj.set_state(pac_dir, 'D')
2528 prj.del_package_node(i)
2529 print statfrmt('D', getTransActPath(i))
2531 prj.write_packages()
2532 pacs = findpacs(args)
2536 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
2537 if is_project_dir(prj_dir):
2538 if conf.config['do_package_tracking']:
2539 prj = Project(prj_dir, False)
2540 prj.delPackage(p, opts.force)
2542 print "WARNING: package tracking is disabled, operation skipped !"
2544 pathn = getTransActPath(p.dir)
2545 for filename in p.todo:
2546 ret, state = p.delete_file(filename, opts.force)
2548 print statfrmt('D', os.path.join(pathn, filename))
2551 sys.exit('\'%s\' is not under version control' % filename)
2552 elif state in ['A', 'M'] and not opts.force:
2553 sys.exit('\'%s\' has local modifications (use --force to remove this file)' % filename)
2556 def do_resolved(self, subcmd, opts, *args):
2557 """${cmd_name}: Remove 'conflicted' state on working copy files
2559 If an upstream change can't be merged automatically, a file is put into
2560 in 'conflicted' ('C') state. Within the file, conflicts are marked with
2561 special <<<<<<< as well as ======== and >>>>>>> lines.
2563 After manually resolving all conflicting parts, use this command to
2564 remove the 'conflicted' state.
2566 Note: this subcommand does not semantically resolve conflicts or
2567 remove conflict markers; it merely removes the conflict-related
2568 artifact files and allows PATH to be committed again.
2571 osc resolved FILE [FILE...]
2576 raise oscerr.WrongArgs('Missing argument.\n\n' \
2577 + self.get_cmd_help('resolved'))
2579 args = parseargs(args)
2580 pacs = findpacs(args)
2583 for filename in p.todo:
2584 print 'Resolved conflicted state of "%s"' % filename
2585 p.clear_from_conflictlist(filename)
2588 @cmdln.alias('platforms')
2589 def do_repositories(self, subcmd, opts, *args):
2590 """${cmd_name}: Shows available repositories
2594 Shows all available repositories/build targets
2596 2. osc repositories <project>
2597 Shows the configured repositories/build targets of a project
2605 print '\n'.join(get_repositories_of_project(conf.config['apiurl'], project))
2607 print '\n'.join(get_repositories(conf.config['apiurl']))
2611 def do_results_meta(self, subcmd, opts, *args):
2612 print "Command results_meta is obsolete. Please use: osc results --xml"
2616 @cmdln.option('-l', '--last-build', action='store_true',
2617 help='show last build results (succeeded/failed/unknown)')
2618 @cmdln.option('-r', '--repo', action='append', default = [],
2619 help='Show results only for specified repo(s)')
2620 @cmdln.option('-a', '--arch', action='append', default = [],
2621 help='Show results only for specified architecture(s)')
2622 @cmdln.option('', '--xml', action='store_true',
2623 help='generate output in XML (former results_meta)')
2624 def do_rresults(self, subcmd, opts, *args):
2625 print "Command rresults is obsolete. Running 'osc results' instead"
2626 self.do_results('results', opts, *args)
2630 @cmdln.option('-f', '--force', action='store_true', default=False,
2631 help="Don't ask and delete files")
2632 def do_rremove(self, subcmd, opts, project, package, *files):
2633 """${cmd_name}: Remove source files from selected package
2640 if not '/' in project:
2641 raise oscerr.WrongArgs("Missing operand, type osc help rremove for help")
2644 project, package = project.split('/')
2648 resp = raw_input("rm: remove source file `%s' from `%s/%s'? (yY|nN) " % (file, project, package))
2649 if resp not in ('y', 'Y'):
2652 delete_files(conf.config['apiurl'], project, package, (file, ))
2653 except urllib2.HTTPError, e:
2655 print >>sys.stderr, e
2657 if e.code in [ 400, 403, 404, 500 ]:
2658 if '<summary>' in body:
2659 msg = body.split('<summary>')[1]
2660 msg = msg.split('</summary>')[0]
2661 print >>sys.stderr, msg
2666 @cmdln.option('-l', '--last-build', action='store_true',
2667 help='show last build results (succeeded/failed/unknown)')
2668 @cmdln.option('-r', '--repo', action='append', default = [],
2669 help='Show results only for specified repo(s)')
2670 @cmdln.option('-a', '--arch', action='append', default = [],
2671 help='Show results only for specified architecture(s)')
2672 @cmdln.option('', '--xml', action='store_true',
2673 help='generate output in XML (former results_meta)')
2674 def do_results(self, subcmd, opts, *args):
2675 """${cmd_name}: Shows the build results of a package
2678 osc results (inside working copy)
2679 osc results remote_project remote_package
2684 args = slash_split(args)
2686 apiurl = conf.config['apiurl']
2689 if is_project_dir(wd):
2693 opts.hide_legend = None
2694 opts.name_filter = None
2695 opts.status_filter = None
2696 opts.vertical = None
2697 self.do_prjresults('prjresults', opts, *args);
2700 project = store_read_project(wd)
2701 package = store_read_package(wd)
2702 apiurl = store_read_apiurl(wd)
2704 raise oscerr.WrongArgs('Too few arguments (required none or two)')
2706 raise oscerr.WrongArgs('Too many arguments (required none or two)')
2715 func = show_results_meta
2718 print delim.join(func(apiurl, project, package, opts.last_build, opts.repo, opts.arch))
2720 # WARNING: this function is also called by do_results. You need to set a default there
2721 # as well when adding a new option!
2722 @cmdln.option('-q', '--hide-legend', action='store_true',
2723 help='hide the legend')
2724 @cmdln.option('-c', '--csv', action='store_true',
2726 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2727 help='show only packages with buildstatus STATUS (see legend)')
2728 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2729 help='show only packages whose names match EXPR')
2730 @cmdln.option('-a', '--arch', metavar='ARCH',
2731 help='show results only for specified architecture(s)')
2732 @cmdln.option('-r', '--repo', metavar='REPO',
2733 help='show results only for specified repo(s)')
2734 @cmdln.option('-V', '--vertical', action='store_true',
2735 help='list packages vertically instead horizontally')
2737 def do_prjresults(self, subcmd, opts, *args):
2738 """${cmd_name}: Shows project-wide build results
2741 osc prjresults (inside working copy)
2742 osc prjresults PROJECT
2748 apiurl = conf.config['apiurl']
2752 raise oscerr.WrongArgs('Wrong number of arguments.')
2755 project = store_read_project(wd)
2756 apiurl = store_read_apiurl(wd)
2758 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))
2761 @cmdln.option('-q', '--hide-legend', action='store_true',
2762 help='hide the legend')
2763 @cmdln.option('-c', '--csv', action='store_true',
2765 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2766 help='show only packages with buildstatus STATUS (see legend)')
2767 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2768 help='show only packages whose names match EXPR')
2771 def do_rprjresults(self, subcmd, opts, *args):
2772 print "Command rprjresults is obsolete. Please use 'osc prjresults'"
2776 @cmdln.option('-s', '--start', metavar='START',
2777 help='get log starting from the offset')
2778 def do_buildlog(self, subcmd, opts, *args):
2779 """${cmd_name}: Shows the build log of a package
2781 Shows the log file of the build of a package. Can be used to follow the
2782 log while it is being written.
2783 Needs to be called from within a package directory.
2785 The arguments REPOSITORY and ARCH are the first two columns in the 'osc
2786 results' output. If the buildlog url is used buildlog command has the
2787 same behavior as remotebuildlog.
2789 ${cmd_usage} [REPOSITORY ARCH | BUILDLOGURL]
2793 repository = arch = None
2795 if len(args) == 1 and args[0].startswith('http'):
2796 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2799 package = store_read_package(wd)
2800 project = store_read_project(wd)
2801 apiurl = store_read_apiurl(wd)
2805 offset = int(opts.start)
2807 if not repository or not arch:
2811 repository = args[0]
2814 print_buildlog(apiurl, project, package, repository, arch, offset)
2817 def print_repos(self):
2820 if is_package_dir(wd):
2823 elif is_project_dir(wd):
2828 print 'Valid arguments for this %s are:' % str
2830 self.do_repos(None, None)
2832 raise oscerr.WrongArgs('Missing arguments')
2835 @cmdln.alias('rbuildlog')
2836 @cmdln.option('-s', '--start', metavar='START',
2837 help='get log starting from the offset')
2838 def do_remotebuildlog(self, subcmd, opts, *args):
2839 """${cmd_name}: Shows the build log of a package
2841 Shows the log file of the build of a package. Can be used to follow the
2842 log while it is being written.
2845 osc remotebuildlog project package repository arch
2847 osc remotebuildlog project/package/repository/arch
2849 osc remotebuildlog buildlogurl
2852 if len(args) == 1 and args[0].startswith('http'):
2853 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2855 args = slash_split(args)
2856 apiurl = conf.config['apiurl']
2858 raise oscerr.WrongArgs('Too few arguments.')
2860 raise oscerr.WrongArgs('Too many arguments.')
2862 project, package, repository, arch = args
2866 offset = int(opts.start)
2868 print_buildlog(apiurl, project, package, repository, arch, offset)
2871 @cmdln.option('-s', '--start', metavar='START',
2872 help='get log starting from offset')
2873 def do_localbuildlog(self, subcmd, opts, *args):
2874 """${cmd_name}: Shows the build log of a local buildchroot
2877 osc lbl [REPOSITORY ARCH]
2878 osc lbl # show log of newest last local build
2882 if conf.config['build-type']:
2883 # FIXME: raise Exception instead
2884 print >>sys.stderr, 'Not implemented for VMs'
2888 package = store_read_package('.')
2890 files = glob.glob(os.path.join(os.getcwd(), store, "_buildinfo-*"))
2893 raise oscerr.WrongArgs('No buildconfig found, please specify repo and arch manually.')
2897 if os.stat(f).st_mtime > os.stat(cfg).st_mtime:
2899 root = ET.parse(cfg).getroot()
2900 project = root.get("project")
2901 repo = root.get("repository")
2902 arch = root.find("arch").text
2903 elif len(args) == 2:
2904 project = store_read_project('.')
2905 package = store_read_package('.')
2909 if is_package_dir(os.curdir):
2911 raise oscerr.WrongArgs('Wrong number of arguments.')
2913 buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root'])
2914 buildroot = buildroot % {'project': project, 'package': package,
2915 'repo': repo, 'arch': arch}
2918 offset = int(opts.start)
2919 logfile = os.path.join(buildroot, '.build.log')
2920 if not os.path.isfile(logfile):
2921 raise oscerr.OscIOError(None, 'logfile \'%s\' does not exist' % logfile)
2922 f = open(logfile, 'r')
2924 data = f.read(BUFSIZE)
2926 sys.stdout.write(data)
2927 data = f.read(BUFSIZE)
2931 def do_triggerreason(self, subcmd, opts, *args):
2932 """${cmd_name}: Show reason why a package got triggered to build
2934 The server decides when a package needs to get rebuild, this command
2935 shows the detailed reason for a package. A brief reason is also stored
2936 in the jobhistory, which can be accessed via "osc jobhistory".
2938 Trigger reasons might be:
2939 - new build (never build yet or rebuild manually forced)
2940 - source change (eg. on updating sources)
2941 - meta change (packages which are used for building have changed)
2942 - rebuild count sync (In case that it is configured to sync release numbers)
2944 usage in package or project directory:
2945 osc reason REPOSITORY ARCH
2946 osc reason PROJECT PACKAGE REPOSITORY ARCH
2951 args = slash_split(args)
2952 project = package = repository = arch = None
2957 if len(args) == 2: # 2
2958 if is_package_dir('.'):
2959 package = store_read_package(wd)
2961 raise oscerr.WrongArgs('package is not specified.')
2962 project = store_read_project(wd)
2963 apiurl = store_read_apiurl(wd)
2964 repository = args[0]
2966 elif len(args) == 4:
2967 apiurl = conf.config['apiurl']
2970 repository = args[2]
2973 raise oscerr.WrongArgs('Too many arguments.')
2975 print apiurl, project, package, repository, arch
2976 xml = show_package_trigger_reason(apiurl, project, package, repository, arch)
2977 root = ET.fromstring(xml)
2978 reason = root.find('explain').text
2980 if reason == "meta change":
2981 print "changed keys:"
2982 for package in root.findall('packagechange'):
2983 print " ", package.get('change'), package.get('key')
2986 # FIXME: the new osc syntax should allow to specify multiple packages
2987 # FIXME: the command should optionally use buildinfo data to show all dependencies
2988 @cmdln.alias('whatdependson')
2989 def do_dependson(self, subcmd, opts, *args):
2990 """${cmd_name}: Show the build dependencies
2992 The command dependson and whatdependson can be used to find out what
2993 will be triggered when a certain package changes.
2994 This is no guarantee, since the new build might have changed dependencies.
2996 dependson shows the build dependencies inside of a project, valid for a
2997 given repository and architecture.
2998 NOTE: to see all binary packages, which can trigger a build you need to
2999 refer the buildinfo, since this command shows only the dependencies
3000 inside of a project.
3002 The arguments REPOSITORY and ARCH can be taken from the first two columns
3003 of the 'osc repos' output.
3005 usage in package or project directory:
3006 osc dependson REPOSITORY ARCH
3007 osc whatdependson REPOSITORY ARCH
3010 osc dependson PROJECT [PACKAGE] REPOSITORY ARCH
3011 osc whatdependson PROJECT [PACKAGE] REPOSITORY ARCH
3016 args = slash_split(args)
3017 project = packages = repository = arch = reverse = None
3019 if len(args) < 2 and (is_package_dir('.') or is_project_dir('.')):
3023 raise oscerr.WrongArgs('Too many arguments.')
3025 if len(args) < 3: # 2
3026 if is_package_dir('.'):
3027 packages = [store_read_package(wd)]
3028 elif not is_project_dir('.'):
3029 raise oscerr.WrongArgs('Project and package is not specified.')
3030 project = store_read_project(wd)
3031 apiurl = store_read_apiurl(wd)
3032 repository = args[0]
3036 apiurl = conf.config['apiurl']
3038 repository = args[1]
3042 apiurl = conf.config['apiurl']
3044 packages = [args[1]]
3045 repository = args[2]
3048 if subcmd == 'whatdependson':
3051 xml = get_dependson(apiurl, project, repository, arch, packages, reverse)
3053 root = ET.fromstring(xml)
3054 for package in root.findall('package'):
3055 print package.get('name'), ":"
3056 for dep in package.findall('pkgdep'):
3060 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
3061 help='Add this package when computing the buildinfo')
3062 def do_buildinfo(self, subcmd, opts, *args):
3063 """${cmd_name}: Shows the build info
3065 Shows the build "info" which is used in building a package.
3066 This command is mostly used internally by the 'build' subcommand.
3067 It needs to be called from within a package directory.
3069 The BUILD_DESCR argument is optional. BUILD_DESCR is a local RPM specfile
3070 or Debian "dsc" file. If specified, it is sent to the server, and the
3071 buildinfo will be based on it. If the argument is not supplied, the
3072 buildinfo is derived from the specfile which is currently on the source
3075 The returned data is XML and contains a list of the packages used in
3076 building, their source, and the expanded BuildRequires.
3078 The arguments REPOSITORY and ARCH can be taken from the first two columns
3079 of the 'osc repos' output.
3082 osc buildinfo REPOSITORY ARCH [BUILD_DESCR] (in pkg dir)
3083 osc buildinfo PROJECT PACKAGE REPOSITORY ARCH [BUILD_DESCR]
3088 args = slash_split(args)
3090 if len(args) < 2 and is_package_dir('.'):
3094 raise oscerr.WrongArgs('Too many arguments.')
3096 if len(args) < 4: # 2 or 3
3097 package = store_read_package(wd)
3098 project = store_read_project(wd)
3099 apiurl = store_read_apiurl(wd)
3100 repository = args[0]
3103 if len(args) > 3 and len(args) < 6: # 4 or 5
3104 apiurl = conf.config['apiurl']
3107 repository = args[2]