Build Service/osc plugins
From openSUSE
It is probably best shown by example.
Let's say, you want to write a script which looks in the Education:desktop project, and shows metadata of all packages inside this project.
You could do it like this:
#!/usr/bin/python
import os
project = 'Education:desktop'
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.
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 = 'Education:desktop'
# 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.
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.
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>".
There's more to it. It is extremely easy to add command line 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)
progress_meter = True
if opts.quiet:
progress_meter = False
for filename in filenames:
if os.path.exists('%s/%s' % (opts.destdir, filename)):
continue
targetfilename = '%s/%s' % (opts.destdir, filename)
get_binary_file(conf.config['apiurl'],
project, repository, architecture,
filename,
targetfilename=targetfilename,
package=opts.package,
progress_meter=progress_meter)
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
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.
And if you implemented a cool command, which you would like to see integrated in the upstream osc, please write to the buildservice-mailinglist now!

