openSUSE:OSC plugins
osc
module from external python scripts.
Simple example of standalone script
It is probably best shown by example.
Let's say, you want to write a script which looks in the openSUSE:Tools project, and shows metadata of all packages inside this project.
Naive approach by forking osc executable
You could do it like this:
#!/usr/bin/python import os project = 'openSUSE:Tools' def run(command): input, output = os.popen2(command) lines = output.readlines() return lines lines = run('osc ls ' + project) for line in lines: package = line.rstrip('\n') meta = run('osc meta pkg ' + project + ' ' + package) for line in meta: print line
But this way, you only call osc as external process. This means that osc is initializing each time, and that you cannot access internal capabilities of osc.
Using the osc API
Instead, you could write a script which imports the osc module, and uses that:
#!/usr/bin/python import os import osc.conf import osc.core project = 'openSUSE:Tools' # initialize osc configuration osc.conf.get_config() packages = osc.core.meta_get_packagelist(osc.conf.config['apiurl'], project) for package in packages: m = osc.core.show_package_meta(osc.conf.config['apiurl'], project, package) print ''.join(m)
This is easier.
Turning a standalone script into a plugin
Now, let's say you wrote something which could be more generally useful. Could it make sense to integrate the functionality as new osc command? If so, it is easy to do so. Custom osc commands can be thrown into ~/.osc-plugins or /var/lib/osc-plugins, and will be loaded by osc from there. The snippet from above could look like this:
def do_show_packages(self, subcmd, opts, project): """${cmd_name}: Show metadata of all packages in an project This command shows metadata of all packages in a given project. ${cmd_usage} ${cmd_option_list} """ packages = meta_get_packagelist(conf.config['apiurl'], project) for package in packages: m = show_package_meta(conf.config['apiurl'], project, package) print ''.join(m)
The file's name is important; it must end in .py. Other than that, no restrictions on how it's named, although it is of course wise for its name to match the command name it implements.
Debugging a plugin
When writing and debugging osc plugins, and something doesn't work, it is helpful to see where exactly you screw up. osc has a "--trace" option (you can also add "traceback=1" to your ~/.oscrc to activate it permanently). Additionally you may find the "--debugger" and "--post-mortem" options useful.
The method implemented must start with "do_". There is some other magic going on. A python module named cmdln (see pydoc osc.cmdln for more info) automatically takes care of documenting, argument checking, and more. After installing the plugin, if you call 'osc help', you will notice a new command in the long help output:
show_packages Show metadata of all packages in an project
The command is automatically documented:
% osc help show_packages show_packages: Show metadata of all packages in an project This command shows metadata of all packages in a given project. usage: osc show_packages PROJECT
and it can be called as "osc show_packages <prj>".
More complex example with CLI options
Here is a more complex example, which also shows command line stuff:
@cmdln.option('-q', '--quiet', action='store_true', help='do not show downloading progress') @cmdln.option('--package', metavar='PACKAGE', help='only binaries of this package') @cmdln.option('-d', '--destdir', default='.', metavar='DIR', help='destination directory') def do_mirror_binaries(self, subcmd, opts, project, repository, architecture): """${cmd_name}: Mirror binaries of a project to local directory It does download directly from the api server. Packages don't need to be "published" to be downloaded. ${cmd_usage} ${cmd_option_list} """ # Get package list filenames = get_binarylist(conf.config['apiurl'], project, repository, architecture, package=opts.package) if not os.path.isdir(opts.destdir): print "Creating %s" % opts.destdir os.makedirs(opts.destdir, 0755) for filename in filenames: if os.path.exists('%s/%s' % (opts.destdir, filename)): continue target_filename = '%s/%s' % (opts.destdir, filename) get_binary_file(conf.config['apiurl'], project, repository, architecture, filename, target_filename=target_filename, package=opts.package, progress_meter = not opts.quiet)
Help output will look like this:
% osc help mirror_binaries mirror_binaries: Mirror binaries of a project to local directory It does download directly from the api server. Packages don't need to be "published" to be downloaded. usage: osc mirror_binaries PROJECT REPOSITORY ARCHITECTURE options: -h, --help show this help message and exit -d DIR, --destdir=DIR destination directory --package=PACKAGE only binaries of this package -q, --quiet do not show downloading progress
More information
There's more to it. It is easy to add command line options to your command with python function decorators. This is best done by reviewing existing osc code with similar functionality. Browse the file "osc/commandline.py" found in the source tar-ball or git repository of osc, or inside <python-libdir>
To get an idea of the osc internal functions see
pydoc osc.commandline pydoc osc.core
for higher and lower level functions.
Plenty other examples can be found by looking inside <python-libdir>/osc/command.py.
This method is very well suited to override, or improve, any existing osc command. If you are not happy with the ls command, just write your own. Or if you have an idea how to improve it or add a new feature, copy and paste the command from osc.command.py to your osc plugin directory and start hacking on it.
There is one difference to note between a command in osc.command.py and a plugged-in command. As indentation matters to Python, you need to indent stuff 4 spaces if you copy it into osc.command.py, or remove 4 spaces the other way round. Other than that, simple copy&paste should do.
GNOME/OscGnome might serve as further example; it describes a more complex system based on osc plugins.
Existing plugins
osc plugins which already exist include:
You may find more by searching for osc-plugin on software.opensuse.org.
Contributing
If you implemented a cool command, which you would like to see integrated in the upstream osc, please write to the buildservice-mailinglist now!
To contribute to osc:
- Fork https://github.com/openSUSE/osc
- Post about your patch/idea/bugfix to the buildservice mailinglist (opensuse-buildservice_AT_opensuse.org).
- Contact adrian_AT_suse.de to get commit access. He'll also arrange commit mails.