3 # Copyright (C) 2006 Novell Inc. All rights reserved.
4 # This program is free software; it may be used, copied, modified
5 # and distributed under the terms of the GNU General Public Licence,
6 # either version 2, or version 3 (at your option).
13 import urlgrabber.progress
14 from optparse import SUPPRESS_HELP
16 MAN_HEADER = r""".TH %(ucname)s "1" "%(date)s" "%(name)s %(version)s" "User Commands"
18 %(name)s \- openSUSE build service command-line tool.
21 [\fIGLOBALOPTS\fR] \fISUBCOMMAND \fR[\fIOPTS\fR] [\fIARGS\fR...]
26 openSUSE build service command-line tool.
30 Type 'osc help <subcommand>' for more detailed help on a specific subcommand.
32 For additional information, see
33 * http://www.opensuse.org/Build_Service_Tutorial
34 * http://www.opensuse.org/Build_Service/CLI
36 You can modify osc commands, or roll you own, via the plugin API:
37 * http://www.opensuse.org/Build_Service/osc_plugins
39 osc was written by several authors. This man page is automatically generated.
42 class OscTextMeter(urlgrabber.progress.TextMeter):
43 """show the progress bar immediately"""
44 def _do_start(self, *args, **kwargs):
45 urlgrabber.progress.TextMeter._do_start(self, *args, **kwargs)
48 class Osc(cmdln.Cmdln):
49 """Usage: osc [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]
50 or: osc help SUBCOMMAND
52 openSUSE build service command-line tool.
53 Type 'osc help <subcommand>' for help on a specific subcommand.
58 For additional information, see
59 * http://www.opensuse.org/Build_Service_Tutorial
60 * http://www.opensuse.org/Build_Service/CLI
62 You can modify osc commands, or roll you own, via the plugin API:
63 * http://www.opensuse.org/Build_Service/osc_plugins
68 man_header = MAN_HEADER
69 man_footer = MAN_FOOTER
71 def __init__(self, *args, **kwargs):
72 cmdln.Cmdln.__init__(self, *args, **kwargs)
73 cmdln.Cmdln.do_help.aliases.append('h')
75 def get_version(self):
76 return get_osc_version()
78 def get_optparser(self):
79 """this is the parser for "global" options (not specific to subcommand)"""
81 optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
82 optparser.add_option('--debugger', action='store_true',
83 help='jump into the debugger before executing anything')
84 optparser.add_option('--post-mortem', action='store_true',
85 help='jump into the debugger in case of errors')
86 optparser.add_option('-t', '--traceback', action='store_true',
87 help='print call trace in case of errors')
88 optparser.add_option('-H', '--http-debug', action='store_true',
89 help='debug HTTP traffic')
90 optparser.add_option('-d', '--debug', action='store_true',
91 help='print info useful for debugging')
92 optparser.add_option('-A', '--apiurl', dest='apiurl',
94 help='specify URL to access API server at or an alias')
95 optparser.add_option('-c', '--config', dest='conffile',
97 help='specify alternate configuration file')
98 optparser.add_option('--no-keyring', action='store_true',
99 help='disable usage of desktop keyring system')
100 optparser.add_option('--no-gnome-keyring', action='store_true',
101 help='disable usage of GNOME Keyring')
102 optparser.add_option('-v', '--verbose', dest='verbose', action='count', default=0,
103 help='increase verbosity')
104 optparser.add_option('-q', '--quiet', dest='verbose', action='store_const', const=-1,
105 help='be quiet, not verbose')
109 def postoptparse(self, try_again = True):
110 """merge commandline options into the config"""
112 conf.get_config(override_conffile = self.options.conffile,
113 override_apiurl = self.options.apiurl,
114 override_debug = self.options.debug,
115 override_http_debug = self.options.http_debug,
116 override_traceback = self.options.traceback,
117 override_post_mortem = self.options.post_mortem,
118 override_no_keyring = self.options.no_keyring,
119 override_no_gnome_keyring = self.options.no_gnome_keyring,
120 override_verbose = self.options.verbose)
121 except oscerr.NoConfigfile, e:
122 print >>sys.stderr, e.msg
123 print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
126 config['user'] = raw_input('Username: ')
127 config['pass'] = getpass.getpass()
128 if self.options.apiurl:
129 config['apiurl'] = self.options.apiurl
131 conf.write_initial_config(e.file, config)
132 print >>sys.stderr, 'done'
133 if try_again: self.postoptparse(try_again = False)
134 except oscerr.ConfigMissingApiurl, e:
135 print >>sys.stderr, e.msg
137 user = raw_input('Username: ')
138 passwd = getpass.getpass()
139 conf.add_section(e.file, e.url, user, passwd)
140 if try_again: self.postoptparse(try_again = False)
142 self.options.verbose = conf.config['verbose']
143 self.download_progress = None
144 if conf.config.get('show_download_progress', False):
145 self.download_progress = OscTextMeter()
148 def get_cmd_help(self, cmdname):
149 doc = self._get_cmd_handler(cmdname).__doc__
150 doc = self._help_reindent(doc)
151 doc = self._help_preprocess(doc, cmdname)
152 doc = doc.rstrip() + '\n' # trim down trailing space
153 return self._str(doc)
156 # overridden from class Cmdln() to use config variables in help texts
157 def _help_preprocess(self, help, cmdname):
158 help = cmdln.Cmdln._help_preprocess(self, help, cmdname)
159 return help % conf.config
162 def do_init(self, subcmd, opts, project, package=None):
163 """${cmd_name}: Initialize a directory as working copy
165 Initialize an existing directory to be a working copy of an
166 (already existing) buildservice project/package.
168 (This is the same as checking out a package and then copying sources
169 into the directory. It does NOT create a new package. To create a
170 package, use 'osc meta pkg ... ...')
172 You wouldn't normally use this command.
174 To get a working copy of a package (e.g. for building it or working on
175 it, you would normally use the checkout command. Use "osc help
176 checkout" to get help for it.
185 init_project_dir(conf.config['apiurl'], os.curdir, project)
186 print 'Initializing %s (Project: %s)' % (os.curdir, project)
188 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
189 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
195 @cmdln.option('-a', '--arch', metavar='ARCH',
196 help='specify architecture (only for binaries)')
197 @cmdln.option('-r', '--repo', metavar='REPO',
198 help='specify repository (only for binaries)')
199 @cmdln.option('-b', '--binaries', action='store_true',
200 help='list built binaries instead of sources')
201 @cmdln.option('-R', '--revision', metavar='REVISION',
202 help='specify revision (only for sources)')
203 @cmdln.option('-e', '--expand', action='store_true',
204 help='expand linked package (only for sources)')
205 @cmdln.option('-v', '--verbose', action='store_true',
206 help='print extra information')
207 @cmdln.option('-l', '--long', action='store_true', dest='verbose',
208 help='print extra information')
209 def do_list(self, subcmd, opts, *args):
210 """${cmd_name}: List sources or binaries on the server
212 Examples for listing sources:
213 ls # list all projects
214 ls PROJECT # list packages in a project
215 ls PROJECT PACKAGE # list source files of package of a project
216 ls PROJECT PACKAGE <file> # list <file> if this file exists
217 ls -v PROJECT PACKAGE # verbosely list source files of package
218 ls -l PROJECT PACKAGE # verbosely list source files of package
219 ll PROJECT PACKAGE # verbosely list source files of package
220 LL PROJECT PACKAGE # verbosely list source files of expanded link
222 With --verbose, the following fields will be shown for each item:
224 Revision number of the last commit
226 Date and time of the last commit
228 Examples for listing binaries:
229 ls -b PROJECT # list all binaries of a project
230 ls -b PROJECT -a ARCH # list ARCH binaries of a project
231 ls -b PROJECT -r REPO # list binaries in REPO
232 ls -b PROJECT PACKAGE REPO ARCH
235 ${cmd_name} [PROJECT [PACKAGE]]
236 ${cmd_name} -b [PROJECT [PACKAGE [REPO [ARCH]]]]
240 apiurl = conf.config['apiurl']
241 args = slash_split(args)
244 if subcmd == 'lL' or subcmd == 'LL':
258 if opts.repo != args[2]:
259 raise oscerr.WrongArgs("conflicting repos specified ('%s' vs '%s')"%(opts.repo, args[2]))
266 if not opts.binaries:
267 raise oscerr.WrongArgs('Too many arguments')
269 if opts.arch != args[3]:
270 raise oscerr.WrongArgs("conflicting archs specified ('%s' vs '%s')"%(opts.arch, args[3]))
275 if opts.binaries and opts.expand:
276 raise oscerr.WrongOptions('Sorry, --binaries and --expand are mutual exclusive.')
280 # ls -b toplevel doesn't make sense, so use info from
281 # current dir if available
284 if is_project_dir(dir):
285 project = store_read_project(dir)
286 apiurl = store_read_apiurl(dir)
287 elif is_package_dir(dir):
288 project = store_read_project(dir)
289 package = store_read_package(dir)
290 apiurl = store_read_apiurl(dir)
293 raise oscerr.WrongArgs('There are no binaries to list above project level.')
295 raise oscerr.WrongOptions('Sorry, the --revision option is not supported for binaries.')
299 if opts.repo and opts.arch:
300 repos.append(Repo(opts.repo, opts.arch))
301 elif opts.repo and not opts.arch:
302 for repo in get_repos_of_project(apiurl, project):
303 if repo.name == opts.repo:
305 elif opts.arch and not opts.repo:
306 for repo in get_repos_of_project(apiurl, project):
307 if repo.arch == opts.arch:
310 repos = get_repos_of_project(apiurl, project)
314 results.append((repo, get_binarylist(apiurl, project, repo.name, repo.arch, package=package, verbose=opts.verbose)))
316 for result in results:
319 print '%s/%s' % (result[0].name, result[0].arch)
324 print "%9d %s %-40s" % (f.size, shorttime(f.mtime), f.name)
330 elif not opts.binaries:
332 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
336 if self.options.verbose:
337 print >>sys.stderr, 'Sorry, the --verbose option is not implemented for projects.'
339 raise oscerr.WrongOptions('Sorry, the --expand option is not implemented for projects.')
341 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
343 elif len(args) == 2 or len(args) == 3:
344 l = meta_get_filelist(conf.config['apiurl'],
347 verbose=opts.verbose,
349 revision=opts.revision)
351 out = [ '%s %7s %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
352 for i in l if not fname or fname == i.name ]
355 print 'file \'%s\' does not exist' % fname
363 print 'file \'%s\' does not exist' % fname
368 @cmdln.option('-f', '--force', action='store_true',
369 help='force generation of new patchinfo file')
370 @cmdln.option('--force-update', action='store_true',
371 help='drops away collected packages from an already built patch and let it collect again')
372 def do_patchinfo(self, subcmd, opts, *args):
373 """${cmd_name}: Generate and edit a patchinfo file.
375 A patchinfo file describes the packages for an update and the kind of
380 osc patchinfo PATCH_NAME
384 project_dir = localdir = os.getcwd()
385 if is_project_dir(localdir):
386 project = store_read_project(localdir)
387 apiurl = store_read_apiurl(localdir)
389 sys.exit('This command must be called in a checked out project.')
391 for p in meta_get_packagelist(apiurl, project):
392 if p.startswith("_patchinfo:"):
395 if opts.force or not patchinfo:
396 print "Creating initial patchinfo..."
397 query='cmd=createpatchinfo'
399 query += "&name=" + args[0]
400 url = makeurl(apiurl, ['source', project], query=query)
402 for p in meta_get_packagelist(apiurl, project):
403 if p.startswith("_patchinfo:"):
406 if not os.path.exists(project_dir + "/" + patchinfo):
407 checkout_package(apiurl, project, patchinfo, prj_dir=project_dir)
409 if sys.platform[:3] != 'win':
410 editor = os.getenv('EDITOR', default='vim')
412 editor = os.getenv('EDITOR', default='notepad')
413 subprocess.call('%s %s' % (editor, project_dir + "/" + patchinfo + "/_patchinfo"), shell=True)
416 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
417 help='affect only a given attribute')
418 @cmdln.option('--attribute-defaults', action='store_true',
419 help='include defined attribute defaults')
420 @cmdln.option('--attribute-project', action='store_true',
421 help='include project values, if missing in packages ')
422 @cmdln.option('-F', '--file', metavar='FILE',
423 help='read metadata from FILE, instead of opening an editor. '
424 '\'-\' denotes standard input. ')
425 @cmdln.option('-e', '--edit', action='store_true',
426 help='edit metadata')
427 @cmdln.option('-c', '--create', action='store_true',
428 help='create attribute without values')
429 @cmdln.option('-s', '--set', metavar='ATTRIBUTE_VALUES',
430 help='set attribute values')
431 @cmdln.option('--delete', action='store_true',
432 help='delete a pattern or attribute')
433 def do_meta(self, subcmd, opts, *args):
434 """${cmd_name}: Show meta information, or edit it
436 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
438 This command displays metadata on buildservice objects like projects,
439 packages, or users. The type of metadata is specified by the word after
440 "meta", like e.g. "meta prj".
442 prj denotes metadata of a buildservice project.
443 prjconf denotes the (build) configuration of a project.
444 pkg denotes metadata of a buildservice package.
445 user denotes the metadata of a user.
446 pattern denotes installation patterns defined for a project.
448 To list patterns, use 'osc meta pattern PRJ'. An additional argument
449 will be the pattern file to view or edit.
451 With the --edit switch, the metadata can be edited. Per default, osc
452 opens the program specified by the environmental variable EDITOR with a
453 temporary file. Alternatively, content to be saved can be supplied via
454 the --file switch. If the argument is '-', input is taken from stdin:
455 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
457 When trying to edit a non-existing resource, it is created implicitly.
463 osc meta pkg PRJ PKG -e
464 osc meta attribute PRJ [PKG [SUBPACKAGE]] [--attribute ATTRIBUTE] [--create|--delete|--set [value_list]]
467 osc meta <prj|pkg|prjconf|user|pattern|attribute> ARGS...
468 osc meta <prj|pkg|prjconf|user|pattern|attribute> -e|--edit ARGS...
469 osc meta <prj|pkg|prjconf|user|pattern|attribute> -F|--file ARGS...
470 osc meta pattern --delete PRJ PATTERN
474 args = slash_split(args)
476 if not args or args[0] not in metatypes.keys():
477 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
478 % ', '.join(metatypes))
484 min_args, max_args = 2, 2
485 elif cmd in ['pattern']:
486 min_args, max_args = 1, 2
487 elif cmd in ['attribute']:
488 min_args, max_args = 1, 3
490 min_args, max_args = 1, 1
491 if len(args) < min_args:
492 raise oscerr.WrongArgs('Too few arguments.')
493 if len(args) > max_args:
494 raise oscerr.WrongArgs('Too many arguments.')
501 project, package = args[0:2]
502 elif cmd == 'attribute':
508 if opts.attribute_project:
509 raise oscerr.WrongOptions('--attribute-project works only when also a package is given')
514 attributepath.append('source')
515 attributepath.append(project)
517 attributepath.append(package)
519 attributepath.append(subpackage)
520 attributepath.append('_attribute')
521 elif cmd == 'prjconf':
525 elif cmd == 'pattern':
531 # enforce pattern argument if needed
532 if opts.edit or opts.file:
533 raise oscerr.WrongArgs('A pattern file argument is required.')
536 if not opts.edit and not opts.file and not opts.delete and not opts.create and not opts.set:
538 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
540 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
541 elif cmd == 'attribute':
542 sys.stdout.write(''.join(show_attribute_meta(conf.config['apiurl'], project, package, subpackage, opts.attribute, opts.attribute_defaults, opts.attribute_project)))
543 elif cmd == 'prjconf':
544 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
546 r = get_user_meta(conf.config['apiurl'], user)
548 sys.stdout.write(''.join(r))
549 elif cmd == 'pattern':
551 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
553 sys.stdout.write(''.join(r))
555 r = show_pattern_metalist(conf.config['apiurl'], project)
557 sys.stdout.write('\n'.join(r) + '\n')
560 if opts.edit and not opts.file:
562 edit_meta(metatype='prj',
564 path_args=quote_plus(project),
567 'user': conf.config['user']}))
569 edit_meta(metatype='pkg',
571 path_args=(quote_plus(project), quote_plus(package)),
574 'user': conf.config['user']}))
575 elif cmd == 'prjconf':
576 edit_meta(metatype='prjconf',
578 path_args=quote_plus(project),
581 edit_meta(metatype='user',
583 path_args=(quote_plus(user)),
584 template_args=({'user': user}))
585 elif cmd == 'pattern':
586 edit_meta(metatype='pattern',
588 path_args=(project, pattern),
591 # create attribute entry
592 if (opts.create or opts.set) and cmd == 'attribute':
593 if not opts.attribute:
594 raise oscerr.WrongOptions('no attribute given to create')
597 opts.set = opts.set.replace('&', '&').replace('<', '<').replace('>', '>')
598 for i in opts.set.split(','):
599 values += '<value>%s</value>' % i
600 aname = opts.attribute.split(":")
601 d = '<attributes><attribute namespace=\'%s\' name=\'%s\' >%s</attribute></attributes>' % (aname[0], aname[1], values)
602 url = makeurl(conf.config['apiurl'], attributepath)
603 for data in streamfile(url, http_POST, data=d):
604 sys.stdout.write(data)
613 f = open(opts.file).read()
615 sys.exit('could not open file \'%s\'.' % opts.file)
618 edit_meta(metatype='prj',
621 path_args=quote_plus(project))
623 edit_meta(metatype='pkg',
626 path_args=(quote_plus(project), quote_plus(package)))
627 elif cmd == 'prjconf':
628 edit_meta(metatype='prjconf',
631 path_args=quote_plus(project))
633 edit_meta(metatype='user',
636 path_args=(quote_plus(user)))
637 elif cmd == 'pattern':
638 edit_meta(metatype='pattern',
641 path_args=(project, pattern))
646 path = metatypes[cmd]['path']
648 path = path % (project, pattern)
649 u = makeurl(conf.config['apiurl'], [path])
651 elif cmd == 'attribute':
652 if not opts.attribute:
653 raise oscerr.WrongOptions('no attribute given to create')
654 attributepath.append(opts.attribute)
655 u = makeurl(conf.config['apiurl'], attributepath)
656 for data in streamfile(u, http_DELETE):
657 sys.stdout.write(data)
659 raise oscerr.WrongOptions('The --delete switch is only for pattern metadata or attributes.')
662 @cmdln.option('-m', '--message', metavar='TEXT',
663 help='specify message TEXT')
664 @cmdln.option('-r', '--revision', metavar='REV',
665 help='for "create", specify a certain source revision ID (the md5 sum)')
666 @cmdln.option('-s', '--supersede', metavar='SUPERSEDE',
667 help='Superseding another request by this one')
668 @cmdln.option('--nodevelproject', action='store_true',
669 help='do not follow a defined devel project ' \
670 '(primary project where a package is developed)')
671 @cmdln.option('--cleanup', action='store_true',
672 help='remove package if submission gets accepted (default for home:<id>:branch projects)')
673 @cmdln.option('--no-cleanup', action='store_true',
674 help='never remove source package on accept, but update its content')
675 @cmdln.option('--no-update', action='store_true',
676 help='never touch source package on accept (will break source links)')
677 @cmdln.option('-d', '--diff', action='store_true',
678 help='show diff only instead of creating the actual request')
679 @cmdln.option('--yes', action='store_true',
680 help='proceed without asking.')
682 @cmdln.alias("submitreq")
683 @cmdln.alias("submitpac")
684 def do_submitrequest(self, subcmd, opts, *args):
685 """${cmd_name}: Create request to submit source into another Project
687 [See http://en.opensuse.org/Build_Service/Collaboration for information
690 See the "request" command for showing and modifing existing requests.
693 osc submitreq [OPTIONS]
694 osc submitreq [OPTIONS] DESTPRJ [DESTPKG]
695 osc submitreq [OPTIONS] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
699 src_update = conf.config['submitrequest_on_accept_action'] or None
700 # we should check here for home:<id>:branch and default to update, but that would require OBS 1.7 server
702 src_update = "cleanup"
703 elif opts.no_cleanup:
704 src_update = "update"
706 src_update = "noupdate"
708 args = slash_split(args)
710 # remove this block later again
711 oldcmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
712 if args and args[0] in oldcmds:
713 print "************************************************************************"
714 print "* WARNING: It looks that you are using this command with a *"
715 print "* deprecated syntax. *"
716 print "* Please run \"osc sr --help\" and \"osc rq --help\" *"
717 print "* to see the new syntax. *"
718 print "************************************************************************"
719 if args[0] == 'create':
725 raise oscerr.WrongArgs('Too many arguments.')
727 if len(args) > 0 and len(args) <= 2 and is_project_dir(os.getcwd()):
728 sys.exit('osc submitrequest from project directory is only working without target specs and for source linked files\n')
730 apiurl = conf.config['apiurl']
732 if len(args) == 0 and is_project_dir(os.getcwd()):
734 # submit requests for multiple packages are currently handled via multiple requests
735 # They could be also one request with multiple actions, but that avoids to accepts parts of it.
736 project = store_read_project(os.curdir)
737 apiurl = store_read_apiurl(os.curdir)
743 # loop via all packages for checking their state
744 for p in meta_get_packagelist(apiurl, project):
745 if p.startswith("_patchinfo:"):
748 # get _link info from server, who knows about the local state ...
749 u = makeurl(apiurl, ['source', project, p])
751 root = ET.parse(f).getroot()
752 linkinfo = root.find('linkinfo')
754 print "Package ", p, " is not a source link."
755 sys.exit("This is currently not supported.")
756 if linkinfo.get('error'):
757 print "Package ", p, " is a broken source link."
758 sys.exit("Please fix this first")
759 t = linkinfo.get('project')
761 if len(root.findall('entry')) > 1: # This is not really correct, but should work mostly
762 # Real fix is to ask the api if sources are modificated
763 # but there is no such call yet.
764 targetprojects.append(t)
766 print "Submitting package ", p
768 print " Skipping package ", p
770 print "Skipping package ", p, " since it is a source link pointing inside to the project."
774 print "Submitting patchinfo ", ', '.join(pi), " to ", ', '.join(targetprojects)
775 print "\nEverything fine? Can we create the requests ? [y/n]"
776 if sys.stdin.read(1) != "y":
777 sys.exit("Aborted...")
779 # loop via all packages to do the action
781 result = create_submit_request(apiurl, project, p)
784 sys.exit("submit request creation failed")
785 sr_ids.append(result)
787 # create submit requests for all found patchinfos
791 options_block="""<options><sourceupdate>%s</sourceupdate></options> """ % (src_update)
794 for t in targetprojects:
795 s = """<action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" /> %s </action>""" % \
796 (project, p, t, p, options_block)
800 xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \
801 (actionxml, cgi.escape(opts.message or ""))
802 u = makeurl(apiurl, ['request'], query='cmd=create')
803 f = http_POST(u, data=xml)
805 root = ET.parse(f).getroot()
806 sr_ids.append(root.get('id'))
808 print "Requests created: ",
811 sys.exit('Successfull finished')
814 # try using the working copy at hand
815 p = findpacs(os.curdir)[0]
816 src_project = p.prjname
819 if len(args) == 0 and p.islink():
820 dst_project = p.linkinfo.project
821 dst_package = p.linkinfo.package
823 dst_project = args[0]
825 dst_package = args[1]
827 dst_package = src_package
829 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
830 'Please provide it the target via commandline arguments.' % p.name)
832 modified = [i for i in p.filenamelist if p.status(i) != ' ' and p.status(i) != '?']
833 if len(modified) > 0:
834 print 'Your working copy has local modifications.'
835 repl = raw_input('Proceed without committing the local changes? (y|N) ')
839 # get the arguments from the commandline
840 src_project, src_package, dst_project = args[0:3]
842 dst_package = args[3]
844 dst_package = src_package
846 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
847 + self.get_cmd_help('request'))
849 if not opts.nodevelproject:
852 devloc = show_develproject(apiurl, dst_project, dst_package)
853 except urllib2.HTTPError:
854 print >>sys.stderr, """\
855 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
856 % (dst_project, dst_package)
860 dst_project != devloc and \
861 src_project != devloc:
863 A different project, %s, is defined as the place where development
864 of the package %s primarily takes place.
865 Please submit there instead, or use --nodevelproject to force direct submission.""" \
866 % (devloc, dst_package)
871 if opts.diff or not opts.message:
873 rdiff = 'old: %s/%s\nnew: %s/%s' %(dst_project, dst_package, src_project, src_package)
874 rdiff += server_diff(apiurl,
875 dst_project, dst_package, opts.revision,
876 src_project, src_package, None, True)
882 reqs = get_request_list(apiurl, dst_project, dst_package, req_type='submit')
883 user = conf.get_apiurl_usr(apiurl)
884 myreqs = [ i for i in reqs if i.state.who == user ]
887 print 'You already created the following submit request: %s.' % \
888 ', '.join([str(i.reqid) for i in myreqs ])
889 repl = raw_input('Supersede the old requests? (y/n/c) ')
890 if repl.lower() == 'c':
891 print >>sys.stderr, 'Aborting'
897 changes_re = re.compile(r'^--- .*\.changes ')
898 for line in rdiff.split('\n'):
899 if line.startswith('--- '):
900 if changes_re.match(line):
905 difflines.append(line)
906 opts.message = edit_message(footer=rdiff, template='\n'.join(parse_diff_for_commit_message('\n'.join(difflines))))
908 result = create_submit_request(apiurl,
909 src_project, src_package,
910 dst_project, dst_package,
911 opts.message, orev=opts.revision, src_update=src_update)
912 if repl.lower() == 'y':
914 change_request_state(apiurl, str(req.reqid), 'superseded',
915 'superseded by %s' % result, result)
918 r = change_request_state(conf.config['apiurl'],
919 opts.supersede, 'superseded', opts.message or '', result)
921 print 'created request id', result
924 @cmdln.option('-m', '--message', metavar='TEXT',
925 help='specify message TEXT')
927 @cmdln.alias("deletereq")
928 def do_deleterequest(self, subcmd, opts, *args):
929 """${cmd_name}: Create request to delete a package or project
933 osc deletereq [-m TEXT] PROJECT [PACKAGE]
937 args = slash_split(args)
940 raise oscerr.WrongArgs('Please specify at least a project.')
942 raise oscerr.WrongArgs('Too many arguments.')
944 apiurl = conf.config['apiurl']
952 opts.message = edit_message()
954 result = create_delete_request(apiurl, project, package, opts.message)
958 @cmdln.option('-m', '--message', metavar='TEXT',
959 help='specify message TEXT')
961 @cmdln.alias("changedevelreq")
962 def do_changedevelrequest(self, subcmd, opts, *args):
963 """${cmd_name}: Create request to change the devel package definition.
965 [See http://en.opensuse.org/Build_Service/Collaboration for information
968 See the "request" command for showing and modifing existing requests.
970 osc changedevelrequest PROJECT PACKAGE DEVEL_PROJECT [DEVEL_PACKAGE]
974 raise oscerr.WrongArgs('Too many arguments.')
976 if len(args) == 0 and is_package_dir('.') and len(conf.config['getpac_default_project']):
978 project = store_read_project(wd)
979 package = store_read_package(wd)
980 apiurl = store_read_apiurl(wd)
983 raise oscerr.WrongArgs('Too few arguments.')
985 apiurl = conf.config['apiurl']
987 devel_project = args[2]
990 devel_package = package
992 devel_package = args[3]
995 opts.message = edit_message()
997 result = create_change_devel_request(apiurl,
998 devel_project, devel_package,
1004 @cmdln.option('-d', '--diff', action='store_true',
1005 help='generate a diff')
1006 @cmdln.option('-u', '--unified', action='store_true',
1007 help='output the diff in the unified diff format')
1008 @cmdln.option('-m', '--message', metavar='TEXT',
1009 help='specify message TEXT')
1010 @cmdln.option('-t', '--type', metavar='TYPE',
1011 help='limit to requests which contain a given action type (submit/delete/change_devel)')
1012 @cmdln.option('-a', '--all', action='store_true',
1013 help='all states. Same as\'-s all\'')
1014 @cmdln.option('-s', '--state', default='', # default is 'all' if no args given, 'new' otherwise
1015 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]')
1016 @cmdln.option('-D', '--days', metavar='DAYS',
1017 help='only list requests in state "new" or changed in the last DAYS. [default=%(request_list_days)s]')
1018 @cmdln.option('-U', '--user', metavar='USER',
1019 help='same as -M, but for the specified USER')
1020 @cmdln.option('-b', '--brief', action='store_true', default=False,
1021 help='print output in list view as list subcommand')
1022 @cmdln.option('-M', '--mine', action='store_true',
1023 help='only show requests created by yourself')
1024 @cmdln.option('-B', '--bugowner', action='store_true',
1025 help='also show requests about packages where I am bugowner')
1026 @cmdln.option('-i', '--interactive', action='store_true',
1027 help='interactive review of request')
1029 @cmdln.alias("review")
1030 def do_request(self, subcmd, opts, *args):
1031 """${cmd_name}: Show and modify requests
1033 [See http://en.opensuse.org/Build_Service/Collaboration for information
1036 This command shows and modifies existing requests. To create new requests
1037 you need to call one of the following:
1040 osc changedevelrequest
1041 To send low level requests to the buildservice API, use:
1044 This command has the following sub commands:
1046 "list" lists open requests attached to a project or package or person.
1047 Uses the project/package of the current directory if none of
1048 -M, -U USER, project/package are given.
1050 "log" will show the history of the given ID
1052 "show" will show the request itself, and generate a diff for review, if
1053 used with the --diff option. The keyword show can be omitted if the ID is numeric.
1055 "decline" will change the request state to "declined" and append a
1056 message that you specify with the --message option.
1058 "wipe" will permanently delete a request.
1060 "revoke" will set the request state to "revoked" and append a
1061 message that you specify with the --message option.
1063 "accept" will change the request state to "accepted" and will trigger
1064 the actual submit process. That would normally be a server-side copy of
1065 the source package to the target package.
1067 "checkout" will checkout the request's source package. This only works for "submit" requests.
1070 osc request list [-M] [-U USER] [-s state] [-D DAYS] [-t type] [-B] [PRJ [PKG]]
1072 osc request [show] [-d] [-b] ID
1073 osc request accept [-m TEXT] ID
1074 osc request decline [-m TEXT] ID
1075 osc request revoke [-m TEXT] ID
1077 osc request checkout/co ID
1078 osc review accept [-m TEXT] ID
1079 osc review decline [-m TEXT] ID
1083 args = slash_split(args)
1085 if opts.all and opts.state:
1086 raise oscerr.WrongOptions('Sorry, the options --all and --state ' \
1087 'are mutually exclusive.')
1088 if opts.mine and opts.user:
1089 raise oscerr.WrongOptions('Sorry, the options --user and --mine ' \
1090 'are mutually exclusive.')
1095 if opts.state == '':
1098 if opts.state == '':
1101 cmds = ['list', 'log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co', 'help']
1102 if not args or args[0] not in cmds:
1103 raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
1104 % (args[0],', '.join(cmds)))
1110 return self.do_help(['help', 'request'])
1113 min_args, max_args = 1, 1
1114 elif cmd in ['list']:
1115 min_args, max_args = 0, 2
1117 min_args, max_args = 1, 1
1118 if len(args) < min_args:
1119 raise oscerr.WrongArgs('Too few arguments.')
1120 if len(args) > max_args:
1121 raise oscerr.WrongArgs('Too many arguments.')
1123 apiurl = conf.config['apiurl']
1130 elif not opts.mine and not opts.user:
1132 project = store_read_project(os.curdir)
1133 apiurl = store_read_apiurl(os.curdir)
1134 package = store_read_package(os.curdir)
1135 except oscerr.NoWorkingCopy:
1140 elif cmd in ['log', 'show', 'decline', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
1145 states = ('new', 'accepted', 'revoked', 'declined')
1146 state_list = opts.state.split(',')
1147 if opts.state == 'all':
1148 state_list = ['all']
1150 for s in state_list:
1152 raise oscerr.WrongArgs('Unknown state \'%s\', try one of %s' % (s, ','.join(states)))
1155 who = conf.get_apiurl_usr(apiurl)
1159 state_list = ['all']
1161 ## FIXME -B not implemented!
1163 if (self.options.debug):
1164 print 'list: option --bugowner ignored: not impl.'
1166 results = get_request_list(apiurl,
1167 project, package, who, state_list, opts.type)
1168 results.sort(reverse=True)
1170 days = opts.days or conf.config['request_list_days']
1177 since = time.strftime('%Y-%m-%dT%H:%M:%S',time.localtime(time.time()-days*24*3600))
1180 ## bs has received 2009-09-20 a new xquery compare() function
1181 ## which allows us to limit the list inside of get_request_list
1182 ## That would be much faster for coolo. But counting the remainder
1183 ## would not be possible with current xquery implementation.
1184 ## Workaround: fetch all, and filter on client side.
1186 ## FIXME: date filtering should become implemented on server side
1187 for result in results:
1188 if days == 0 or result.state.when > since or result.state.name == 'new':
1189 print result.list_view()
1193 print "There are %d requests older than %s days.\n" % (skipped, days)
1196 for l in get_request_log(conf.config['apiurl'], reqid):
1201 r = get_request(conf.config['apiurl'], reqid)
1204 elif opts.interactive or conf.config['request_show_interactive']:
1205 return request_interactive_review(conf.config['apiurl'], r)
1208 # fixme: will inevitably fail if the given target doesn't exist
1211 print server_diff(conf.config['apiurl'],
1212 r.actions[0].dst_project, r.actions[0].dst_package, None,
1213 r.actions[0].src_project, r.actions[0].src_package, r.actions[0].src_rev, opts.unified)
1214 except urllib2.HTTPError, e:
1215 e.osc_msg = 'Diff not possible'
1219 elif cmd == 'checkout' or cmd == 'co':
1220 r = get_request(conf.config['apiurl'], reqid)
1221 submits = [ i for i in r.actions if i.type == 'submit' ]
1222 if not len(submits):
1223 raise oscerr.WrongArgs('\'checkout\' only works for \'submit\' requests')
1224 checkout_package(conf.config['apiurl'], submits[0].src_project, submits[0].src_package, \
1225 submits[0].src_rev, expand_link=True, prj_dir=submits[0].src_project)
1228 if not opts.message:
1229 opts.message = edit_message()
1230 state_map = {'accept' : 'accepted', 'decline' : 'declined', 'wipe' : 'deleted', 'revoke' : 'revoked'}
1231 # Change review state only
1232 if subcmd == 'review':
1233 if cmd in ['accept', 'decline']:
1234 r = change_review_state(conf.config['apiurl'],
1235 reqid, state_map[cmd], conf.config['user'], '', opts.message or '')
1237 # Change state of entire request
1238 elif cmd in ['accept', 'decline', 'wipe', 'revoke']:
1239 r = change_request_state(conf.config['apiurl'],
1240 reqid, state_map[cmd], opts.message or '')
1243 # editmeta and its aliases are all depracated
1244 @cmdln.alias("editprj")
1245 @cmdln.alias("createprj")
1246 @cmdln.alias("editpac")
1247 @cmdln.alias("createpac")
1248 @cmdln.alias("edituser")
1249 @cmdln.alias("usermeta")
1251 def do_editmeta(self, subcmd, opts, *args):
1254 Obsolete command to edit metadata. Use 'meta' now.
1256 See the help output of 'meta'.
1260 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
1261 print >>sys.stderr, 'See \'osc help meta\'.'
1262 #self.do_help([None, 'meta'])
1266 @cmdln.option('-r', '--revision', metavar='rev',
1267 help='use the specified revision.')
1268 @cmdln.option('-u', '--unset', action='store_true',
1269 help='remove revision in link, it will point always to latest revision')
1270 def do_setlinkrev(self, subcmd, opts, *args):
1271 """${cmd_name}: Updates a revision number in a source link.
1273 This command adds or updates a specified revision number in a source link.
1274 The current revision of the source is used, if no revision number is specified.
1278 osc setlinkrev PROJECT [PACKAGE]
1282 args = slash_split(args)
1283 apiurl = conf.config['apiurl']
1286 p = findpacs(os.curdir)[0]
1291 sys.exit('Local directory is no checked out source link package, aborting')
1292 elif len(args) == 2:
1295 elif len(args) == 1:
1298 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1299 + self.get_cmd_help('setlinkrev'))
1302 packages = [ package ]
1304 packages = meta_get_packagelist(apiurl, project)
1307 print "setting revision for package", p
1311 rev, dummy = parseRevisionOption(opts.revision)
1312 set_link_rev(apiurl, project, p, rev)
1315 def do_linktobranch(self, subcmd, opts, *args):
1316 """${cmd_name}: Convert a package containing a classic link with patch to a branch
1318 This command tells the server to convert a _link with or without a project.diff
1319 to a branch. This is a full copy with a _link file pointing to the branched place.
1322 osc linktobranch # can be used in checked out package
1323 osc linktobranch PROJECT PACKAGE
1327 args = slash_split(args)
1330 project = store_read_project(wd)
1331 package = store_read_package(wd)
1332 apiurl = store_read_apiurl(wd)
1333 update_local_dir = True
1335 raise oscerr.WrongArgs('Too few arguments (required none or two)')
1337 raise oscerr.WrongArgs('Too many arguments (required none or two)')
1339 apiurl = conf.config['apiurl']
1342 update_local_dir = False
1345 link_to_branch(apiurl, project, package)
1346 if update_local_dir:
1347 pac = findpacs(wd)[0]
1351 @cmdln.option('-C', '--cicount', choices=['add', 'copy', 'local'],
1352 help='cicount attribute in the link, known values are add, copy, and local, default in buildservice is currently add.')
1353 @cmdln.option('-c', '--current', action='store_true',
1354 help='link fixed against current revision.')
1355 @cmdln.option('-r', '--revision', metavar='rev',
1356 help='link the specified revision.')
1357 @cmdln.option('-f', '--force', action='store_true',
1358 help='overwrite an existing link file if it is there.')
1359 @cmdln.option('-d', '--disable-publish', action='store_true',
1360 help='disable publishing of the linked package')
1361 def do_linkpac(self, subcmd, opts, *args):
1362 """${cmd_name}: "Link" a package to another package
1364 A linked package is a clone of another package, but plus local
1365 modifications. It can be cross-project.
1367 The DESTPAC name is optional; the source packages' name will be used if
1370 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
1372 To add a patch, add the patch as file and add it to the _link file.
1373 You can also specify text which will be inserted at the top of the spec file.
1375 See the examples in the _link file.
1378 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1382 args = slash_split(args)
1384 if not args or len(args) < 3:
1385 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1386 + self.get_cmd_help('linkpac'))
1388 rev, dummy = parseRevisionOption(opts.revision)
1390 src_project = args[0]
1391 src_package = args[1]
1392 dst_project = args[2]
1394 dst_package = args[3]
1396 dst_package = src_package
1398 if src_project == dst_project and src_package == dst_package:
1399 raise oscerr.WrongArgs('Error: source and destination are the same.')
1401 if src_project == dst_project and not opts.cicount:
1402 # in this case, the user usually wants to build different spec
1403 # files from the same source
1404 opts.cicount = "copy"
1407 rev = show_upstream_rev(conf.config['apiurl'], src_project, src_package)
1409 if rev and not checkRevision(src_project, src_package, rev):
1410 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1413 link_pac(src_project, src_package, dst_project, dst_package, opts.force, rev, opts.cicount, opts.disable_publish)
1415 @cmdln.option('-m', '--map-repo', metavar='SRC=TARGET[,SRC=TARGET]',
1416 help='Allows repository mapping(s) to be given as SRC=TARGET[,SRC=TARGET]')
1417 @cmdln.option('-d', '--disable-publish', action='store_true',
1418 help='disable publishing of the aggregated package')
1419 def do_aggregatepac(self, subcmd, opts, *args):
1420 """${cmd_name}: "Aggregate" a package to another package
1422 Aggregation of a package means that the build results (binaries) of a
1423 package are basically copied into another project.
1424 This can be used to make packages available from building that are
1425 needed in a project but available only in a different project. Note
1426 that this is done at the expense of disk space. See
1427 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
1428 for more information.
1430 The DESTPAC name is optional; the source packages' name will be used if
1434 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1438 args = slash_split(args)
1440 if not args or len(args) < 3:
1441 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1442 + self.get_cmd_help('aggregatepac'))
1444 src_project = args[0]
1445 src_package = args[1]
1446 dst_project = args[2]
1448 dst_package = args[3]
1450 dst_package = src_package
1452 if src_project == dst_project and src_package == dst_package:
1453 raise oscerr.WrongArgs('Error: source and destination are the same.')
1457 for pair in opts.map_repo.split(','):
1458 src_tgt = pair.split('=')
1459 if len(src_tgt) != 2:
1460 raise oscerr.WrongOptions('map "%s" must be SRC=TARGET[,SRC=TARGET]' % opts.map_repo)
1461 repo_map[src_tgt[0]] = src_tgt[1]
1463 aggregate_pac(src_project, src_package, dst_project, dst_package, repo_map, opts.disable_publish)
1466 @cmdln.option('-c', '--client-side-copy', action='store_true',
1467 help='do a (slower) client-side copy')
1468 @cmdln.option('-k', '--keep-maintainers', action='store_true',
1469 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
1470 @cmdln.option('-d', '--keep-develproject', action='store_true',
1471 help='keep develproject tag in the package metadata')
1472 @cmdln.option('-r', '--revision', metavar='rev',
1473 help='link the specified revision.')
1474 @cmdln.option('-t', '--to-apiurl', metavar='URL',
1475 help='URL of destination api server. Default is the source api server.')
1476 @cmdln.option('-m', '--message', metavar='TEXT',
1477 help='specify message TEXT')
1478 @cmdln.option('-e', '--expand', action='store_true',
1479 help='if the source package is a link then copy the expanded version of the link')
1480 def do_copypac(self, subcmd, opts, *args):
1481 """${cmd_name}: Copy a package
1483 A way to copy package to somewhere else.
1485 It can be done across buildservice instances, if the -t option is used.
1486 In that case, a client-side copy is implied.
1488 Using --client-side-copy always involves downloading all files, and
1489 uploading them to the target.
1491 The DESTPAC name is optional; the source packages' name will be used if
1495 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
1499 args = slash_split(args)
1501 if not args or len(args) < 3:
1502 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
1503 + self.get_cmd_help('copypac'))
1505 src_project = args[0]
1506 src_package = args[1]
1507 dst_project = args[2]
1509 dst_package = args[3]
1511 dst_package = src_package
1513 src_apiurl = conf.config['apiurl']
1515 dst_apiurl = conf.config['apiurl_aliases'].get(opts.to_apiurl, opts.to_apiurl)
1517 dst_apiurl = src_apiurl
1519 if src_project == dst_project and \
1520 src_package == dst_package and \
1521 src_apiurl == dst_apiurl:
1522 raise oscerr.WrongArgs('Source and destination are the same.')
1524 if src_apiurl != dst_apiurl:
1525 opts.client_side_copy = True
1527 rev, dummy = parseRevisionOption(opts.revision)
1530 comment = opts.message
1533 rev = show_upstream_rev(src_apiurl, src_project, src_package)
1534 comment = 'osc copypac from project:%s package:%s revision:%s' % ( src_project, src_package, rev )
1536 r = copy_pac(src_apiurl, src_project, src_package,
1537 dst_apiurl, dst_project, dst_package,
1538 client_side_copy=opts.client_side_copy,
1539 keep_maintainers=opts.keep_maintainers,
1540 keep_develproject=opts.keep_develproject,
1547 @cmdln.option('-c', '--checkout', action='store_true',
1548 help='Checkout branched package afterwards ' \
1549 '(\'osc bco\' is a shorthand for this option)' )
1550 @cmdln.option('-a', '--attribute', metavar='ATTRIBUTE',
1551 help='Use this attribute to find affected packages (default is OBS:Maintained)')
1552 @cmdln.option('-u', '--update-project-attribute', metavar='UPDATE_ATTRIBUTE',
1553 help='Use this attribute to find update projects (default is OBS:UpdateProject) ')
1554 def do_mbranch(self, subcmd, opts, *args):
1555 """${cmd_name}: Multiple branch of a package
1557 [See http://en.opensuse.org/Build_Service/Concepts/Maintenance for information
1560 This command is used for creating multiple links of defined version of a package
1561 in one project. This is esp. used for maintenance updates.
1563 The branched package will live in
1564 home:USERNAME:branches:ATTRIBUTE:PACKAGE
1565 if nothing else specified.
1568 osc mbranch [ SOURCEPACKAGE [ TARGETPROJECT ] ]
1571 args = slash_split(args)
1574 maintained_attribute = conf.config['maintained_attribute']
1575 maintained_update_project_attribute = conf.config['maintained_update_project_attribute']
1577 if not len(args) or len(args) > 2:
1578 raise oscerr.WrongArgs('Wrong number of arguments.')
1584 r = attribute_branch_pkg(conf.config['apiurl'], maintained_attribute, maintained_update_project_attribute, \
1588 print >>sys.stderr, 'ERROR: Attribute branch call came not back with a project.'
1591 print "Project " + r + " created."
1594 init_project_dir(conf.config['apiurl'], r, r)
1595 print statfrmt('A', r)
1598 for package in meta_get_packagelist(conf.config['apiurl'], r):
1600 checkout_package(conf.config['apiurl'], r, package, expand_link = True, prj_dir = r)
1602 print >>sys.stderr, 'Error while checkout package:\n', package
1604 if conf.config['verbose']:
1605 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1608 @cmdln.alias('branchco')
1610 @cmdln.alias('getpac')
1611 @cmdln.option('--nodevelproject', action='store_true',
1612 help='do not follow a defined devel project ' \
1613 '(primary project where a package is developed)')
1614 @cmdln.option('-c', '--checkout', action='store_true',
1615 help='Checkout branched package afterwards ' \
1616 '(\'osc bco\' is a shorthand for this option)' )
1617 @cmdln.option('-r', '--revision', metavar='rev',
1618 help='branch against a specific revision')
1619 def do_branch(self, subcmd, opts, *args):
1620 """${cmd_name}: Branch a package
1622 [See http://en.opensuse.org/Build_Service/Collaboration for information
1625 Create a source link from a package of an existing project to a new
1626 subproject of the requesters home project (home:branches:)
1628 The branched package will live in
1629 home:USERNAME:branches:PROJECT/PACKAGE
1630 if nothing else specified.
1632 With getpac or bco, the branched package will come from
1633 %(getpac_default_project)s
1634 if nothing else specified.
1637 osc branch SOURCEPROJECT SOURCEPACKAGE
1638 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT
1639 osc branch SOURCEPROJECT SOURCEPACKAGE TARGETPROJECT TARGETPACKAGE
1640 osc getpac SOURCEPACKAGE
1645 if subcmd == 'getpac' or subcmd == 'branchco' or subcmd == 'bco': opts.checkout = True
1646 args = slash_split(args)
1647 tproject = tpackage = None
1649 if (subcmd == 'getpac' or subcmd == 'bco') and len(args) == 1:
1650 print >>sys.stderr, 'defaulting to %s/%s' % (conf.config['getpac_default_project'], args[0])
1651 # python has no args.unshift ???
1652 args = [ conf.config['getpac_default_project'] , args[0] ]
1654 if len(args) < 2 or len(args) > 4:
1655 raise oscerr.WrongArgs('Wrong number of arguments.')
1656 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
1658 expected = tproject = args[2]
1662 exists, targetprj, targetpkg, srcprj, srcpkg = \
1663 branch_pkg(conf.config['apiurl'], args[0], args[1],
1664 nodevelproject=opts.nodevelproject, rev=opts.revision,
1665 target_project=tproject, target_package=tpackage,
1666 return_existing=opts.checkout)
1668 print >>sys.stderr, 'Using existing branch project: %s' % targetprj
1671 if not exists and (srcprj is not None and srcprj != args[0] or \
1672 srcprj is None and targetprj != expected):
1673 devloc = srcprj or targetprj
1674 if not srcprj and 'branches:' in targetprj:
1675 devloc = targetprj.split('branches:')[1]
1676 print '\nNote: The branch has been created of a different project,\n' \
1678 ' which is the primary location of where development for\n' \
1679 ' that package takes place.\n' \
1680 ' That\'s also where you would normally make changes against.\n' \
1681 ' A direct branch of the specified package can be forced\n' \
1682 ' with the --nodevelproject option.\n' % devloc
1684 package = tpackage or args[1]
1686 checkout_package(conf.config['apiurl'], targetprj, package,
1687 expand_link=True, prj_dir=targetprj)
1688 if conf.config['verbose']:
1689 print 'Note: You can use "osc delete" or "osc submitpac" when done.\n'
1692 if conf.get_configParser().get('general', 'apiurl') != conf.config['apiurl']:
1693 apiopt = '-A %s ' % conf.config['apiurl']
1694 print 'A working copy of the branched package can be checked out with:\n\n' \
1696 % (apiopt, targetprj, package)
1697 print_request_list(conf.config['apiurl'], args[0], args[1])
1699 print_request_list(conf.config['apiurl'], devloc, args[1])
1703 @cmdln.option('-f', '--force', action='store_true',
1704 help='deletes a package or an empty project')
1705 def do_rdelete(self, subcmd, opts, *args):
1706 """${cmd_name}: Delete a project or packages on the server.
1708 As a safety measure, project must be empty (i.e., you need to delete all
1709 packages first). If you are sure that you want to remove this project and all
1710 its packages use \'--force\' switch.
1713 osc rdelete -f PROJECT
1714 osc rdelete PROJECT PACKAGE [PACKAGE ...]
1719 args = slash_split(args)
1721 raise oscerr.WrongArgs('Missing argument.')
1727 # careful: if pkg is an empty string, the package delete request results
1728 # into a project delete request - which works recursively...
1730 delete_package(conf.config['apiurl'], prj, pkg)
1731 elif len(meta_get_packagelist(conf.config['apiurl'], prj)) >= 1 and not opts.force:
1732 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
1733 'If you are sure that you want to remove this project and all its ' \
1734 'packages use the \'--force\' switch'
1737 delete_project(conf.config['apiurl'], prj)
1740 def do_deletepac(self, subcmd, opts, *args):
1741 print """${cmd_name} is obsolete !
1744 osc delete for checked out packages or projects
1746 osc rdelete for server side operations."""
1751 @cmdln.option('-f', '--force', action='store_true',
1752 help='deletes a project and its packages')
1753 def do_deleteprj(self, subcmd, opts, project):
1754 """${cmd_name} is obsolete !
1761 @cmdln.alias('metafromspec')
1762 @cmdln.option('', '--specfile', metavar='FILE',
1763 help='Path to specfile. (if you pass more than working copy this option is ignored)')
1764 def do_updatepacmetafromspec(self, subcmd, opts, *args):
1765 """${cmd_name}: Update package meta information from a specfile
1767 ARG, if specified, is a package working copy.
1773 args = parseargs(args)
1774 if opts.specfile and len(args) == 1:
1775 specfile = opts.specfile
1778 pacs = findpacs(args)
1780 p.read_meta_from_spec(specfile)
1781 p.update_package_meta()
1785 @cmdln.option('-c', '--change', metavar='rev',
1786 help='the change made by revision rev (like -r rev-1:rev).'
1787 'If rev is negative this is like -r rev:rev-1.')
1788 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
1789 help='If rev1 is specified it will compare your working copy against '
1790 'the revision (rev1) on the server. '
1791 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
1792 '(NOTE: changes in your working copy are ignored in this case)')
1793 @cmdln.option('-p', '--plain', action='store_true',
1794 help='output the diff in plain (not unified) diff format')
1795 def do_diff(self, subcmd, opts, *args):
1796 """${cmd_name}: Generates a diff
1798 Generates a diff, comparing local changes against the repository
1801 ARG, specified, is a filename to include in the diff.
1807 args = parseargs(args)
1808 pacs = findpacs(args)
1812 rev = int(opts.change)
1822 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1825 rev1, rev2 = parseRevisionOption(opts.revision)
1829 diff += ''.join(make_diff(pac, rev1))
1831 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
1832 pac.prjname, pac.name, rev2, not opts.plain)
1837 @cmdln.option('--oldprj', metavar='OLDPRJ',
1838 help='project to compare against'
1839 ' (deprecated, use 3 argument form)')
1840 @cmdln.option('--oldpkg', metavar='OLDPKG',
1841 help='package to compare against'
1842 ' (deprecated, use 3 argument form)')
1843 @cmdln.option('-r', '--revision', metavar='N[:M]',
1844 help='revision id, where N = old revision and M = new revision')
1845 @cmdln.option('-p', '--plain', action='store_true',
1846 help='output the diff in plain (not unified) diff format')
1847 @cmdln.option('-c', '--change', metavar='rev',
1848 help='the change made by revision rev (like -r rev-1:rev). '
1849 'If rev is negative this is like -r rev:rev-1.')
1850 def do_rdiff(self, subcmd, opts, *args):
1851 """${cmd_name}: Server-side "pretty" diff of two packages
1853 Compares two packages (three or four arguments) or shows the
1854 changes of a specified revision of a package (two arguments)
1856 If no revision is specified the latest revision is used.
1858 Note that this command doesn't return a normal diff (which could be
1859 applied as patch), but a "pretty" diff, which also compares the content
1864 osc ${cmd_name} OLDPRJ OLDPAC NEWPRJ [NEWPAC]
1865 osc ${cmd_name} PROJECT PACKAGE
1869 args = slash_split(args)
1880 new_project = args[0]
1881 new_package = args[1]
1883 old_project = opts.oldprj
1885 old_package = opts.oldpkg
1886 elif len(args) == 3 or len(args) == 4:
1887 if opts.oldprj or opts.oldpkg:
1888 raise oscerr.WrongArgs('--oldpkg and --oldprj are only valid with two arguments')
1889 old_project = args[0]
1890 new_package = old_package = args[1]
1891 new_project = args[2]
1893 new_package = args[3]
1895 raise oscerr.WrongArgs('Wrong number of arguments')
1900 rev = int(opts.change)
1910 print >>sys.stderr, 'Revision \'%s\' not an integer' % opts.change
1914 rev1, rev2 = parseRevisionOption(opts.revision)
1916 rdiff = server_diff(conf.config['apiurl'],
1917 old_project, old_package, rev1,
1918 new_project, new_package, rev2, not opts.plain)
1924 def do_install(self, subcmd, opts, *args):
1925 """${cmd_name}: install a package after build via zypper in -r
1927 Not implemented yet. Use osc repourls,
1928 select the url you best like (standard),
1929 chop off after the last /, this should work with zypper.
1936 args = slash_split(args)
1937 args = expand_proj_pack(args)
1940 ## if there is only one argument, and it ends in .ymp
1941 ## then fetch it, Parse XML to get the first
1942 ## metapackage.group.repositories.repository.url
1943 ## and construct zypper cmd's for all
1944 ## metapackage.group.software.item.name
1946 ## if args[0] is already an url, the use it as is.
1948 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])
1949 print self.do_install.__doc__
1950 print "Example: \n" + cmd
1953 def do_repourls(self, subcmd, opts, *args):
1954 """${cmd_name}: Shows URLs of .repo files
1956 Shows URLs on which to access the project .repos files (yum-style
1957 metadata) on download.opensuse.org.
1960 osc repourls [PROJECT]
1965 apiurl = conf.config['apiurl']
1969 elif len(args) == 0:
1970 project = store_read_project('.')
1971 apiurl = store_read_apiurl('.')
1973 raise oscerr.WrongArgs('Wrong number of arguments')
1975 # XXX: API should somehow tell that
1976 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1977 repos = get_repositories_of_project(apiurl, project)
1979 print url_tmpl % (project.replace(':', ':/'), repo, project)
1982 @cmdln.option('-r', '--revision', metavar='rev',
1983 help='checkout the specified revision. '
1984 'NOTE: if you checkout the complete project '
1985 'this option is ignored!')
1986 @cmdln.option('-e', '--expand-link', action='store_true',
1987 help='if a package is a link, check out the expanded '
1988 'sources (no-op, since this became the default)')
1989 @cmdln.option('-u', '--unexpand-link', action='store_true',
1990 help='if a package is a link, check out the _link file ' \
1991 'instead of the expanded sources')
1992 @cmdln.option('-c', '--current-dir', action='store_true',
1993 help='place PACKAGE folder in the current directory' \
1994 'instead of a PROJECT/PACKAGE directory')
1995 @cmdln.option('-s', '--source-service-files', action='store_true',
1996 help='server side generated files of source services' \
1997 'gets downloaded as well' )
1999 def do_checkout(self, subcmd, opts, *args):
2000 """${cmd_name}: Check out content from the repository
2002 Check out content from the repository server, creating a local working
2005 When checking out a single package, the option --revision can be used
2006 to specify a revision of the package to be checked out.
2008 When a package is a source link, then it will be checked out in
2009 expanded form. If --unexpand-link option is used, the checkout will
2010 instead produce the raw _link file plus patches.
2013 osc co PROJECT [PACKAGE] [FILE]
2014 osc co PROJECT # entire project
2015 osc co PROJECT PACKAGE # a package
2016 osc co PROJECT PACKAGE FILE # single file -> to current dir
2018 while inside a project directory:
2019 osc co PACKAGE # check out PACKAGE from project
2024 if opts.unexpand_link:
2028 if opts.source_service_files:
2029 service_files = True
2031 service_files = False
2033 args = slash_split(args)
2034 project = package = filename = None
2035 apiurl = conf.config['apiurl']
2037 project = project_dir = args[0]
2043 if args and len(args) == 1:
2044 localdir = os.getcwd()
2045 if is_project_dir(localdir):
2046 project = store_read_project(localdir)
2047 project_dir = localdir
2049 apiurl = store_read_apiurl(localdir)
2051 rev, dummy = parseRevisionOption(opts.revision)
2055 if rev and rev != "latest" and not checkRevision(project, package, rev):
2056 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2060 get_source_file(apiurl, project, package, filename, revision=rev, progress_obj=self.download_progress)
2063 if opts.current_dir:
2065 checkout_package(apiurl, project, package, rev, expand_link=expand_link, \
2066 prj_dir=project_dir, service_files=service_files, progress_obj=self.download_progress)
2067 print_request_list(apiurl, project, package)
2071 if sys.platform[:3] == 'win':
2072 prj_dir = prj_dir.replace(':', ';')
2073 if os.path.exists(prj_dir):
2074 sys.exit('osc: project \'%s\' already exists' % project)
2076 # check if the project does exist (show_project_meta will throw an exception)
2077 show_project_meta(apiurl, project)
2079 init_project_dir(apiurl, prj_dir, project)
2080 print statfrmt('A', prj_dir)
2083 for package in meta_get_packagelist(apiurl, project):
2085 checkout_package(apiurl, project, package, expand_link = expand_link, \
2086 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2087 except oscerr.LinkExpandError, e:
2088 print >>sys.stderr, 'Link cannot be expanded:\n', e
2089 print >>sys.stderr, 'Use "osc repairlink" for fixing merge conflicts:\n'
2090 # check out in unexpanded form at least
2091 checkout_package(apiurl, project, package, expand_link = False, \
2092 prj_dir = prj_dir, service_files = service_files, progress_obj=self.download_progress)
2093 print_request_list(apiurl, project)
2096 raise oscerr.WrongArgs('Missing argument.\n\n' \
2097 + self.get_cmd_help('checkout'))
2100 @cmdln.option('-q', '--quiet', action='store_true',
2101 help='print as little as possible')
2102 @cmdln.option('-v', '--verbose', action='store_true',
2103 help='print extra information')
2105 def do_status(self, subcmd, opts, *args):
2106 """${cmd_name}: Show status of files in working copy
2108 Show the status of files in a local working copy, indicating whether
2109 files have been changed locally, deleted, added, ...
2111 The first column in the output specifies the status and is one of the
2112 following characters:
2113 ' ' no modifications
2118 '?' item is not under version control
2119 '!' item is missing (removed by non-osc command) or incomplete
2124 osc st file1 file2 ...
2127 osc status [OPTS] [PATH...]
2131 args = parseargs(args)
2133 # storage for single Package() objects
2135 # storage for a project dir ( { prj_instance : [ package objects ] } )
2138 # when 'status' is run inside a project dir, it should
2139 # stat all packages existing in the wc
2140 if is_project_dir(arg):
2141 prj = Project(arg, False)
2143 if conf.config['do_package_tracking']:
2145 for pac in prj.pacs_have:
2146 # we cannot create package objects if the dir does not exist
2147 if not pac in prj.pacs_broken:
2148 prjpacs[prj].append(os.path.join(arg, pac))
2150 pacpaths += [arg + '/' + n for n in prj.pacs_have]
2151 elif is_package_dir(arg):
2152 pacpaths.append(arg)
2153 elif os.path.isfile(arg):
2154 pacpaths.append(arg)
2156 msg = '\'%s\' is neither a project or a package directory' % arg
2157 raise oscerr.NoWorkingCopy, msg
2159 # process single packages
2160 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
2161 # process project dirs
2162 for prj, pacs in prjpacs.iteritems():
2163 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
2165 print '\n'.join(lines)
2168 def do_add(self, subcmd, opts, *args):
2169 """${cmd_name}: Mark files to be added upon the next commit
2172 osc add FILE [FILE...]
2176 raise oscerr.WrongArgs('Missing argument.\n\n' \
2177 + self.get_cmd_help('add'))
2179 filenames = parseargs(args)
2183 def do_mkpac(self, subcmd, opts, *args):
2184 """${cmd_name}: Create a new package under version control
2187 osc mkpac new_package
2190 if not conf.config['do_package_tracking']:
2191 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
2192 "in the [general] section in the configuration file"
2196 raise oscerr.WrongArgs('Wrong number of arguments.')
2198 createPackageDir(args[0])
2200 @cmdln.option('-r', '--recursive', action='store_true',
2201 help='If CWD is a project dir then scan all package dirs as well')
2203 def do_addremove(self, subcmd, opts, *args):
2204 """${cmd_name}: Adds new files, removes disappeared files
2206 Adds all files new in the local copy, and removes all disappeared files.
2208 ARG, if specified, is a package working copy.
2214 args = parseargs(args)
2216 for arg in arg_list:
2217 if is_project_dir(arg) and conf.config['do_package_tracking']:
2218 prj = Project(arg, False)
2219 for pac in prj.pacs_unvers:
2220 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2221 if os.path.isdir(pac_dir):
2222 addFiles([pac_dir], prj)
2223 for pac in prj.pacs_broken:
2224 if prj.get_state(pac) != 'D':
2225 prj.set_state(pac, 'D')
2226 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
2228 for pac in prj.pacs_have:
2229 state = prj.get_state(pac)
2230 if state != None and state != 'D':
2231 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
2232 args.append(pac_dir)
2234 prj.write_packages()
2235 elif is_project_dir(arg):
2236 print >>sys.stderr, 'osc: addremove is not supported in a project dir unless ' \
2237 '\'do_package_tracking\' is enabled in the configuration file'
2240 pacs = findpacs(args)
2242 p.todo = p.filenamelist + p.filenamelist_unvers
2244 for filename in p.todo:
2245 if os.path.isdir(filename):
2247 # ignore foo.rXX, foo.mine for files which are in 'C' state
2248 if os.path.splitext(filename)[0] in p.in_conflict:
2250 state = p.status(filename)
2253 # TODO: should ignore typical backup files suffix ~ or .orig
2255 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
2257 p.put_on_deletelist(filename)
2258 p.write_deletelist()
2259 os.unlink(os.path.join(p.storedir, filename))
2260 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
2265 @cmdln.alias('checkin')
2266 @cmdln.option('-m', '--message', metavar='TEXT',
2267 help='specify log message TEXT')
2268 @cmdln.option('-F', '--file', metavar='FILE',
2269 help='read log message from FILE')
2270 @cmdln.option('-f', '--force', default=False, action="store_true",
2271 help='force commit - do not tests a file list')
2272 def do_commit(self, subcmd, opts, *args):
2273 """${cmd_name}: Upload content to the repository server
2275 Upload content which is changed in your working copy, to the repository
2278 Optionally checks the state of a working copy, if found a file with
2279 unknown state, it requests an user input:
2280 * skip - don't change anything, just move to another file
2281 * remove - remove a file from dir
2282 * edit file list - edit filelist using EDITOR
2283 * commit - don't check anything and commit package
2284 * abort - abort commit - this is default value
2285 This can be supressed by check_filelist config item, or -f/--force
2286 command line option.
2289 osc ci # current dir
2291 osc ci file1 file2 ...
2297 args = parseargs(args)
2304 msg = open(opts.file).read()
2306 sys.exit('could not open file \'%s\'.' % opts.file)
2309 for arg in arg_list:
2310 if conf.config['do_package_tracking'] and is_project_dir(arg):
2311 Project(arg).commit(msg=msg)
2313 msg = edit_message()
2316 pacs = findpacs(args)
2318 if conf.config['check_filelist'] and not opts.force:
2319 check_filelist_before_commit(pacs)
2322 template = store_read_file(os.path.abspath('.'), '_commit_msg')
2323 # open editor for commit message
2324 # but first, produce status and diff to append to the template
2328 changed = getStatus([pac], quiet=True)
2331 diffs += ['\nDiff for working copy: %s' % pac.dir]
2332 diffs += make_diff(pac, 0)
2333 lines.extend(get_commit_message_template(pac))
2334 if template == None:
2335 template='\n'.join(lines)
2336 # if footer is empty, there is nothing to commit, and no edit needed.
2338 msg = edit_message(footer='\n'.join(footer), template=template)
2341 store_write_string(os.path.abspath('.'), '_commit_msg', msg)
2343 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2345 if conf.config['do_package_tracking'] and len(pacs) > 0:
2349 # it is possible to commit packages from different projects at the same
2350 # time: iterate over all pacs and put each pac to the right project in the dict
2352 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
2353 if is_project_dir(path):
2354 pac_path = os.path.basename(os.path.normpath(pac.absdir))
2355 prj_paths.setdefault(path, []).append(pac_path)
2356 files[pac_path] = pac.todo
2358 single_paths.append(pac.dir)
2359 for prj, packages in prj_paths.iteritems():
2360 Project(prj).commit(tuple(packages), msg, files)
2361 for pac in single_paths:
2362 Package(pac).commit(msg)
2367 store_unlink_file(os.path.abspath('.'), '_commit_msg')
2369 @cmdln.option('-r', '--revision', metavar='REV',
2370 help='update to specified revision (this option will be ignored '
2371 'if you are going to update the complete project or more than '
2373 @cmdln.option('-u', '--unexpand-link', action='store_true',
2374 help='if a package is an expanded link, update to the raw _link file')
2375 @cmdln.option('-e', '--expand-link', action='store_true',
2376 help='if a package is a link, update to the expanded sources')
2377 @cmdln.option('-s', '--source-service-files', action='store_true',
2378 help='Use server side generated sources instead of local generation.' )
2380 def do_update(self, subcmd, opts, *args):
2381 """${cmd_name}: Update a working copy
2386 If the current working directory is a package, update it.
2387 If the directory is a project directory, update all contained
2388 packages, AND check out newly added packages.
2390 To update only checked out packages, without checking out new
2391 ones, you might want to use "osc up *" from within the project
2395 Update the packages specified by the path argument(s)
2397 When --expand-link is used with source link packages, the expanded
2398 sources will be checked out. Without this option, the _link file and
2399 patches will be checked out. The option --unexpand-link can be used to
2400 switch back to the "raw" source with a _link file plus patch(es).
2406 if (opts.expand_link and opts.unexpand_link) \
2407 or (opts.expand_link and opts.revision) \
2408 or (opts.unexpand_link and opts.revision):
2409 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
2410 '--revision are mutually exclusive.')
2412 if opts.source_service_files: service_files = True
2413 else: service_files = False
2415 args = parseargs(args)
2418 for arg in arg_list:
2419 if is_project_dir(arg):
2420 prj = Project(arg, progress_obj=self.download_progress)
2422 if conf.config['do_package_tracking']:
2423 prj.update(expand_link=opts.expand_link,
2424 unexpand_link=opts.unexpand_link)
2427 # if not tracking package, and 'update' is run inside a project dir,
2428 # it should do the following:
2429 # (a) update all packages
2430 args += prj.pacs_have
2431 # (b) fetch new packages
2432 prj.checkout_missing_pacs(expand_link = not opts.unexpand_link)
2434 print_request_list(prj.apiurl, prj.name)
2437 pacs = findpacs(args, progress_obj=self.download_progress)
2439 if opts.revision and len(args) == 1:
2440 rev, dummy = parseRevisionOption(opts.revision)
2441 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
2442 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2449 print 'Updating %s' % p.name
2451 # FIXME: ugly workaround for #399247
2452 if opts.expand_link or opts.unexpand_link:
2453 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' and p.status(i) != '?']:
2454 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
2455 'copy has local modifications.\nPlease revert/commit them ' \
2460 if opts.expand_link and p.islink() and not p.isexpanded():
2461 if p.haslinkerror():
2463 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev)
2465 rev = show_upstream_xsrcmd5(p.apiurl, p.prjname, p.name, revision=p.rev, linkrev="base")
2468 rev = p.linkinfo.xsrcmd5
2469 print 'Expanding to rev', rev
2470 elif opts.unexpand_link and p.islink() and p.isexpanded():
2471 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
2472 rev = p.linkinfo.lsrcmd5
2473 elif p.islink() and p.isexpanded():
2474 rev = p.latest_rev()
2476 p.update(rev, service_files)
2477 if opts.unexpand_link:
2480 print_request_list(p.apiurl, p.prjname, p.name)
2483 @cmdln.option('-f', '--force', action='store_true',
2484 help='forces removal of entire package and its files')
2487 @cmdln.alias('remove')
2488 def do_delete(self, subcmd, opts, *args):
2489 """${cmd_name}: Mark files or package directories to be deleted upon the next 'checkin'
2492 cd .../PROJECT/PACKAGE
2493 osc delete FILE [...]
2495 osc delete PACKAGE [...]
2497 This command works on check out copies. Use "rdelete" for working on server
2498 side only. This is needed for removing the entire project.
2500 As a safety measure, projects must be empty (i.e., you need to delete all
2503 If you are sure that you want to remove a package and all
2504 its files use \'--force\' switch. Sometimes this also works without --force.
2510 raise oscerr.WrongArgs('Missing argument.\n\n' \
2511 + self.get_cmd_help('delete'))
2513 args = parseargs(args)
2514 # check if args contains a package which was removed by
2515 # a non-osc command and mark it with the 'D'-state
2518 if not os.path.exists(i):
2519 prj_dir, pac_dir = getPrjPacPaths(i)
2520 if is_project_dir(prj_dir):
2521 prj = Project(prj_dir, False)
2522 if i in prj.pacs_broken:
2523 if prj.get_state(i) != 'A':
2524 prj.set_state(pac_dir, 'D')
2526 prj.del_package_node(i)
2527 print statfrmt('D', getTransActPath(i))
2529 prj.write_packages()
2530 pacs = findpacs(args)
2534 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
2535 if is_project_dir(prj_dir):
2536 if conf.config['do_package_tracking']:
2537 prj = Project(prj_dir, False)
2538 prj.delPackage(p, opts.force)
2540 print "WARNING: package tracking is disabled, operation skipped !"
2542 pathn = getTransActPath(p.dir)
2543 for filename in p.todo:
2544 ret, state = p.delete_file(filename, opts.force)
2546 print statfrmt('D', os.path.join(pathn, filename))
2549 sys.exit('\'%s\' is not under version control' % filename)
2550 elif state in ['A', 'M'] and not opts.force:
2551 sys.exit('\'%s\' has local modifications (use --force to remove this file)' % filename)
2554 def do_resolved(self, subcmd, opts, *args):
2555 """${cmd_name}: Remove 'conflicted' state on working copy files
2557 If an upstream change can't be merged automatically, a file is put into
2558 in 'conflicted' ('C') state. Within the file, conflicts are marked with
2559 special <<<<<<< as well as ======== and >>>>>>> lines.
2561 After manually resolving all conflicting parts, use this command to
2562 remove the 'conflicted' state.
2564 Note: this subcommand does not semantically resolve conflicts or
2565 remove conflict markers; it merely removes the conflict-related
2566 artifact files and allows PATH to be committed again.
2569 osc resolved FILE [FILE...]
2574 raise oscerr.WrongArgs('Missing argument.\n\n' \
2575 + self.get_cmd_help('resolved'))
2577 args = parseargs(args)
2578 pacs = findpacs(args)
2581 for filename in p.todo:
2582 print 'Resolved conflicted state of "%s"' % filename
2583 p.clear_from_conflictlist(filename)
2586 @cmdln.alias('platforms')
2587 def do_repositories(self, subcmd, opts, *args):
2588 """${cmd_name}: Shows available repositories
2592 Shows all available repositories/build targets
2594 2. osc repositories <project>
2595 Shows the configured repositories/build targets of a project
2603 print '\n'.join(get_repositories_of_project(conf.config['apiurl'], project))
2605 print '\n'.join(get_repositories(conf.config['apiurl']))
2609 def do_results_meta(self, subcmd, opts, *args):
2610 print "Command results_meta is obsolete. Please use: osc results --xml"
2614 @cmdln.option('-l', '--last-build', action='store_true',
2615 help='show last build results (succeeded/failed/unknown)')
2616 @cmdln.option('-r', '--repo', action='append', default = [],
2617 help='Show results only for specified repo(s)')
2618 @cmdln.option('-a', '--arch', action='append', default = [],
2619 help='Show results only for specified architecture(s)')
2620 @cmdln.option('', '--xml', action='store_true',
2621 help='generate output in XML (former results_meta)')
2622 def do_rresults(self, subcmd, opts, *args):
2623 print "Command rresults is obsolete. Running 'osc results' instead"
2624 self.do_results('results', opts, *args)
2628 @cmdln.option('-f', '--force', action='store_true', default=False,
2629 help="Don't ask and delete files")
2630 def do_rremove(self, subcmd, opts, project, package, *files):
2631 """${cmd_name}: Remove source files from selected package
2638 if not '/' in project:
2639 raise oscerr.WrongArgs("Missing operand, type osc help rremove for help")
2642 project, package = project.split('/')
2646 resp = raw_input("rm: remove source file `%s' from `%s/%s'? (yY|nN) " % (file, project, package))
2647 if resp not in ('y', 'Y'):
2650 delete_files(conf.config['apiurl'], project, package, (file, ))
2651 except urllib2.HTTPError, e:
2653 print >>sys.stderr, e
2655 if e.code in [ 400, 403, 404, 500 ]:
2656 if '<summary>' in body:
2657 msg = body.split('<summary>')[1]
2658 msg = msg.split('</summary>')[0]
2659 print >>sys.stderr, msg
2664 @cmdln.option('-l', '--last-build', action='store_true',
2665 help='show last build results (succeeded/failed/unknown)')
2666 @cmdln.option('-r', '--repo', action='append', default = [],
2667 help='Show results only for specified repo(s)')
2668 @cmdln.option('-a', '--arch', action='append', default = [],
2669 help='Show results only for specified architecture(s)')
2670 @cmdln.option('', '--xml', action='store_true',
2671 help='generate output in XML (former results_meta)')
2672 def do_results(self, subcmd, opts, *args):
2673 """${cmd_name}: Shows the build results of a package
2676 osc results (inside working copy)
2677 osc results remote_project remote_package
2682 args = slash_split(args)
2684 apiurl = conf.config['apiurl']
2687 if is_project_dir(wd):
2691 opts.hide_legend = None
2692 opts.name_filter = None
2693 opts.status_filter = None
2694 opts.vertical = None
2695 self.do_prjresults('prjresults', opts, *args);
2698 project = store_read_project(wd)
2699 package = store_read_package(wd)
2700 apiurl = store_read_apiurl(wd)
2702 raise oscerr.WrongArgs('Too few arguments (required none or two)')
2704 raise oscerr.WrongArgs('Too many arguments (required none or two)')
2713 func = show_results_meta
2716 print delim.join(func(apiurl, project, package, opts.last_build, opts.repo, opts.arch))
2718 # WARNING: this function is also called by do_results. You need to set a default there
2719 # as well when adding a new option!
2720 @cmdln.option('-q', '--hide-legend', action='store_true',
2721 help='hide the legend')
2722 @cmdln.option('-c', '--csv', action='store_true',
2724 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2725 help='show only packages with buildstatus STATUS (see legend)')
2726 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2727 help='show only packages whose names match EXPR')
2728 @cmdln.option('-a', '--arch', metavar='ARCH',
2729 help='show results only for specified architecture(s)')
2730 @cmdln.option('-r', '--repo', metavar='REPO',
2731 help='show results only for specified repo(s)')
2732 @cmdln.option('-V', '--vertical', action='store_true',
2733 help='list packages vertically instead horizontally')
2735 def do_prjresults(self, subcmd, opts, *args):
2736 """${cmd_name}: Shows project-wide build results
2739 osc prjresults (inside working copy)
2740 osc prjresults PROJECT
2746 apiurl = conf.config['apiurl']
2750 raise oscerr.WrongArgs('Wrong number of arguments.')
2753 project = store_read_project(wd)
2754 apiurl = store_read_apiurl(wd)
2756 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))
2759 @cmdln.option('-q', '--hide-legend', action='store_true',
2760 help='hide the legend')
2761 @cmdln.option('-c', '--csv', action='store_true',
2763 @cmdln.option('-s', '--status-filter', metavar='STATUS',
2764 help='show only packages with buildstatus STATUS (see legend)')
2765 @cmdln.option('-n', '--name-filter', metavar='EXPR',
2766 help='show only packages whose names match EXPR')
2769 def do_rprjresults(self, subcmd, opts, *args):
2770 print "Command rprjresults is obsolete. Please use 'osc prjresults'"
2774 @cmdln.option('-s', '--start', metavar='START',
2775 help='get log starting from the offset')
2776 def do_buildlog(self, subcmd, opts, *args):
2777 """${cmd_name}: Shows the build log of a package
2779 Shows the log file of the build of a package. Can be used to follow the
2780 log while it is being written.
2781 Needs to be called from within a package directory.
2783 The arguments REPOSITORY and ARCH are the first two columns in the 'osc
2786 ${cmd_usage} REPOSITORY ARCH
2791 package = store_read_package(wd)
2792 project = store_read_project(wd)
2793 apiurl = store_read_apiurl(wd)
2797 offset = int(opts.start)
2802 repository = args[0]
2805 print_buildlog(apiurl, project, package, repository, arch, offset)
2808 def print_repos(self):
2811 if is_package_dir(wd):
2814 elif is_project_dir(wd):
2819 print 'Valid arguments for this %s are:' % str
2821 self.do_repos(None, None)
2823 raise oscerr.WrongArgs('Missing arguments')
2826 @cmdln.alias('rbuildlog')
2827 @cmdln.option('-s', '--start', metavar='START',
2828 help='get log starting from the offset')
2829 def do_remotebuildlog(self, subcmd, opts, *args):
2830 """${cmd_name}: Shows the build log of a package
2832 Shows the log file of the build of a package. Can be used to follow the
2833 log while it is being written.
2836 osc remotebuildlog project package repository arch
2838 osc remotebuildlog project/package/repository/arch
2841 if len(args) == 1 and args[0].startswith('http'):
2842 apiurl, project, package, repository, arch = parse_buildlogurl(args[0])
2844 args = slash_split(args)
2845 apiurl = conf.config['apiurl']
2847 raise oscerr.WrongArgs('Too few arguments.')
2849 raise oscerr.WrongArgs('Too many arguments.')
2851 project, package, repository, arch = args
2855 offset = int(opts.start)
2857 print_buildlog(apiurl, project, package, repository, arch, offset)
2860 @cmdln.option('-s', '--start', metavar='START',
2861 help='get log starting from offset')
2862 def do_localbuildlog(self, subcmd, opts, *args):
2864 raise oscerr.WrongArgs('Too few arguments')
2866 raise oscerr.WrongArgs('Too many arguments')
2867 if conf.config['build-type']:
2868 print >>sys.stderr, 'Not implemented for VMs'
2870 buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root'])
2871 buildroot = buildroot % {'project': store_read_project('.'), 'package': store_read_package('.'),
2872 'repo': args[0], 'arch': args[1]}
2875 offset = int(opts.start)
2876 logfile = os.path.join(buildroot, '.build.log')
2877 if not os.path.isfile(logfile):
2878 raise oscerr.OscIOError(None, 'logfile \'%s\' does not exist' % logfile)
2879 f = open(logfile, 'r')
2881 data = f.read(BUFSIZE)
2883 sys.stdout.write(data)
2884 data = f.read(BUFSIZE)
2888 def do_triggerreason(self, subcmd, opts, *args):
2889 """${cmd_name}: Show reason why a package got triggered to build
2891 The server decides when a package needs to get rebuild, this command
2892 shows the detailed reason for a package. A brief reason is also stored
2893 in the jobhistory, which can be accessed via "osc jobhistory".
2895 Trigger reasons might be:
2896 - new build (never build yet or rebuild manually forced)
2897 - source change (eg. on updating sources)
2898 - meta change (packages which are used for building have changed)
2899 - rebuild count sync (In case that it is configured to sync release numbers)
2901 usage in package or project directory:
2902 osc reason REPOSITORY ARCH
2903 osc reason PROJECT PACKAGE REPOSITORY ARCH
2908 args = slash_split(args)
2909 project = package = repository = arch = None
2914 if len(args) == 2: # 2
2915 if is_package_dir('.'):
2916 package = store_read_package(wd)
2918 raise oscerr.WrongArgs('package is not specified.')
2919 project = store_read_project(wd)
2920 apiurl = store_read_apiurl(wd)
2921 repository = args[0]
2923 elif len(args) == 4:
2924 apiurl = conf.config['apiurl']
2927 repository = args[2]
2930 raise oscerr.WrongArgs('Too many arguments.')
2932 print apiurl, project, package, repository, arch
2933 xml = show_package_trigger_reason(apiurl, project, package, repository, arch)
2934 root = ET.fromstring(xml)
2935 reason = root.find('explain').text
2937 if reason == "meta change":
2938 print "changed keys:"
2939 for package in root.findall('packagechange'):
2940 print " ", package.get('change'), package.get('key')
2943 # FIXME: the new osc syntax should allow to specify multiple packages
2944 # FIXME: the command should optionally use buildinfo data to show all dependencies
2945 @cmdln.alias('whatdependson')
2946 def do_dependson(self, subcmd, opts, *args):
2947 """${cmd_name}: Show the build dependencies
2949 The command dependson and whatdependson can be used to find out what
2950 will be triggered when a certain package changes.
2951 This is no guarantee, since the new build might have changed dependencies.
2953 dependson shows the build dependencies inside of a project, valid for a
2954 given repository and architecture.
2955 NOTE: to see all binary packages, which can trigger a build you need to
2956 refer the buildinfo, since this command shows only the dependencies
2957 inside of a project.
2959 The arguments REPOSITORY and ARCH can be taken from the first two columns
2960 of the 'osc repos' output.
2962 usage in package or project directory:
2963 osc dependson REPOSITORY ARCH
2964 osc whatdependson REPOSITORY ARCH
2967 osc dependson PROJECT [PACKAGE] REPOSITORY ARCH
2968 osc whatdependson PROJECT [PACKAGE] REPOSITORY ARCH
2973 args = slash_split(args)
2974 project = packages = repository = arch = reverse = None
2976 if len(args) < 2 and (is_package_dir('.') or is_project_dir('.')):
2980 raise oscerr.WrongArgs('Too many arguments.')
2982 if len(args) < 3: # 2
2983 if is_package_dir('.'):
2984 packages = [store_read_package(wd)]
2985 elif not is_project_dir('.'):
2986 raise oscerr.WrongArgs('Project and package is not specified.')
2987 project = store_read_project(wd)
2988 apiurl = store_read_apiurl(wd)
2989 repository = args[0]
2993 apiurl = conf.config['apiurl']
2995 repository = args[1]
2999 apiurl = conf.config['apiurl']
3001 packages = [args[1]]
3002 repository = args[2]
3005 if subcmd == 'whatdependson':
3008 xml = get_dependson(apiurl, project, repository, arch, packages, reverse)
3010 root = ET.fromstring(xml)
3011 for package in root.findall('package'):
3012 print package.get('name'), ":"
3013 for dep in package.findall('pkgdep'):
3017 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
3018 help='Add this package when computing the buildinfo')
3019 def do_buildinfo(self, subcmd, opts, *args):
3020 """${cmd_name}: Shows the build info
3022 Shows the build "info" which is used in building a package.
3023 This command is mostly used internally by the 'build' subcommand.
3024 It needs to be called from within a package directory.
3026 The BUILD_DESCR argument is optional. BUILD_DESCR is a local RPM specfile
3027 or Debian "dsc" file. If specified, it is sent to the server, and the
3028 buildinfo will be based on it. If the argument is not supplied, the
3029 buildinfo is derived from the specfile which is currently on the source
3032 The returned data is XML and contains a list of the packages used in
3033 building, their source, and the expanded BuildRequires.
3035 The arguments REPOSITORY and ARCH can be taken from the first two columns
3036 of the 'osc repos' output.
3039 osc buildinfo REPOSITORY ARCH [BUILD_DESCR] (in pkg dir)
3040 osc buildinfo PROJECT PACKAGE REPOSITORY ARCH [BUILD_DESCR]
3045 args = slash_split(args)
3047 if len(args) < 2 and is_package_dir('.'):
3051 raise oscerr.WrongArgs('Too many arguments.')
3053 if len(args) < 4: # 2 or 3
3054 package = store_read_package(wd)
3055 project = store_read_project(wd)
3056 apiurl = store_read_apiurl(wd)
3057 repository = args[0]
3060 if len(args) > 3 and len(args) < 6: # 4 or 5
3061 apiurl = conf.config['apiurl']
3064 repository = args[2]
3066 # for following specfile detection ...
3070 # were we given a specfile (third argument)?
3072 spec = open(args[2]).read()
3076 print >>sys.stderr, e
3079 print ''.join(get_buildinfo(apiurl,
3080 project, package, repository, arch,
3082 addlist=opts.extra_pkgs))
3085 def do_buildconfig(self, subcmd, opts, *args):
3086 """${cmd_name}: Shows the build config
3088 Shows the build configuration which is used in building a package.
3089 This command is mostly used internally by the 'build' command.
3090 It needs to be called from inside a package directory.
3092 The returned data is the project-wide build configuration in a format
3093 which is directly readable by the build script. It contains RPM macros
3094 and BuildRequires expansions, for example.
3096 The arguments REPOSITORY and ARCH can be taken from the first two columns
3097 of the 'osc repos' output.
3100 osc buildconfig REPOSITORY ARCH (in pkg dir)
3101 osc buildconfig PROJECT PACKAGE REPOSITORY ARCH
3106 args = slash_split(args)
3108 if len(args) < 2 and is_package_dir('.'):
3112 raise oscerr.WrongArgs('Too many arguments.')