4 # Copyright (C) 2006 Novell Inc. All rights reserved.
5 # This program is free software; it may be used, copied, modified
6 # and distributed under the terms of the GNU General Public Licence,
7 # either version 2, or (at your option) any later version.
16 class Osc(cmdln.Cmdln):
18 osc [GLOBALOPTS] SUBCOMMAND [OPTS] [ARGS...]
20 OpenSUSE build service command-line tool.
21 Type 'osc help <subcommand>' for help on a specific subcommand.
26 For additional information, see
27 * http://www.opensuse.org/Build_Service_Tutorial
28 * http://www.opensuse.org/Build_Service/CLI
30 You can modify osc commands, or roll you own, via the plugin API:
31 * http://www.opensuse.org/Build_Service/osc_plugins
37 def __init__(self, *args, **kwargs):
38 cmdln.Cmdln.__init__(self, *args, **kwargs)
39 cmdln.Cmdln.do_help.aliases.append('h')
42 def get_optparser(self):
43 """this is the parser for "global" options (not specific to subcommand)"""
45 optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
46 optparser.add_option('--debugger', action='store_true',
47 help='jump into the debugger before executing anything')
48 optparser.add_option('--post-mortem', action='store_true',
49 help='jump into the debugger in case of errors')
50 optparser.add_option('-t', '--traceback', action='store_true',
51 help='print call trace in case of errors')
52 optparser.add_option('-H', '--http-debug', action='store_true',
53 help='debug HTTP traffic')
54 optparser.add_option('-d', '--debug', action='store_true',
55 help='print info useful for debugging')
56 optparser.add_option('-A', '--apiurl', dest='apiurl',
58 help='specify URL to access API server at')
59 optparser.add_option('-c', '--config', dest='conffile',
61 help='specify alternate configuration file')
65 def postoptparse(self, try_again = True):
66 """merge commandline options into the config"""
68 conf.get_config(override_conffile = self.options.conffile,
69 override_apiurl = self.options.apiurl,
70 override_debug = self.options.debug,
71 override_http_debug = self.options.http_debug,
72 override_traceback = self.options.traceback,
73 override_post_mortem = self.options.post_mortem)
74 except oscerr.NoConfigfile, e:
75 print >>sys.stderr, e.msg
76 print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
79 config['user'] = raw_input('Username: ')
80 config['pass'] = getpass.getpass()
81 if self.options.apiurl:
82 config['apiurl'] = self.options.apiurl
84 conf.write_initial_config(e.file, config)
85 print >>sys.stderr, 'done'
86 if try_again: self.postoptparse(try_again = False)
87 except oscerr.ConfigMissingApiurl, e:
88 print >>sys.stderr, e.msg
90 user = raw_input('Username: ')
91 passwd = getpass.getpass()
92 cp = conf.get_configParser()
94 cp.set(e.url, 'user', user)
95 cp.set(e.url, 'pass', passwd)
96 file = open(e.file, 'w')
99 if try_again: self.postoptparse(try_again = False)
104 def get_cmd_help(self, cmdname):
105 doc = self._get_cmd_handler(cmdname).__doc__
106 doc = self._help_reindent(doc)
107 doc = self._help_preprocess(doc, cmdname)
108 doc = doc.rstrip() + '\n' # trim down trailing space
109 return self._str(doc)
112 def do_init(self, subcmd, opts, project, package=None):
113 """${cmd_name}: Initialize a directory as working copy
115 Initialize an existing directory to be a working copy of an
116 (already existing) buildservice project/package.
118 (This is the same as checking out a package and then copying sources
119 into the directory. It does NOT create a new package. To create a
120 package, use 'osc meta pkg ... ...')
122 You wouldn't normally use this command.
124 To get a working copy of a package (e.g. for building it or working on
125 it, you would normally use the checkout command. Use "osc help
126 checkout" to get help for it.
135 init_project_dir(conf.config['apiurl'], os.curdir, project)
136 print 'Initializing %s (Project: %s)' % (os.curdir, project)
138 init_package_dir(conf.config['apiurl'], project, package, os.path.curdir)
139 print 'Initializing %s (Project: %s, Package: %s)' % (os.curdir, project, package)
143 @cmdln.option('-a', '--arch', metavar='ARCH',
144 help='specify architecture')
145 @cmdln.option('-r', '--repo', metavar='REPO',
146 help='specify repository')
147 @cmdln.option('-b', '--binaries', action='store_true',
148 help='list built binaries, instead of sources')
149 @cmdln.option('-v', '--verbose', action='store_true',
150 help='print extra information')
151 def do_list(self, subcmd, opts, *args):
152 """${cmd_name}: List existing content on the server
154 This command is used to list sources, or binaries (when used with the
155 --binaries option). To use the --binary option, --repo and --arch are
159 ls # list all projects
160 ls Apache # list packages in a project
161 ls -b Apache # list all binaries of a project
162 ls Apache apache2 # list source files of package of a project
163 ls Apache apache2 <file> # list <file> if this file exists
164 ls -v Apache apache2 # verbosely list source files of package
166 With --verbose, the following fields will be shown for each item:
168 Revision number of the last commit
170 Date and time of the last commit
176 args = slash_split(args)
180 elif len(args) == 2 or len(args) == 3:
187 raise oscerr.WrongArgs('Too many arguments')
189 if opts.binaries and (not opts.repo or not opts.arch):
190 raise oscerr.WrongOptions('Sorry, -r <repo> -a <arch> missing\n'
191 'You can list repositories with: \'osc platforms <project>\'')
196 raise oscerr.WrongArgs('There are no binaries to list above project level.')
200 # sys.exit('The verbose option is not implemented for projects.')
201 r = get_binarylist(conf.config['apiurl'], project, opts.repo, opts.arch)
205 r = get_binarylist(conf.config['apiurl'], project, opts.repo, opts.arch, package=package)
209 elif not opts.binaries:
211 print '\n'.join(meta_get_project_list(conf.config['apiurl']))
215 raise oscerr.WrongOptions('Sorry, the --verbose option is not implemented for projects.')
217 print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
219 elif len(args) == 2 or len(args) == 3:
220 l = meta_get_filelist(conf.config['apiurl'],
223 verbose=opts.verbose)
225 out = [ '%s %7d %9d %s %s' % (i.md5, i.rev, i.size, shorttime(i.mtime), i.name) \
226 for i in l if not fname or fname == i.name ]
229 print 'file \'%s\' does not exist' % fname
237 print 'file \'%s\' does not exist' % fname
242 @cmdln.option('-F', '--file', metavar='FILE',
243 help='read metadata from FILE, instead of opening an editor. '
244 '\'-\' denotes standard input. ')
245 @cmdln.option('-e', '--edit', action='store_true',
246 help='edit metadata')
247 @cmdln.option('--delete', action='store_true',
248 help='delete a pattern file')
249 def do_meta(self, subcmd, opts, *args):
250 """${cmd_name}: Show meta information, or edit it
252 Show or edit build service metadata of type <prj|pkg|prjconf|user|pattern>.
254 This command displays metadata on buildservice objects like projects,
255 packages, or users. The type of metadata is specified by the word after
256 "meta", like e.g. "meta prj".
258 prj denotes metadata of a buildservice project.
259 prjconf denotes the (build) configuration of a project.
260 pkg denotes metadata of a buildservice package.
261 user denotes the metadata of a user.
262 pattern denotes installation patterns defined for a project.
264 To list patterns, use 'osc meta pattern PRJ'. An additional argument
265 will be the pattern file to view or edit.
267 With the --edit switch, the metadata can be edited. Per default, osc
268 opens the program specified by the environmental variable EDITOR with a
269 temporary file. Alternatively, content to be saved can be supplied via
270 the --file switch. If the argument is '-', input is taken from stdin:
271 osc meta prjconf home:user | sed ... | osc meta prjconf home:user -F -
273 When trying to edit a non-existing resource, it is created implicitely.
279 osc meta pkg PRJ PKG -e
282 osc meta <prj|pkg|prjconf|user|pattern> ARGS...
283 osc meta <prj|pkg|prjconf|user|pattern> -e|--edit ARGS...
284 osc meta <prj|pkg|prjconf|user|pattern> -F|--file ARGS...
285 osc meta pattern --delete PRJ PATTERN
289 args = slash_split(args)
291 if not args or args[0] not in metatypes.keys():
292 raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
293 % ', '.join(metatypes))
299 min_args, max_args = 2, 2
300 elif cmd in ['pattern']:
301 min_args, max_args = 1, 2
303 min_args, max_args = 1, 1
304 if len(args) < min_args:
305 raise oscerr.WrongArgs('Too few arguments.')
306 if len(args) > max_args:
307 raise oscerr.WrongArgs('Too many arguments.')
313 project, package = args[0:2]
314 elif cmd == 'prjconf':
318 elif cmd == 'pattern':
324 # enforce pattern argument if needed
325 if opts.edit or opts.file:
326 raise oscerr.WrongArgs('A pattern file argument is required.')
329 if not opts.edit and not opts.file and not opts.delete:
331 sys.stdout.write(''.join(show_project_meta(conf.config['apiurl'], project)))
333 sys.stdout.write(''.join(show_package_meta(conf.config['apiurl'], project, package)))
334 elif cmd == 'prjconf':
335 sys.stdout.write(''.join(show_project_conf(conf.config['apiurl'], project)))
337 r = get_user_meta(conf.config['apiurl'], user)
339 sys.stdout.write(''.join(r))
340 elif cmd == 'pattern':
342 r = show_pattern_meta(conf.config['apiurl'], project, pattern)
344 sys.stdout.write(''.join(r))
346 r = show_pattern_metalist(conf.config['apiurl'], project)
348 sys.stdout.write('\n'.join(r) + '\n')
351 if opts.edit and not opts.file:
353 edit_meta(metatype='prj',
355 path_args=quote_plus(project),
358 'user': conf.config['user']}))
360 edit_meta(metatype='pkg',
362 path_args=(quote_plus(project), quote_plus(package)),
365 'user': conf.config['user']}))
366 elif cmd == 'prjconf':
367 edit_meta(metatype='prjconf',
369 path_args=quote_plus(project),
372 edit_meta(metatype='user',
374 path_args=(quote_plus(user)),
375 template_args=({'user': user}))
376 elif cmd == 'pattern':
377 edit_meta(metatype='pattern',
379 path_args=(project, pattern),
389 f = open(opts.file).read()
391 sys.exit('could not open file \'%s\'.' % opts.file)
394 edit_meta(metatype='prj',
397 path_args=quote_plus(project))
399 edit_meta(metatype='pkg',
402 path_args=(quote_plus(project), quote_plus(package)))
403 elif cmd == 'prjconf':
404 edit_meta(metatype='prjconf',
407 path_args=quote_plus(project))
409 edit_meta(metatype='user',
412 path_args=(quote_plus(user)))
413 elif cmd == 'pattern':
414 edit_meta(metatype='pattern',
417 path_args=(project, pattern))
422 path = metatypes[cmd]['path']
424 path = path % (project, pattern)
425 u = makeurl(conf.config['apiurl'], [path])
428 sys.exit('The --delete switch is only for pattern metadata.')
432 @cmdln.option('-d', '--diff', action='store_true',
433 help='generate a diff')
434 @cmdln.option('-u', '--unified', action='store_true',
435 help='output the diff in the unified diff format')
436 @cmdln.option('-m', '--message', metavar='TEXT',
437 help='specify message TEXT')
438 @cmdln.option('-r', '--revision', metavar='REV',
439 help='for "create", specify a certain source revision ID (the md5 sum)')
440 @cmdln.option('--nodevelproject', action='store_true',
441 help='do not follow a defined devel project ' \
442 '(primary project where a package is developed)')
443 @cmdln.option('-s', '--state', default='new',
444 help='only list requests in one of the comma separated given states [default=new]')
446 @cmdln.alias("submitrequest")
447 def do_submitreq(self, subcmd, opts, *args):
448 """${cmd_name}: Handle requests to submit a package into another project
450 [See http://en.opensuse.org/Build_Service/Collaboration for information
453 For "create", there are two ways to use it. Either with a working copy
454 or without. If called with no arguments, osc will guess what to submit
455 where. If you don't have a working copy, you can give the respective
456 arguments on the commandline (see below for an example).
457 Then, the DESTPAC name is optional; the source packages' name will be
458 used if DESTPAC is omitted.
459 With --message, a message can be attached.
460 With --revision, a revision MD5 of a package can be specified which is
461 to be submitted. The default is to request submission of the currently
464 "list" lists open requests attached to a project or package.
466 "log" will show the history of the given ID
468 "show" will show the request itself, and generate a diff for review, if
469 used with the --diff option.
471 "decline" will change the request state to "declined" and append a
472 message that you specify with the --message option.
474 "delete" will permanently delete a request and append a
475 message that you specify with the --message option.
477 "revoke" will set the request state to "revoked" and append a
478 message that you specify with the --message option.
480 "accept" will change the request state to "accepted" and will trigger
481 the actual submit process. That would normally be a server-side copy of
482 the source package to the target package.
486 osc submitreq create [-m TEXT]
487 osc submitreq create [-m TEXT] SOURCEPRJ SOURCEPKG DESTPRJ [DESTPKG]
488 osc submitreq list [PRJ [PKG]]
490 osc submitreq show [-d] ID
491 osc submitreq accept [-m TEXT] ID
492 osc submitreq decline [-m TEXT] ID
493 osc submitreq delete [-m TEXT] ID
494 osc submitreq revoke [-m TEXT] ID
498 args = slash_split(args)
500 cmds = ['create', 'list', 'log', 'show', 'decline', 'accept', 'delete', 'revoke']
501 if not args or args[0] not in cmds:
502 raise oscerr.WrongArgs('Unknown submitreq action. Choose one of %s.' \
508 if cmd in ['create']:
509 min_args, max_args = 0, 4
510 elif cmd in ['list']:
511 min_args, max_args = 0, 2
513 min_args, max_args = 1, 1
514 if len(args) < min_args:
515 raise oscerr.WrongArgs('Too few arguments.')
516 if len(args) > max_args:
517 raise oscerr.WrongArgs('Too many arguments.')
519 apiurl = conf.config['apiurl']
521 # collect specific arguments
524 # try using the working copy at hand
525 p = findpacs(os.curdir)[0]
526 src_project = p.prjname
529 dst_project = p.linkinfo.project
530 dst_package = p.linkinfo.package
533 sys.exit('Package \'%s\' is not a source link, so I cannot guess the submit target.\n'
534 'Please provide it the target via commandline arguments.' % p.name)
537 # get the arguments from the commandline
538 src_project, src_package, dst_project = args[0:3]
540 dst_package = args[3]
542 dst_package = src_package
544 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
545 + self.get_cmd_help('submitreq'))
553 project = store_read_project(os.curdir)
554 apiurl = store_read_apiurl(os.curdir)
556 package = store_read_package(os.curdir)
557 except oscerr.NoWorkingCopy:
562 elif cmd in ['log', 'show', 'decline', 'accept', 'delete', 'revoke']:
568 if not opts.nodevelproject:
571 devloc = show_develproject(apiurl, dst_project, dst_package)
572 except urllib2.HTTPError:
573 print >>sys.stderr, """\
574 Warning: failed to fetch meta data for '%s' package '%s' (new package?) """ \
575 % (dst_project, dst_package)
579 and dst_project != devloc \
580 and src_project != devloc:
582 Sorry, but a different project, %s, is defined as the place where development
583 of the package %s primarily takes place.
584 Please submit there instead, or use --nodevelproject to force direct submission.""" \
585 % (devloc, dst_package)
587 reqs = get_submit_request_list(apiurl, dst_project, dst_package)
588 user = conf.get_apiurl_usr(apiurl)
589 myreqs = [ i for i in reqs if i.state.who == user ]
592 print 'You already created the following submitrequests: %s.' % \
593 ', '.join([str(i.reqid) for i in myreqs ])
594 repl = raw_input('Revoke the old requests? (y/N) ')
596 result = create_submit_request(apiurl,
597 src_project, src_package,
598 dst_project, dst_package,
599 opts.message, orev=opts.revision)
602 change_submit_request_state(apiurl, str(req.reqid), 'revoked',
603 'superseded by %s' % result)
605 print 'created request id', result
610 state_list = opts.state.split(',')
612 results = get_submit_request_list(apiurl,
613 project, package, state_list)
615 results.sort(reverse=True)
617 for result in results:
618 print result.list_view()
621 for l in get_submit_request_log(conf.config['apiurl'], reqid):
627 r = get_submit_request(conf.config['apiurl'], reqid)
629 # fixme: will inevitably fail if the given target doesn't exist
632 print server_diff(conf.config['apiurl'],
633 r.dst_project, r.dst_package, None,
634 r.src_project, r.src_package, r.src_md5, opts.unified)
635 except urllib2.HTTPError, e:
636 e.osc_msg = 'Diff not possible'
641 elif cmd == 'decline':
642 r = change_submit_request_state(conf.config['apiurl'],
643 reqid, 'declined', opts.message or '')
647 elif cmd == 'accept':
648 r = change_submit_request_state(conf.config['apiurl'],
649 reqid, 'accepted', opts.message or '')
652 elif cmd == 'delete':
653 r = change_submit_request_state(conf.config['apiurl'],
654 reqid, 'deleted', opts.message or '')
657 elif cmd == 'revoke':
658 r = change_submit_request_state(conf.config['apiurl'],
659 reqid, 'revoked', opts.message or '')
662 # editmeta and its aliases are all depracated
663 @cmdln.alias("editprj")
664 @cmdln.alias("createprj")
665 @cmdln.alias("editpac")
666 @cmdln.alias("createpac")
667 @cmdln.alias("edituser")
668 @cmdln.alias("usermeta")
669 def do_editmeta(self, subcmd, opts, *args):
672 Obsolete command to edit metadata. Use 'meta' now.
674 See the help output of 'meta'.
678 print >>sys.stderr, 'This command is obsolete. Use \'osc meta <metatype> ...\'.'
679 print >>sys.stderr, 'See \'osc help meta\'.'
680 #self.do_help([None, 'meta'])
684 @cmdln.option('-r', '--revision', metavar='rev',
685 help='link the specified revision.')
686 def do_linkpac(self, subcmd, opts, *args):
687 """${cmd_name}: "Link" a package to another package
689 A linked package is a clone of another package, but plus local
690 modifications. It can be cross-project.
692 The DESTPAC name is optional; the source packages' name will be used if
695 Afterwards, you will want to 'checkout DESTPRJ DESTPAC'.
697 To add a patch, add the patch as file and add it to the _link file.
698 You can also specify text which will be inserted at the top of the spec file.
700 See the examples in the _link file.
703 osc linkpac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
707 args = slash_split(args)
709 if not args or len(args) < 3:
710 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
711 + self.get_cmd_help('linkpac'))
713 rev, dummy = parseRevisionOption(opts.revision)
715 src_project = args[0]
716 src_package = args[1]
717 dst_project = args[2]
719 dst_package = args[3]
721 dst_package = src_package
723 if src_project == dst_project and src_package == dst_package:
724 print >>sys.stderr, 'Error: source and destination are the same.'
727 if rev and not checkRevision(src_project, src_package, rev):
728 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
731 link_pac(src_project, src_package, dst_project, dst_package, rev)
733 def do_aggregatepac(self, subcmd, opts, *args):
734 """${cmd_name}: "Aggregate" a package to another package
736 Aggregation of a package means that the build results (binaries) of a
737 package are basically copied into another project.
738 This can be used to make packages available from building that are
739 needed in a project but available only in a different project. Note
740 that this is done at the expense of disk space. See
741 http://en.opensuse.org/Build_Service/Tips_and_Tricks#_link_and__aggregate
742 for more information.
744 The DESTPAC name is optional; the source packages' name will be used if
748 osc aggregatepac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
752 args = slash_split(args)
754 if not args or len(args) < 3:
755 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
756 + self.get_cmd_help('aggregatepac'))
758 src_project = args[0]
759 src_package = args[1]
760 dst_project = args[2]
762 dst_package = args[3]
764 dst_package = src_package
766 if src_project == dst_project and src_package == dst_package:
767 print >>sys.stderr, 'Error: source and destination are the same.'
769 aggregate_pac(src_project, src_package, dst_project, dst_package)
772 @cmdln.option('-c', '--client-side-copy', action='store_true',
773 help='do a (slower) client-side copy')
774 @cmdln.option('-k', '--keep-maintainers', action='store_true',
775 help='keep original maintainers. Default is remove all and replace with the one calling the script.')
776 @cmdln.option('-t', '--to-apiurl', metavar='URL',
777 help='URL of destination api server. Default is the source api server.')
778 def do_copypac(self, subcmd, opts, *args):
779 """${cmd_name}: Copy a package
781 A way to copy package to somewhere else.
783 It can be done across buildservice instances, if the -t option is used.
784 In that case, a client-side copy is implied.
786 Using --client-side-copy always involves downloading all files, and
787 uploading them to the target.
789 The DESTPAC name is optional; the source packages' name will be used if
793 osc copypac SOURCEPRJ SOURCEPAC DESTPRJ [DESTPAC]
797 args = slash_split(args)
799 if not args or len(args) < 3:
800 raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
801 + self.get_cmd_help('copypac'))
803 src_project = args[0]
804 src_package = args[1]
805 dst_project = args[2]
807 dst_package = args[3]
809 dst_package = src_package
811 src_apiurl = conf.config['apiurl']
813 dst_apiurl = opts.to_apiurl
815 dst_apiurl = src_apiurl
817 if src_project == dst_project and \
818 src_package == dst_package and \
819 src_apiurl == dst_apiurl:
820 raise oscerr.WrongArgs('Source and destination are the same.')
822 if src_apiurl != dst_apiurl:
823 opts.client_side_copy = True
825 r = copy_pac(src_apiurl, src_project, src_package,
826 dst_apiurl, dst_project, dst_package,
827 client_side_copy=opts.client_side_copy,
828 keep_maintainers=opts.keep_maintainers)
832 @cmdln.option('--nodevelproject', action='store_true',
833 help='do not follow a defined devel project ' \
834 '(primary project where a package is developed)')
835 @cmdln.option('-r', '--revision', metavar='rev',
836 help='branch against a specific revision')
837 def do_branch(self, subcmd, opts, *args):
838 """${cmd_name}: Branch a package
840 [See http://en.opensuse.org/Build_Service/Collaboration for information
843 Create a source link from a package of an existing project to a new
844 subproject of the requesters home project (home:branches:)
846 The branched package will live in
847 home:USERNAME:branches:PROJECT/PACKAGE
850 osc branch SOURCEPRJ SOURCEPKG
854 args = slash_split(args)
856 raise oscerr.WrongArgs('Wrong number of arguments.')
858 r = branch_pkg(conf.config['apiurl'], args[0], args[1],
859 nodevelproject=opts.nodevelproject, rev=opts.revision)
861 expected = 'home:%s:branches:%s' % (conf.config['user'], args[0])
863 devloc = r.split('branches:')[1]
864 print '\nNote: The branch has been created of a different project,\n' \
866 ' which is the primary location of where development for\n' \
867 ' that package takes place.\n' \
868 ' That\'s also where you would normally make changes against.\n' \
869 ' A direct branch of the specified package can be forced\n' \
870 ' with the --nodevelproject option.\n' % devloc
872 print 'A working copy of the branched package can be checked out with:\n\n' \
877 def do_deletepac(self, subcmd, opts, *args):
878 """${cmd_name}: Delete packages on the repository server
881 osc deletepac PROJECT PACKAGE [PACKAGE ...]
885 args = slash_split(args)
888 raise oscerr.WrongArgs('Missing argument.')
891 # careful: if pkg is an empty string, the package delete request results
892 # into a project delete request - which works recursively...
894 delete_package(conf.config['apiurl'], args[0], pkg)
897 @cmdln.option('-f', '--force', action='store_true',
898 help='deletes a project and its packages')
899 def do_deleteprj(self, subcmd, opts, project):
900 """${cmd_name}: Delete a project on the repository server
902 As a safety measure, project must be empty (i.e., you need to delete all
903 packages first). If you are sure that you want to remove this project and all
904 its packages use \'--force\' switch.
911 if len(meta_get_packagelist(conf.config['apiurl'], project)) >= 1 and not opts.force:
912 print >>sys.stderr, 'Project contains packages. It must be empty before deleting it. ' \
913 'If you are sure that you want to remove this project and all its ' \
914 'packages use the \'--force\' switch'
917 delete_project(conf.config['apiurl'], project)
920 @cmdln.alias('metafromspec')
921 @cmdln.option('', '--specfile', metavar='FILE',
922 help='Path to specfile. (if you pass more than working copy this option is ignored)')
923 def do_updatepacmetafromspec(self, subcmd, opts, *args):
924 """${cmd_name}: Update package meta information from a specfile
926 ARG, if specified, is a package working copy.
932 args = parseargs(args)
933 if opts.specfile and (len(args) == 1):
934 specfile = opts.specfile
937 pacs = findpacs(args)
939 p.read_meta_from_spec(specfile)
940 p.update_package_meta()
944 @cmdln.option('-r', '--revision', metavar='rev1[:rev2]',
945 help='If rev1 is specified it will compare your working copy against '
946 'the revision (rev1) on the server. '
947 'If rev1 and rev2 are specified it will compare rev1 against rev2 '
948 '(NOTE: changes in your working copy are ignored in this case)')
949 @cmdln.option('-p', '--pretty', action='store_true',
950 help='output the diff in the pretty diff format')
951 def do_diff(self, subcmd, opts, *args):
952 """${cmd_name}: Generates a diff
954 Generates a diff, comparing local changes against the repository
957 ARG, specified, is a filename to include in the diff.
963 args = parseargs(args)
964 pacs = findpacs(args)
966 rev1, rev2 = parseRevisionOption(opts.revision)
970 diff += ''.join(make_diff(pac, rev1))
972 diff += server_diff(pac.apiurl, pac.prjname, pac.name, rev1,
973 pac.prjname, pac.name, rev2, not opts.pretty)
978 @cmdln.option('--oldprj', metavar='OLDPRJ',
979 help='project to compare against')
980 @cmdln.option('--oldpkg', metavar='OLDPKG',
981 help='package to compare against')
982 @cmdln.option('-r', '--revision', metavar='N[:M]',
983 help='revision id, where N = old revision and M = new revision')
984 @cmdln.option('-u', '--unified', action='store_true',
985 help='output the diff in the unified diff format')
986 def do_rdiff(self, subcmd, opts, new_project, new_package):
987 """${cmd_name}: Server-side "pretty" diff of two packages
989 If neither OLDPRJ nor OLDPKG are specified, the diff is against the
990 last revision, thus showing the latest change.
992 Note that this command doesn't reply a "normal" diff which can be
993 applied as patch, but a pretty diff, which also compares the content of
1004 old_revision, new_revision = parseRevisionOption(opts.revision)
1006 rdiff = server_diff(conf.config['apiurl'],
1007 opts.oldprj, opts.oldpkg, old_revision,
1008 new_project, new_package, new_revision, opts.unified)
1013 def do_repourls(self, subcmd, opts, *args):
1014 """${cmd_name}: Shows URLs of .repo files
1016 Shows URLs on which to access the project .repos files (yum-style
1017 metadata) on download.opensuse.org.
1019 ARG, if specified, is a package working copy.
1025 args = parseargs(args)
1026 pacs = findpacs(args)
1028 url_tmpl = 'http://download.opensuse.org/repositories/%s/%s/%s.repo'
1030 platforms = get_platforms_of_project(p.apiurl, p.prjname)
1031 for platform in platforms:
1032 print url_tmpl % (p.prjname.replace(':', ':/'), platform, p.prjname)
1036 @cmdln.option('-r', '--revision', metavar='rev',
1037 help='checkout the specified revision. '
1038 'NOTE: if you checkout the complete project '
1039 'this option is ignored!')
1040 @cmdln.option('-e', '--expand-link', action='store_true',
1041 help='if a package is a link, check out the expanded '
1042 'sources (no-op, since this became the default)')
1043 @cmdln.option('-u', '--unexpand-link', action='store_true',
1044 help='if a package is a link, check out the _link file ' \
1045 'instead of the expanded sources')
1047 def do_checkout(self, subcmd, opts, *args):
1048 """${cmd_name}: Check out content from the repository
1050 Check out content from the repository server, creating a local working
1053 When checking out a single package, the option --revision can be used
1054 to specify a revions of the package to be checked out.
1056 When a package is a source link, then it will be checked out in
1057 expanded form. If --unexpand-link option is used, the checkout will
1058 instead produce the raw _link file plus patches.
1062 osc co Apache # entire project
1063 osc co Apache apache2 # a package
1064 osc co Apache apache2 foo # single file -> to current dir
1067 osc co PROJECT [PACKAGE] [FILE]
1071 if opts.unexpand_link: expand_link = False
1072 else: expand_link = True
1074 args = slash_split(args)
1075 project = package = filename = None
1084 rev, dummy = parseRevisionOption(opts.revision)
1086 if rev and not checkRevision(project, package, rev):
1087 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1091 get_source_file(conf.config['apiurl'], project, package, filename, revision=rev)
1094 checkout_package(conf.config['apiurl'], project, package,
1095 rev, expand_link=expand_link, prj_dir=project)
1098 if os.path.exists(project):
1099 sys.exit('osc: project \'%s\' already exists' % project)
1101 # check if the project does exist (show_project_meta will throw an exception)
1102 show_project_meta(conf.config['apiurl'], project)
1104 init_project_dir(conf.config['apiurl'], project, project)
1105 print statfrmt('A', project)
1108 for package in meta_get_packagelist(conf.config['apiurl'], project):
1110 checkout_package(conf.config['apiurl'], project, package,
1111 expand_link=expand_link, prj_dir=project)
1112 except oscerr.LinkExpandError, e:
1113 print >>sys.stderr, 'Link cannot be expanded:\n', e
1114 # check out in unexpanded form at least
1115 checkout_package(conf.config['apiurl'], project, package,
1116 expand_link=False, prj_dir=project)
1119 raise oscerr.WrongArgs('Missing argument.\n\n' \
1120 + self.get_cmd_help('checkout'))
1123 @cmdln.option('-q', '--quiet', action='store_true',
1124 help='print as little as possible')
1125 @cmdln.option('-v', '--verbose', action='store_true',
1126 help='print extra information')
1128 def do_status(self, subcmd, opts, *args):
1129 """${cmd_name}: Show status of files in working copy
1131 Show the status of files in a local working copy, indicating whether
1132 files have been changed locally, deleted, added, ...
1134 The first column in the output specifies the status and is one of the
1135 following characters:
1136 ' ' no modifications
1141 '?' item is not under version control
1142 '!' item is missing (removed by non-svn command) or incomplete
1147 osc st file1 file2 ...
1150 osc status [OPTS] [PATH...]
1154 args = parseargs(args)
1156 # storage for single Package() objects
1158 # storage for a project dir ( { prj_instance : [ package objects ] } )
1161 # when 'status' is run inside a project dir, it should
1162 # stat all packages existing in the wc
1163 if is_project_dir(arg):
1164 prj = Project(arg, False)
1166 if conf.config['do_package_tracking']:
1168 for pac in prj.pacs_have:
1169 # we cannot create package objects if the dir does not exist
1170 if not pac in prj.pacs_broken:
1171 prjpacs[prj].append(os.path.join(arg, pac))
1173 pacpaths += [arg + '/' + n for n in prj.pacs_have]
1174 elif is_package_dir(arg):
1175 pacpaths.append(arg)
1176 elif os.path.isfile(arg):
1177 pacpaths.append(arg)
1179 msg = '\'%s\' is neither a project or a package directory' % arg
1180 raise oscerr.NoWorkingCopy, msg
1182 # process single packages
1183 lines = getStatus(findpacs(pacpaths), None, opts.verbose, opts.quiet)
1184 # process project dirs
1185 for prj, pacs in prjpacs.iteritems():
1186 lines += getStatus(findpacs(pacs), prj, opts.verbose, opts.quiet)
1188 print '\n'.join(lines)
1191 def do_add(self, subcmd, opts, *args):
1192 """${cmd_name}: Mark files to be added upon the next commit
1195 osc add FILE [FILE...]
1199 raise oscerr.WrongArgs('Missing argument.\n\n' \
1200 + self.get_cmd_help('add'))
1202 filenames = parseargs(args)
1206 def do_mkpac(self, subcmd, opts, *args):
1207 """${cmd_name}: Create a new package under version control
1210 osc mkpac new_package
1213 if not conf.config['do_package_tracking']:
1214 print >>sys.stderr, "to use this feature you have to enable \'do_package_tracking\' " \
1215 "in the [general] section in the configuration file"
1219 raise oscerr.WrongArgs('Wrong number of arguments.')
1221 createPackageDir(args[0])
1223 @cmdln.option('-r', '--recursive', action='store_true',
1224 help='If CWD is a project dir then scan all package dirs as well')
1225 def do_addremove(self, subcmd, opts, *args):
1226 """${cmd_name}: Adds new files, removes disappeared files
1228 Adds all files new in the local copy, and removes all disappeared files.
1230 ARG, if specified, is a package working copy.
1236 args = parseargs(args)
1238 for arg in arg_list:
1239 if is_project_dir(arg):
1240 prj = Project(arg, False)
1241 for pac in prj.pacs_unvers:
1242 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
1243 if os.path.isdir(pac_dir):
1244 addFiles([pac_dir], prj)
1245 for pac in prj.pacs_broken:
1246 if prj.get_state(pac) != 'D':
1247 prj.set_state(pac, 'D')
1248 print statfrmt('D', getTransActPath(os.path.join(prj.dir, pac)))
1250 for pac in prj.pacs_have:
1251 state = prj.get_state(pac)
1252 if state != None and state != 'D':
1253 pac_dir = getTransActPath(os.path.join(prj.dir, pac))
1254 args.append(pac_dir)
1256 prj.write_packages()
1258 pacs = findpacs(args)
1261 p.todo = p.filenamelist + p.filenamelist_unvers
1263 for filename in p.todo:
1264 if os.path.isdir(filename):
1266 # ignore foo.rXX, foo.mine for files which are in 'C' state
1267 if os.path.splitext(filename)[0] in p.in_conflict:
1269 state = p.status(filename)
1272 print statfrmt('A', getTransActPath(os.path.join(p.dir, filename)))
1274 p.put_on_deletelist(filename)
1275 p.write_deletelist()
1276 os.unlink(os.path.join(p.storedir, filename))
1277 print statfrmt('D', getTransActPath(os.path.join(p.dir, filename)))
1282 @cmdln.alias('checkin')
1283 @cmdln.option('-m', '--message', metavar='TEXT',
1284 help='specify log message TEXT')
1285 @cmdln.option('-F', '--file', metavar='FILE',
1286 help='read log message from FILE')
1287 def do_commit(self, subcmd, opts, *args):
1288 """${cmd_name}: Upload content to the repository server
1290 Upload content which is changed in your working copy, to the repository
1294 osc ci # current dir
1296 osc ci file1 file2 ...
1302 args = parseargs(args)
1309 msg = open(opts.file).read()
1311 sys.exit('could not open file \'%s\'.' % opts.file)
1314 for arg in arg_list:
1315 if conf.config['do_package_tracking'] and is_project_dir(arg):
1316 Project(arg).commit(msg=msg)
1319 pacs = findpacs(args)
1322 # open editor for commit message
1323 # but first, produce status and diff to append to the template
1326 changed = getStatus([pac], quiet=True)
1329 diffs += ['\nDiff for working copy: %s' % pac.dir]
1330 diffs += make_diff(pac, 0)
1331 # if footer is empty, there is nothing to commit, and no edit needed.
1333 msg = edit_message(footer='\n'.join(footer))
1335 if conf.config['do_package_tracking'] and len(pacs) > 0:
1339 # it is possible to commit packages from different projects at the same
1340 # time: iterate over all pacs and put each pac to the right project in the dict
1342 path = os.path.normpath(os.path.join(pac.dir, os.pardir))
1343 if is_project_dir(path):
1344 pac_path = os.path.basename(os.path.normpath(pac.absdir))
1345 prj_paths.setdefault(path, []).append(pac_path)
1346 files[pac_path] = pac.todo
1348 single_paths.append(pac.dir)
1349 for prj, packages in prj_paths.iteritems():
1350 Project(prj).commit(tuple(packages), msg, files)
1351 for pac in single_paths:
1352 Package(pac).commit(msg)
1358 @cmdln.option('-r', '--revision', metavar='REV',
1359 help='update to specified revision (this option will be ignored '
1360 'if you are going to update the complete project or more than '
1362 @cmdln.option('-u', '--unexpand-link', action='store_true',
1363 help='if a package is an expanded link, update to the raw _link file')
1364 @cmdln.option('-e', '--expand-link', action='store_true',
1365 help='if a package is a link, update to the expanded sources')
1367 def do_update(self, subcmd, opts, *args):
1368 """${cmd_name}: Update a working copy
1373 If the current working directory is a package, update it.
1374 If the directory is a project directory, update all contained
1375 packages, AND check out newly added packages.
1377 To update only checked out packages, without checking out new
1378 ones, you might want to use "osc up *" from within the project
1382 Update the packages specified by the path argument(s)
1384 When --expand-link is used with source link packages, the expanded
1385 sources will be checked out. Without this option, the _link file and
1386 patches will be checked out. The option --unexpand-link can be used to
1387 switch back to the "raw" source with a _link file plus patch(es).
1393 args = parseargs(args)
1396 for arg in arg_list:
1398 if is_project_dir(arg):
1401 if conf.config['do_package_tracking']:
1402 prj.update(expand_link=opts.expand_link,
1403 unexpand_link=opts.unexpand_link)
1406 # if not tracking package, and 'update' is run inside a project dir,
1407 # it should do the following:
1408 # (a) update all packages
1409 args += prj.pacs_have
1410 # (b) fetch new packages
1411 prj.checkout_missing_pacs()
1415 pacs = findpacs(args)
1417 if (opts.expand_link and opts.unexpand_link) \
1418 or (opts.expand_link and opts.revision) \
1419 or (opts.unexpand_link and opts.revision):
1420 raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
1421 '--revision are mutually exclusive.')
1423 if opts.revision and ( len(args) == 1):
1424 rev, dummy = parseRevisionOption(opts.revision)
1425 if not checkRevision(pacs[0].prjname, pacs[0].name, rev, pacs[0].apiurl):
1426 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
1433 print 'Updating %s' % p.name
1436 if opts.expand_link and p.islink() and not p.isexpanded():
1437 print 'Expanding to rev', p.linkinfo.xsrcmd5
1438 rev = p.linkinfo.xsrcmd5
1439 elif opts.unexpand_link and p.islink() and p.isexpanded():
1440 print 'Unexpanding to rev', p.linkinfo.lsrcmd5
1441 rev = p.linkinfo.lsrcmd5
1442 elif p.islink() and p.isexpanded():
1443 rev = show_upstream_xsrcmd5(p.apiurl,
1446 # FIXME: ugly workaround for #399247
1447 if opts.expand_link or opts.unexpand_link:
1448 if [ i for i in p.filenamelist+p.filenamelist_unvers if p.status(i) != ' ' ]:
1449 print >>sys.stderr, 'osc: cannot expand/unexpand because your working ' \
1450 'copy has local modifications. Please remove them ' \
1457 @cmdln.option('-f', '--force', action='store_true',
1458 help='forces removal of package')
1461 @cmdln.alias('remove')
1462 def do_delete(self, subcmd, opts, *args):
1463 """${cmd_name}: Mark files to be deleted upon the next 'checkin'
1466 osc rm FILE [FILE...]
1471 raise oscerr.WrongArgs('Missing argument.\n\n' \
1472 + self.get_cmd_help('delete'))
1474 args = parseargs(args)
1475 # check if args contains a package which was removed by
1476 # a non-osc command and mark it with the 'D'-state
1479 if not os.path.exists(i):
1480 prj_dir, pac_dir = getPrjPacPaths(i)
1481 if is_project_dir(prj_dir):
1482 prj = Project(prj_dir, False)
1483 if i in prj.pacs_broken:
1484 if prj.get_state(i) != 'A':
1485 prj.set_state(pac_dir, 'D')
1487 prj.del_package_node(i)
1488 print statfrmt('D', getTransActPath(i))
1490 prj.write_packages()
1491 pacs = findpacs(args)
1495 prj_dir, pac_dir = getPrjPacPaths(p.absdir)
1496 if conf.config['do_package_tracking'] and is_project_dir(prj_dir):
1497 prj = Project(prj_dir, False)
1498 prj.delPackage(p, opts.force)
1500 pathn = getTransActPath(p.dir)
1501 for filename in p.todo:
1502 if filename not in p.filenamelist:
1503 sys.exit('\'%s\' is not under version control' % filename)
1504 p.put_on_deletelist(filename)
1505 p.write_deletelist()
1506 p.delete_source_file(filename)
1507 print statfrmt('D', os.path.join(pathn, filename))
1510 def do_resolved(self, subcmd, opts, *args):
1511 """${cmd_name}: Remove 'conflicted' state on working copy files
1513 If an upstream change can't be merged automatically, a file is put into
1514 in 'conflicted' ('C') state. Within the file, conflicts are marked with
1515 special <<<<<<< as well as ======== and >>>>>>> lines.
1517 After manually resolving all conflicting parts, use this command to
1518 remove the 'conflicted' state.
1520 Note: this subcommand does not semantically resolve conflicts or
1521 remove conflict markers; it merely removes the conflict-related
1522 artifact files and allows PATH to be committed again.
1525 osc resolved FILE [FILE...]
1530 raise oscerr.WrongArgs('Missing argument.\n\n' \
1531 + self.get_cmd_help('resolved'))
1533 args = parseargs(args)
1534 pacs = findpacs(args)
1538 for filename in p.todo:
1539 print 'Resolved conflicted state of "%s"' % filename
1540 p.clear_from_conflictlist(filename)
1543 def do_platforms(self, subcmd, opts, *args):
1544 """${cmd_name}: Shows available platforms
1548 Shows all available platforms/build targets
1550 2. osc platforms <project>
1551 Shows the configured platforms/build targets of a project
1559 print '\n'.join(get_platforms_of_project(conf.config['apiurl'], project))
1561 print '\n'.join(get_platforms(conf.config['apiurl']))
1564 def do_results_meta(self, subcmd, opts, *args):
1565 """${cmd_name}: Shows raw build results of a package
1567 Shows the build results of the package in raw XML.
1569 ARG, if specified, is the working copy of a package.
1575 args = parseargs(args)
1576 pacs = findpacs(args)
1579 print ''.join(show_results_meta(pac.apiurl, pac.prjname, package=pac.name))
1583 @cmdln.option('-l', '--last-build', action='store_true',
1584 help='show last build results (succeeded/failed/unknown)')
1585 def do_results(self, subcmd, opts, *args):
1586 """${cmd_name}: Shows the build results of a package
1588 ARG, if specified, is the working copy of a package.
1594 args = parseargs(args)
1595 pacs = findpacs(args)
1598 print '\n'.join(get_results(pac.apiurl, pac.prjname, pac.name, opts.last_build))
1601 @cmdln.option('-l', '--last-build', action='store_true',
1602 help='show last build results (succeeded/failed/unknown)')
1603 def do_rresults(self, subcmd, opts, prj, pkg):
1604 """${cmd_name}: Shows the build results of a remote package
1608 osc rresults <remote project name> <remote package name>
1614 apiurl = conf.config['apiurl']
1615 print '\n'.join(get_results(apiurl, prj, pkg, opts.last_build))
1618 @cmdln.option('-q', '--hide-legend', action='store_true',
1619 help='hide the legend')
1620 @cmdln.option('-c', '--csv', action='store_true',
1622 @cmdln.option('-s', '--status-filter', metavar='STATUS',
1623 help='show only packages with buildstatus STATUS (see legend)')
1624 @cmdln.option('-n', '--name-filter', metavar='EXPR',
1625 help='show only packages whose names match EXPR')
1626 @cmdln.option('-p', '--project', metavar='PROJECT',
1627 help='show packages in project PROJECT')
1630 def do_prjresults(self, subcmd, opts, *args):
1631 """${cmd_name}: Shows project-wide build results
1635 1. osc prjresults <dir>
1636 dir is a project or package directory
1639 the project is guessed from the current dir
1641 3. osc prjresults --project=<project>
1642 the project is specified from the command line
1648 if args and len(args) > 1:
1649 print >>sys.stderr, 'getting results for more than one project is not supported'
1653 project = opts.project
1654 apiurl = conf.config['apiurl']
1661 project = store_read_project(wd)
1662 apiurl = store_read_apiurl(wd)
1664 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))
1667 @cmdln.option('-q', '--hide-legend', action='store_true',
1668 help='hide the legend')
1669 @cmdln.option('-c', '--csv', action='store_true',
1671 @cmdln.option('-s', '--status-filter', metavar='STATUS',
1672 help='show only packages with buildstatus STATUS (see legend)')
1673 @cmdln.option('-n', '--name-filter', metavar='EXPR',
1674 help='show only packages whose names match EXPR')
1676 def do_rprjresults(self, subcmd, opts, prj):
1677 """${cmd_name}: Shows project-wide build results of remote Projects
1681 osc rprjresults <remote project name>
1687 apiurl = conf.config['apiurl']
1689 print '\n'.join(get_prj_results(apiurl, prj, hide_legend=opts.hide_legend, csv=opts.csv, status_filter=opts.status_filter, name_filter=opts.name_filter))
1693 def do_buildlog(self, subcmd, opts, platform, arch):
1694 """${cmd_name}: Shows the build log of a package
1696 Shows the log file of the build of a package. Can be used to follow the
1697 log while it is being written.
1698 Needs to be called from within a package directory.
1700 The arguments PLATFORM and ARCH are the first two columns in the 'osc
1708 package = store_read_package(wd)
1709 project = store_read_project(wd)
1710 apiurl = store_read_apiurl(wd)
1712 print_buildlog(apiurl, project, package, platform, arch)
1716 def do_remotebuildlog(self, subcmd, opts, *args):
1717 """${cmd_name}: Shows the build log of a package
1719 Shows the log file of the build of a package. Can be used to follow the
1720 log while it is being written.
1723 osc remotebuildlog project package platform arch
1725 osc remotebuildlog project/package/platform/arch
1728 args = slash_split(args)
1730 raise oscerr.WrongArgs('Too few arguments.')
1732 raise oscerr.WrongArgs('Too many arguments.')
1734 print_buildlog(conf.config['apiurl'], *args)
1737 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
1738 help='Add this package when computing the buildinfo')
1739 def do_buildinfo(self, subcmd, opts, *args):
1740 """${cmd_name}: Shows the build info
1742 Shows the build "info" which is used in building a package.
1743 This command is mostly used internally by the 'build' subcommand.
1744 It needs to be called from within a package directory.
1746 The BUILD_DESCR argument is optional. BUILD_DESCR is a local RPM specfile
1747 or Debian "dsc" file. If specified, it is sent to the server, and the
1748 buildinfo will be based on it. If the argument is not supplied, the
1749 buildinfo is derived from the specfile which is currently on the source
1752 The returned data is XML and contains a list of the packages used in
1753 building, their source, and the expanded BuildRequires.
1755 The arguments PLATFORM and ARCH can be taken from first two columns
1756 of the 'osc repos' output.
1759 osc buildinfo PLATFORM ARCH [BUILD_DESCR]
1764 package = store_read_package(wd)
1765 project = store_read_project(wd)
1766 apiurl = store_read_apiurl(wd)
1768 if args is None or len(args) < 2:
1769 print 'Valid arguments for this package are:'
1771 self.do_repos(None, None)
1773 raise oscerr.WrongArgs('Missing argument')
1778 # were we given a specfile (third argument)?
1780 spec = open(args[2]).read()
1784 print >>sys.stderr, e
1787 print ''.join(get_buildinfo(apiurl,
1788 project, package, platform, arch,
1790 addlist=opts.extra_pkgs))
1793 def do_buildconfig(self, subcmd, opts, platform, arch):
1794 """${cmd_name}: Shows the build config
1796 Shows the build configuration which is used in building a package.
1797 This command is mostly used internally by the 'build' command.
1798 It needs to be called from inside a package directory.
1800 The returned data is the project-wide build configuration in a format
1801 which is directly readable by the build script. It contains RPM macros
1802 and BuildRequires expansions, for example.
1804 The arguments PLATFORM and ARCH can be taken first two columns in the
1812 package = store_read_package(wd)
1813 project = store_read_project(wd)
1814 apiurl = store_read_apiurl(wd)
1816 print ''.join(get_buildconfig(apiurl, project, package, platform, arch))
1819 def do_repos(self, subcmd, opts, *args):
1820 """${cmd_name}: Shows the repositories which are defined for a package or a project
1822 ARG, if specified, is a package working copy or a project dir.
1824 examples: 1. osc repos # project/package = current dir
1825 2. osc repos <packagedir>
1826 3. osc repos <projectdir>
1832 args = parseargs(args)
1835 for platform in get_repos_of_project(store_read_apiurl(arg), store_read_project(arg)):
1839 @cmdln.option('--clean', action='store_true',
1840 help='Delete old build root before initializing it')
1841 @cmdln.option('--no-changelog', action='store_true',
1842 help='don\'t update the package changelog from a changes file')
1843 @cmdln.option('--noinit', '--no-init', action='store_true',
1844 help='Skip initialization of build root and start with build immediately.')
1845 @cmdln.option('--nochecks', '--no-checks', action='store_true',
1846 help='Do not run post build checks on the resulting packages.')
1847 @cmdln.option('--no-verify', action='store_true',
1848 help='Skip signature verification of packages used for build.')
1849 @cmdln.option('-p', '--prefer-pkgs', metavar='DIR', action='append',
1850 help='Prefer packages from this directory when installing the build-root')
1851 @cmdln.option('-k', '--keep-pkgs', metavar='DIR',
1852 help='Save built packages into this directory')
1853 @cmdln.option('-x', '--extra-pkgs', metavar='PAC', action='append',
1854 help='Add this package when installing the build-root')
1855 @cmdln.option('-j', '--jobs', metavar='N',
1856 help='Compile with N jobs')
1857 @cmdln.option('--icecream', metavar='N',
1858 help='use N parallel build jobs with icecream')
1859 @cmdln.option('--ccache', action='store_true',
1860 help='use ccache to speed up rebuilds')
1861 @cmdln.option('--with', metavar='X', dest='_with',
1862 help='enable feature X for build')
1863 @cmdln.option('--without', metavar='X',
1864 help='disable feature X for build')
1865 # will not work as build.py does not support proper quoting
1866 # @cmdln.option('--define', metavar='\'X Y\'',
1867 # help='define macro X with value Y')
1868 @cmdln.option('--userootforbuild', action='store_true',
1869 help='Run build as root. The default is to build as '
1870 'unprivileged user. Note that a line "# norootforbuild" '
1871 'in the spec file will invalidate this option.')
1872 @cmdln.option('', '--local-package', action='store_true',
1873 help='build a package which does not exist on the server')
1874 @cmdln.option('', '--alternative-project', metavar='PROJECT',
1875 help='specify the build target project')
1876 @cmdln.option('-d', '--debuginfo', action='store_true',
1877 help='also build debuginfo sub-packages')
1878 @cmdln.option('-b', '--baselibs', action='store_true',
1879 help='Create -32bit/-64bit/-x86 rpms for other architectures')
1880 def do_build(self, subcmd, opts, *args):
1881 """${cmd_name}: Build a package on your local machine
1883 You need to call the command inside a package directory, which should be a
1884 buildsystem checkout. (Local modifications are fine.)
1886 The arguments PLATFORM and ARCH can be taken from first two columns
1887 of the 'osc repos' output. BUILD_DESCR is either a RPM spec file, or a
1890 The command honours packagecachedir and build-root settings in .oscrc,
1891 if present. You may want to set su-wrapper = 'sudo' in .oscrc, and
1892 configure sudo with option NOPASSWD for /usr/bin/build.
1894 If neither --clean nor --noinit is given, build will reuse an existing
1895 build-root again, removing unneeded packages and add missing ones. This
1896 is usually the fastest option.
1898 If the package doesn't exist on the server please use the --local-package
1900 If the project of the package doesn't exist on the server please use the
1901 --alternative-project <alternative-project> option:
1903 osc build [OPTS] --alternative-project openSUSE:10.3 standard i586 BUILD_DESCR
1906 osc build [OPTS] PLATFORM ARCH BUILD_DESCR
1907 osc build [OPTS] PLATFORM (ARCH = hostarch, BUILD_DESCR is detected automatically)
1908 osc build [OPTS] ARCH (PLATFORM = build_platform (config option), BUILD_DESCR is detected automatically)
1909 osc build [OPTS] BUILD_DESCR (PLATFORM = build_platform (config option), ARCH = hostarch)
1910 osc build [OPTS] (PLATFORM = build_platform (config option), ARCH = hostarch, BUILD_DESCR is detected automatically)
1913 # Configuration can be overridden by envvars, e.g.
1914 # OSC_SU_WRAPPER overrides the setting of su-wrapper.
1915 # OSC_BUILD_ROOT overrides the setting of build-root.
1916 # OSC_PACKAGECACHEDIR overrides the setting of packagecachedir.
1923 if not os.path.exists('/usr/lib/build/debtransform') \
1924 and not os.path.exists('/usr/lib/lbuild/debtransform'):
1925 sys.stderr.write('Error: you need build.rpm with version 2007.3.12 or newer.\n')
1926 sys.stderr.write('See http://download.opensuse.org/repositories/openSUSE:/Tools/\n')
1930 raise oscerr.WrongArgs('Too many arguments')
1932 arg_arch = arg_platform = arg_descr = None
1935 if arg.endswith('.spec') or arg.endswith('.dsc'):
1938 if arg in osc.build.can_also_build.get(osc.build.hostarch, []) or \
1939 arg in osc.build.hostarch:
1941 elif not arg_platform:
1944 raise oscerr.WrongArgs('unexpected argument: \'%s\'' % arg)
1946 arg_platform, arg_arch, arg_descr = args
1948 arg_arch = arg_arch or osc.build.hostarch
1949 arg_platform = arg_platform or conf.config['build_platform']
1950 descr = [ i for i in os.listdir('.') if i.endswith('.spec') or i.endswith('.dsc') ]
1952 # * request repos from server and select by build type.
1953 if not arg_descr and len(descr) == 1:
1954 arg_descr = descr[0]
1956 msg = 'Missing argument: build description (spec, dsc or kiwi file)'
1959 if p.islink() and not p.isexpanded():
1960 msg += ' (this package is not expanded - you might want to try osc up --expand)'
1963 raise oscerr.WrongArgs(msg)
1965 args = (arg_platform, arg_arch, arg_descr)
1967 if opts.prefer_pkgs:
1968 for d in opts.prefer_pkgs:
1969 if not os.path.isdir(d):
1970 print >> sys.stderr, 'Preferred package location \'%s\' is not a directory' % d
1974 if not os.path.isdir(opts.keep_pkgs):
1975 print >> sys.stderr, 'Preferred save location \'%s\' is not a directory' % opts.keep_pkgs
1978 print 'Building %s for %s/%s' % (arg_descr, arg_platform, arg_arch)
1979 return osc.build.main(opts, args)
1983 @cmdln.alias('buildhist')
1984 def do_buildhistory(self, subcmd, opts, platform, arch):
1985 """${cmd_name}: Shows the build history of a package
1987 The arguments PLATFORM and ARCH can be taken from first two columns
1988 of the 'osc repos' output.
1995 package = store_read_package(wd)
1996 project = store_read_project(wd)
1997 apiurl = store_read_apiurl(wd)
1999 print '\n'.join(get_buildhistory(apiurl, project, package, platform, arch))
2001 @cmdln.alias('jobhist')
2002 def do_jobhistory(self, subcmd, opts, platform, arch):
2003 """${cmd_name}: Shows the job history of a project
2005 The arguments PLATFORM and ARCH can be taken from first two columns
2006 of the 'osc repos' output.
2013 project = store_read_project(wd)
2016 package = store_read_package(wd)
2019 apiurl = store_read_apiurl(wd)
2021 print_jobhistory(apiurl, project, package, platform, arch)
2024 @cmdln.option('-r', '--revision', metavar='rev',
2025 help='show log of the specified revision')
2026 def do_log(self, subcmd, opts):
2027 """${cmd_name}: Shows the commit log of a package
2034 package = store_read_package(wd)
2035 project = store_read_project(wd)
2036 apiurl = store_read_apiurl(wd)
2037 rev, dummy = parseRevisionOption(opts.revision)
2038 if rev and not checkRevision(project, package, rev, apiurl):
2039 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2042 print '\n'.join(get_commitlog(apiurl, project, package, rev))
2045 @cmdln.option('-r', '--revision', metavar='rev',
2046 help='show log of the specified revision')
2047 def do_rlog(self, subcmd, opts, prj, pkg):
2048 """${cmd_name}: Shows the commit log of a remote package
2054 apiurl = conf.config['apiurl']
2055 rev, dummy = parseRevisionOption(opts.revision)
2056 if rev and not checkRevision(prj, pkg, rev, apiurl):
2057 print >>sys.stderr, 'Revision \'%s\' does not exist' % rev
2060 print '\n'.join(get_commitlog(apiurl, prj, pkg, rev))
2063 @cmdln.option('-f', '--failed', action='store_true',
2064 help='rebuild all failed packages')
2065 def do_rebuildpac(self, subcmd, opts, *args):
2066 """${cmd_name}: Triggers package rebuilds
2068 With the optional <repo> and <arch> arguments, the rebuild can be limited
2069 to a certain repository or architecture.
2071 Note that it is normally NOT needed to kick off rebuilds like this, because
2072 they principally happen in a fully automatic way, triggered by source
2073 check-ins. In particular, the order in which packages are built is handled
2074 by the build service.
2076 Note the --failed option, which can be used to rebuild all failed
2079 The arguments PLATFORM and ARCH are as in the first two columns of the
2083 osc rebuildpac PROJECT [PACKAGE [PLATFORM [ARCH]]]
2087 args = slash_split(args)
2090 raise oscerr.WrongArgs('Missing argument.')
2092 package = repo = arch = code = None
2104 print rebuild(conf.config['apiurl'], project, package, repo, arch, code)
2107 def do_info(self, subcmd, opts, *args):
2108 """${cmd_name}: Print information about a working copy
2110 Print information about each ARG (default: '.')
2111 ARG is a working-copy path.
2117 args = parseargs(args)
2118 pacs = findpacs(args)
2125 @cmdln.option('-a', '--arch', metavar='ARCH',
2126 help='Abort builds for a specific architecture')
2127 @cmdln.option('-r', '--repo', metavar='REPO',
2128 help='Abort builds for a specific repository')
2129 def do_abortbuild(self, subcmd, opts, *args):
2130 """${cmd_name}: Aborts the build of a certain project/package
2132 With the optional argument <package> you can specify a certain package
2133 otherwise all builds in the project will be cancelled.
2136 osc abortbuild [OPTS] PROJECT [PACKAGE]
2141 raise oscerr.WrongArgs('Missing <project> argument.')
2148 print abortbuild(conf.config['apiurl'], args[0], package, opts.arch, opts.repo)
2151 @cmdln.option('-a', '--arch', metavar='ARCH',
2152 help='Delete all binary packages for a specific architecture')
2153 @cmdln.option('-r', '--repo', metavar='REPO',
2154 help='Delete all binary packages for a specific repository')
2155 @cmdln.option('--build-disabled', action='store_true',
2156 help='Delete all binaries of packages for which the build is disabled')
2157 @cmdln.option('--build-failed', action='store_true',
2158 help='Delete all binaries of packages for which the build failed')
2159 @cmdln.option('--broken', action='store_true',
2160 help='Delete all binaries of packages for which the package source is bad')
2161 @cmdln.option('--expansion', action='store_true',
2162 help='Delete all binaries of packages which have expansion errors')
2163 def do_wipebinaries(self, subcmd, opts, *args):
2164 """${cmd_name}: Delete all binary packages of a certain project/package
2166 With the optional argument <package> you can specify a certain package
2167 otherwise all binary packages in the project will be deleted.
2170 osc wipebinaries [OPTS] PROJECT [PACKAGE]
2174 args = slash_split(args)
2177 raise oscerr.WrongArgs('Missing <project> argument.')
2179 raise oscerr.WrongArgs('Wrong number of arguments.')
2187 if opts.build_disabled:
2188 codes.append('disabled')
2189 if opts.build_failed:
2190 codes.append('failed')
2192 codes.append('broken')
2194 codes.append('expansion error')
2199 # make a new request for each code= parameter
2201 print wipebinaries(conf.config['apiurl'], args[0], package, opts.arch, opts.repo, code)
2204 @cmdln.option('-q', '--quiet', action='store_true',
2205 help='do not show downloading progress')
2206 @cmdln.option('-d', '--destdir', default='.', metavar='DIR',
2207 help='destination directory')
2208 @cmdln.option('--sources', action="store_true",
2209 help='also fetch source packages')
2210 def do_getbinaries(self, subcmd, opts, project, package, repository, architecture):
2211 """${cmd_name}: Download binaries to a local directory
2213 This command downloads packages directly from the api server.
2214 Thus, it directly accesses the packages that are used for building
2215 others even when they are not "published" yet.
2222 binaries = get_binarylist(conf.config['apiurl'],
2223 project, repository, architecture,
2224 package = package, verbose=True)
2226 if not os.path.isdir(opts.destdir):
2227 print "Creating %s" % opts.destdir
2228 os.makedirs(opts.destdir, 0755)
2231 sys.exit('no binaries found. Either the package does not '
2232 'exist or no binaries have been built.')
2234 for binary in binaries:
2237 if not opts.sources and binary.name.endswith('.src.rpm'):
2240 target_filename = '%s/%s' % (opts.destdir, binary.name)
2242 if os.path.exists(target_filename):
2243 st = os.stat(target_filename)
2244 if st.st_mtime == binary.mtime and st.st_size == binary.size:
2247 get_binary_file(conf.config['apiurl'],
2249 repository, architecture,
2252 target_filename = target_filename,
2253 target_mtime = binary.mtime,
2254 progress_meter = not opts.quiet)
2258 @cmdln.option('--repos-baseurl', action='store_true',
2259 help='show base URLs of download repositories')
2260 @cmdln.option('-e', '--enable-exact', action='store_true',
2261 help='show only exact matches')
2262 @cmdln.option('--package', action='store_true',
2263 help='search for a package')
2264 @cmdln.option('--project', action='store_true',
2265 help='search for a project')
2266 @cmdln.option('--title', action='store_true',
2267 help='search for matches in the \'title\' element')
2268 @cmdln.option('--description', action='store_true',
2269 help='search for matches in the \'description\' element')
2270 @cmdln.option('-v', '--verbose', action='store_true',
2271 help='show more information')
2272 @cmdln.option('-i', '--involved', action='store_true',
2273 help='show involved projects/packages')
2274 def do_search(self, subcmd, opts, *args):
2275 """${cmd_name}: Search for a project and/or package.
2277 If no option is specified osc will search for projects and
2278 packages which contains the \'search term\' in their name,
2279 title or description.
2282 osc search \'search term\' <options>
2288 raise oscerr.WrongArgs('Too many arguments.')
2289 elif len(args) < 1 and not opts.involved:
2290 raise oscerr.WrongArgs('Too few arguments.')
2291 elif len(args) == 1:
2292 search_term = args[0]
2294 if (opts.title or opts.description) and opts.involved:
2295 raise oscerr.WrongArgs('Sorry, the options \'--title\' and/or \'--description\' ' \
2296 'plus \'--involved\' are mutual exclusive')
2301 search_list.append('title')
2302 if opts.description:
2303 search_list.append('description')
2305 search_list.append('@name')
2306 search_for.append('package')
2308 search_list.append('@name')
2309 search_for.append('project')
2311 search_list = [ 'person/@userid' ]
2312 search_term = search_term or conf.get_apiurl_usr(conf.config['apiurl'])
2313 opts.enable_exact = True
2316 search_list = ['title', 'description', '@name']
2318 search_for = [ 'project', 'package' ]
2319 for kind in search_for:
2320 result = search(conf.config['apiurl'], set(search_list), kind, search_term, opts.verbose, opts.enable_exact, opts.repos_baseurl)
2322 if kind == 'package':
2323 headline = [ '# Package', '# Project' ]
2325 headline = [ '# Project' ]
2327 headline.append('# Title')
2328 if opts.repos_baseurl:
2329 headline.append('# URL')
2330 if len(search_for) > 1:
2332 print 'matches for \'%s\' in %ss:\n' % (search_term, kind)
2333 for row in build_table(len(headline), result, headline, 2):
2336 print 'No matches found for \'%s\' in %ss' % (search_term, kind)
2339 @cmdln.option('-p', '--project', metavar='project',
2340 help='specify a project name')
2341 @cmdln.option('-n', '--name', metavar='name',
2342 help='specify a package name')
2343 @cmdln.option('-t', '--title', metavar='title',
2345 @cmdln.option('-d', '--description', metavar='description',
2346 help='set the description of the package')
2347 @cmdln.option('', '--delete-old-files', action='store_true',
2348 help='delete existing files from the server')
2349 @cmdln.option('-c', '--commit', action='store_true',
2350 help='commit the new files')
2351 def do_importsrcpkg(self, subcmd, opts, srpm):
2352 """${cmd_name}: Import a new package from a src.rpm
2354 A new package dir will be created inside the project dir
2355 (if no project is specified and the current working dir is a
2356 project dir the package will be created in this project). If
2357 the package does not exist on the server it will be created
2358 too otherwise the meta data of the existing package will be
2359 updated (<title /> and <description />).
2360 The src.rpm will be extracted into the package dir. The files
2361 won't be committed unless you explicitly pass the --commit switch.
2363 SRPM is the path of the src.rpm in the local filesystem,
2371 if opts.delete_old_files and conf.config['do_package_tracking']:
2372 # IMHO the --delete-old-files option doesn't really fit into our
2373 # package tracking strategy
2374 print >>sys.stderr, '--delete-old-files is not supported anymore'
2375 print >>sys.stderr, 'when do_package_tracking is enabled'
2379 print 'trying to fetch', srpm
2381 urlgrabber.urlgrab(srpm)
2382 srpm = os.path.basename(srpm)
2384 srpm = os.path.abspath(srpm)
2385 if not os.path.isfile(srpm):
2386 print >>sys.stderr, 'file \'%s\' does not exist' % srpm
2390 project_dir = opts.project
2392 project_dir = os.curdir
2394 if conf.config['do_package_tracking']:
2395 project = Project(project_dir)
2397 project = store_read_project(project_dir)
2399 rpm_data = data_from_rpm(srpm, 'Name:', 'Summary:', '%description', 'Url:')
2401 title, pac, descr, url = ( v for k, v in rpm_data.iteritems() )
2403 title = pac = descr = url = ''
2409 if opts.description:
2410 descr = opts.description
2412 # title and description can be empty
2414 print >>sys.stderr, 'please specify a package name with the \'--name\' option. ' \
2415 'The automatic detection failed'
2418 olddir = os.getcwd()
2419 if conf.config['do_package_tracking']:
2420 if createPackageDir(os.path.join(project.dir, pac), project):
2421 os.chdir(os.path.join(project.dir, pac))
2425 if not os.path.exists(os.path.join(project_dir, pac)):
2426 apiurl = store_read_apiurl(project_dir)
2427 user = conf.get_apiurl_usr(apiurl)
2428 data = meta_exists(metatype='pkg',
2429 path_args=(quote_plus(project), quote_plus(pac)),
2432 'user': user}), apiurl=apiurl)
2434 data = ET.fromstring(''.join(data))
2435 data.find('title').text = title
2436 data.find('description').text = ''.join(descr)
2437 data.find('url').text = url
2438 data = ET.tostring(data)
2440 print >>sys.stderr, 'error - cannot get meta data'
2442 edit_meta(metatype='pkg',
2443 path_args=(quote_plus(project), quote_plus(pac)),
2444 data = data, apiurl=apiurl)
2445 os.mkdir(os.path.join(project_dir, pac))
2446 os.chdir(os.path.join(project_dir, pac))
2447 init_package_dir(apiurl, project, pac, os.path.join(project, pac))
2449 print >>sys.stderr, 'error - local package already exists'
2452 unpack_srcrpm(srpm, os.getcwd())
2453 p = Package(os.getcwd())
2454 if len(p.filenamelist) == 0 and opts.commit:
2455 print 'Adding files to working copy...'
2456 addFiles(glob.glob('*'))
2457 if conf.config['do_package_tracking']:
2459 project.commit((pac, ))
2461 p.update_datastructs()
2463 elif opts.commit and opts.delete_old_files:
2464 for file in p.filenamelist:
2465 p.delete_remote_source_file(file)
2466 p.update_local_filesmeta()
2467 print 'Adding files to working copy...'
2468 addFiles(glob.glob('*'))
2469 p.update_datastructs()
2472 print 'No files were committed to the server. Please ' \
2473 'commit them manually.'
2474 print 'Package \'%s\' only imported locally' % pac
2477 print 'Package \'%s\' imported successfully' % pac
2480 @cmdln.option('-m', '--method', default='GET', metavar='HTTP_METHOD',
2481 help='specify HTTP method to use (GET|PUT|DELETE|POST)')
2482 @cmdln.option('-d', '--data', default=None, metavar='STRING',
2483 help='specify string data for e.g. POST')
2484 @cmdln.option('-f', '--file', default=None, metavar='FILE',
2485 help='specify filename for e.g. PUT or DELETE')
2486 @cmdln.option('-a', '--add-header', default=None, metavar='NAME STRING',
2487 nargs=2, action='append', dest='headers',
2488 help='add the specified header to the request')
2489 def do_req(self, subcmd, opts, url):
2490 """${cmd_name}: Issue an arbitrary request to the API
2494 URL can be specified either partially (only the path component), or fully
2495 with URL scheme and hostname ('http://...').
2497 Note the global -A and -H options (see osc help).
2500 osc req /source/home:user
2501 osc req -m PUT -f /etc/fstab source/home:user/test5/myfstab
2507 if not opts.method in ['GET', 'PUT', 'POST', 'DELETE']:
2508 sys.exit('unknown method %s' % opts.method)
2510 if not url.startswith('http'):
2511 if not url.startswith('/'):
2513 url = conf.config['apiurl'] + url
2516 opts.headers = dict(opts.headers)
2518 r = http_request(opts.method,
2522 headers=opts.headers)
2525 sys.stdout.write(out)
2528 @cmdln.option('-e', '--email', action='store_true',
2529 help='show email addresses instead of user names')
2530 @cmdln.option('-v', '--verbose', action='store_true',
2531 help='show more information')
2532 @cmdln.option('-D', '--devel-project', metavar='devel_project',
2533 help='define the project where this package is primarily developed')
2534 @cmdln.option('-a', '--add', metavar='user',
2535 help='add a new maintainer')
2536 @cmdln.option('-d', '--delete', metavar='user',
2537 help='delete a maintainer from a project or package')
2538 def do_maintainer(self, subcmd, opts, *args):
2539 """${cmd_name}: Show maintainers of a project/package
2541 To be used like this:
2543 osc maintainer PRJ <options>
2545 osc maintainer PRJ PKG <options>
2553 m = show_project_meta(conf.config['apiurl'], args[0])
2555 elif len(args) == 2:
2556 m = show_package_meta(conf.config['apiurl'], args[0], args[1])
2560 raise oscerr.WrongArgs('I need at least one argument.')
2564 tree = ET.parse(StringIO(''.join(m)))
2565 for person in tree.findall('person'):
2566 maintainers.append(person.get('userid'))
2570 for maintainer in maintainers:
2571 user = get_user_data(conf.config['apiurl'], maintainer, 'email')
2573 emails.append(''.join(user))
2574 print ', '.join(emails)
2577 for maintainer in maintainers:
2578 user = get_user_data(conf.config['apiurl'], maintainer, 'realname', 'login', 'email')
2581 userdata.append(itm)
2582 for row in build_table(3, userdata, ['realname', 'userid', 'email\n']):
2585 addMaintainer(conf.config['apiurl'], prj, pac, opts.add)
2587 delMaintainer(conf.config['apiurl'], prj, pac, opts.delete)
2588 elif opts.devel_project:
2589 addDevelProject(conf.config['apiurl'], prj, pac, opts.devel_project)
2592 print ', '.join(maintainers)
2595 @cmdln.option('-r', '--revision', metavar='rev',
2596 help='print out the specified revision')
2597 def do_cat(self, subcmd, opts, *args):
2598 """${cmd_name}: Output the content of a file to standard output
2601 osc cat project package file
2602 osc cat project/package/file
2608 args = slash_split(args)
2610 raise oscerr.WrongArgs('Wrong number of arguments.')
2611 rev, dummy = parseRevisionOption(opts.revision)
2614 (fd, filename) = tempfile.mkstemp(prefix = 'osc_%s.' % args[2], dir = '/tmp')
2616 get_source_file(conf.config['apiurl'], args[0], args[1], args[2],
2617 targetfilename=filename, revision=rev)
2619 if binary_file(filename):
2620 print >>sys.stderr, 'error - cannot display binary file \'%s\'' % args[2]
2622 for line in open(filename):
2623 print line.rstrip('\n')
2630 ###############################################################################
2632 # load subcommands plugged-in locally
2633 plugin_dirs = ['/var/lib/osc-plugins', os.path.expanduser('~/.osc-plugins')]
2634 for plugin_dir in plugin_dirs:
2635 if os.path.isdir(plugin_dir):
2636 for extfile in os.listdir(plugin_dir):
2637 if not extfile.endswith('.py'):
2639 exec open(os.path.join(plugin_dir, extfile))