Category Archives: Sinzui

Restoring network to lxc and juju local-provider

I have experienced two cases where lxc containers stop working or new containers never work because they cannot join the network. My LTS container started, but without network, there were a lot of things I couldn’t do in it. In the case of Juju, I couldn’t deploy new local services. The unit agent status was stuck in PENDING.

You can verify the network is broken by fancy listing the lxc containers you started either by lxc-start or juju deploy:

sudo lxc-ls --fancy

<container> RUNNING -    -    NO

This shows that the running container doesn’t have IPV4 or IPV6 networks.

For each existing container that needs fixing, you need to install the new dhcp packages, but without a network, you cannot do it from a running container. Instead chroot can be used to update the container’s root file system.

cd /var/lib/lxc/<container>/rootfs/
sudo chroot ./
sudo apt-get update
apt-get install isc-dhcp-common isc-dhcp-client

To ensure that new containers work, you need to clear the lxc cache. The lxc image cache was built when you created or deployed your first container. The images there are probably more than a few months old.

sudo ls -lh /var/cache/lxc/precise/
sudo ls -lh /var/cache/lxc/cloud-precise/
sudo rm -r /var/cache/lxc/*

Lxc will get the new images the next time it needs to create a container. The first call will be slower to complete since it will build a fresh cache.

Restoring grub after OS X Mavericks

I had a scary moment after I updated my dual boot MacBookAir to Mavericks. Refit started Ubuntu, but grub was showing me the rescue prompt. This was a different misadventure from my upgrade to Mountain Lion; the partitions were there, but grub was lost.

I use refit to manage dual boot. I remembered from past experience that I needed to use the partition tool from the refit boot screen to sync the GPT and EFI tables. The task was done in seconds.

I knew Ubuntu was on the 4th partition (suggested by the GPT output from the sync), but to be sure I listed the partitions, then listed the 4th one.

ls (hd0,gpt4)/boot

and I saw the boot images and a grub dir. I then assumed, all my data was in place, but the MBR was lost during the Mavericks install. To start Ubuntu, I typed:

set root=(hd0,gpt4)
set prefix=(hd0,gpt4)/boot/grub
insmod normal

Ubuntu started up as normal. The grub screen was identical to my last boot. I was confident grub’s configuration was fine, I just needed to restore grub to the MBR. I opened a terminal after logging in and typed:

sudo grub-install --force /dev/sda

With hesitation, I rebooted. All was back to normal.

Managing Juju Charm Versions

It is difficult for Juju charm authors to support forked charms. dev-ops often don’t realise they are forking a charm when they add files that they want the charm to deploy. We cannot assume that the deployed charm’s revision (or the version control revision number) will ever match what the author released. I use Bazaar tags to mark the versions I support and help identify the true version of a deployed charm.

Juju uses the charm’s “revision” file to store the version. The number in this file must be incremented to deploy additions or changes to the charm. Upgrading a deployed charm requires a higher revision number. Organisations commonly create a local charm repo (managed by a version control system like Bazaar) to freeze the versions they deploy. These known version are stable; this is a best practice when working with charms that change often. The local branch will get commits that are not in the branch published at Launchpad. The revision numbers are meaningless between the two branches.

I tag my charm branches with the charm revision number. When a dev-op forks my charm to add it to the local charm repo, we can compare branch tags. For example, I tagged lp:~charming-devs/charms/precise/elasticsearch/trunk r39 as elasticsearch-29 to match the number in the revision file. I can ask the dev-op which tags the local branch has. I can quickly identify missing releases. I can ask for a diff from the tag I last released to help diagnose problems with the changes.

For example, several issues were reported about my elasticsearch charm. I had addressed the problems months before. I suggested the dev-ops upgrade to the latest version. Since their version numbers were higher than my versions, they did not upgrade. Tags are more precise. I can suggest the dev-ops merge “-r tag:elasticsearch-29″.

Another case, charms may need to change often if the application is deploys is under active development. A recent deploy of charmworld failed; the charm upgrade errored. The dev-op and I couldn’t reconcile the version numbers between what was deployed and what I released. We had to read a diff of the charm’s directories and files to discover the deployed charm was missing several releases–the charm was incompatible with the deployed code. If I had tagged my release, we could have identified the issue in minutes.

Common mistakes that broke Launchpad’s browser compatibility

At this moment, Launchpad is providing all features to Opera, Internet Explorer (IE), and many other browsers. Many features like bug listings, bug tag auto-completion, changing bug status and privacy, choosing people, and editing subscriptions were broken in many browsers. Some features were broken for years because developers mistook their inexperience for something broken these browsers.

I am summarising what was wrong with Launchpad and how to avoid repeating the mistakes of the past. Launchpad reviewers, I urge you to read this so that bad code is not merged into Launchpad’s trunk again. Launchpad engineers commonly work with Firefox and Chromium, two browsers with great support the latests technologies, These browsers are also very good at correcting mistakes in markup, CSS, and JavaScript. The lesser browsers are less forgiving; developers must fix their mistakes and on rare occasion write alternate code to make a feature work. Launchpad developers were too quick to blame the browser. They failed to recognise their own inexperience, and they failed to find the root cause of the problem.

Why Do We Want to Support More Browsers?

One user, lazarus, responded to my A tale of two travesties post asking “why bother with IE? Those 4% of users should switch to a *DECENT* browser.”

Canonical’s partners often run older releases of Firefox and Chromium. Some partners are running IE. Their staff cannot install software onto their computers, they have no choice but to use the corporate browser. The people who help build Ubuntu, who help Canonical enable Ubuntu to run on new hardware, cannot use Launchpad to do their job.

Consider users who use their phone or tablet with Launchpad. These browsers are also locked down, and they are commonly older versions of browsers. There were several bugs in the past that prevented Canonical staff from using their phones to login to Launchpad. They, even myself, had to find another computer to respond to a notification received via phone. Mobiles devices are now commonly used to work on the Net; Launchpad risks loosing users and projects who can choose another service.

Servers often do not have a GUI. We assume Ubuntu server is running headless. Servers also tend to be LTS installs that might can be 2 to 5 years old. Tasks such as reporting a bugs, updating it, and adding a comment must work in a text-based browser without JavaScript.

What is the Cost of Supporting More Browsers?

Again lazarus stated in his reply to my struggle to run IE in Ubuntu, ”I’m really sick of seeing all those work-arounds just to make it work for that damn browser.”

I wrote less than 20 lines of code to Support IE, yet I removed more than than 125 lines that disabled it or other browsers. I Also removed about 200 lines of duplicate or alternate code from <noscript> elements. It takes fewer lines of code to support older and mediocre browsers than to not support them!

For every bug that was tagged as affecting IE, I discovered it also affected Opera, Konqueror, Firefox 3, and even the latest version of chromium. Fixing one bug solved an issue for multiple browsers each time. I found many bugs overlapped with or were duplicates of other bugs that were listed as a higher priority, yet remained unsolved. Understanding the root cause of a problem allows us to make a single change that fixes many things. Honestly, the most common way to fix an issue was to read the YUI documentation to learn what Launchpad was doing wrong. Thus, the root cause of IE not working is rarely IE itself…the JavaScript bugs in Launchpad are caused by inexperience.

Running other browsers can be difficult as I reported in my Travesty post. I now have a custom apache configuration that cannot be copied to other developer machines. Running Microsoft’s official IE developer images take all my available RAM. I also run the VM image from an SD card because Windows is still bloatware. Installing Konqueror and all the KDE dependencies can also be a problem if you are short on disk space.

The maintenance savings by writing good code that supports all browsers are diminished by the days or weeks it takes to install all the browsers to reproduce a bug. If I cannot reproduce a bug, I cannot fix it. For many Launchpad developers, the best way to not fix a bug in a mediocre browser is to avoid introducing the bug. This is why we use YUI as our JS library, it reconciles the differences between browsers. This is why we have code standards and reviews, to ensure that we agree the code is correct and maintainable. We want to recognise bad code before it is merged into trunk and we absolutely must avoid dangerous code,

The Last Sign of Inexperience

This example of code is dangerous and unmaintainable:

if ( {

This is the last sign of inexperience. Bugs have already been slipped past review, merged into trunk, and are probably in production preventing user from completing their task. Why is IE not using the feature? Which browser versions and YUI versions were tested? This kind of guard often has an associated comment like:

# Usual IE brokenness.

I think the comment can also be interpreted to mean: “I do not have IE installed, I do not trust it, I do not trust my own code, I do not understand YUI”.

Do not let this kind of guard merge. If one browser does not work, there are probably 5 other browsers, like those shipped with an Ubuntu LTS, that also do not work. This branch needs fixing!

Is This a Presentation Problem?

Several bugs fixed by William Grant and myself were not JavaScript bugs as was reported. They were CSS or markup bugs. These bugs were trivially fixed in minutes when shown to the right person, yet some of these bugs were reported years ago. Your chances of fixing a presentation problem with JavaScript is similar to painting a house with a hammer. If the UI for a JavaScript feature does not look right, or not render at all, consider a CSS or markup change.

Non-conformant CSS the most common cause of broken presentation. I often uses Wikipedia’s Comparison of Layout Engines when researching differences in layout. Even the latest versions of Firefox and Chromium have inherited defects from their layout engine. Firefox was showing a scrollbar because the form overlay buttons had a text-indent property set to twice the width of the page. Though Chromium did not show the scrollbar, I think Firefox was right in this case; I fixed the issue by setting a text indent that was a few characters wide. Many issues can be fixed using alternate properties or additional CSS selectors to add properties to the problem elements. For example, IE has poor support for visibility (introduced by Netscape in 1998), but has better support for display (introduced by Microsoft in 1998). IE was always showing the fields in the overlay forms that change the page. Though the form overlays were visibility: hidden, IE requires a specific selector for .yui3-lazr-formoverlay-hidden input to be display: none.

Experimental CSS can be both the cause and the solution to a display problem. We want Launchpad to look good, and we often use CSS3 to define the styles. Many CSS3 properties are not fully implemented in browsers, but there are vendor properties (ones with a dash prefix) that do support the presentation. Launchpad has many of these for Gecko and Webkit. We also need to add them for KHtml, Opera and IE. While IE does not support the CSS3 box-shadow property, it has supported shadows since 1998 and we added its vendor property to give all overlays shadows.

Invalid Markup breaks older browsers, but newer browsers are better at fixing (and hiding) markup problems. You can read about how browsers work in depth, but I think I can summarise the issue in a few sentences. When browsers discover an error while parsing the markup, they pause to fix the markup then carry on. For example, paragraphs cannot contain lists. Most browsers assume that the author meant that there is a paragraph before and after the list. If you query the DOM using JS, you may discover it does not match the source. When I switched the Launchpad test suite to use a webkit-based test browser many tests failed. Developers were ignoring test failures because they passed in Firefox. I traced the errors to existing bug reports and discovered code was creating invalid tables and Chromium fixed the markup differently than Firefox. IE is very prone to choke on malformed tables. The lesson is clear, write what you mean, or else browsers are free to reinterpret your bogus markup and topple your scripts.

Launchpad users will soon loose their last line of defence against invalid markup when Launchpad switches to Ubuntu 12.04. My squad stopped development a few months ago to learn why Launchpad’s ec2 test runner failed a test that passed on all the other test runners. The reason is because the script used bad markup that toppled the Ubuntu 10.04 version of my test browser. The new webkit library in Ubuntu 12.04 is better at correcting markup, hiding from mistakes from tests, allowing the mistakes to be released to break older browsers.

Is This a Structural Problem?

Structural problems are issues where the choice in markup is brittle or unmaintainable.

Noscript is BAD. Netscape meant well by introducing the <noscript> element, but it leads to maintenance burdens and it assumes that all scripts run perfectly. The intent is to have two implementations and do not diverge. They do diverge though, leading to differing and confusing behaviours. If the script fails to initialise, the user gets no behaviour. This situation was exacerbated in Launchpad with the use of the if ( guards that meant IE users never saw the HTML or JavaScript links to change project roles or change blueprints. Launchpad prefers, and YUI recommends, progressive enhancement; write simple markup that does not need JavaScript, then modify the page to add the scripted features. Scripts that replace or hide the HTML feature should do that as the last step; If the script fails, there is still a usable link.

Mutating parent markup while parsing is dangerous. Several widgets like the TextAreaEditor were mutating their parent element before the parent element had closed itself. Only the mad and the brave mutate the thing they are iterating over. Chromium and Firefox handled the insane situation with grace, but other browsers threw a wobbly. IE 8 could not render the bug page, it stopped at the bug description. This situation is hard to see in a review. In general the script block must be outside of the elements it changes, but several widgets enhance parent and grandparent nodes, which means the markup appears to have redundantly nested elements. Some of this madness can be averted by running the script on DOMReady. There are many scripts that load as the page is parsed; I suspect these could all be changed to run at DOMReady.

Is This a YUI Problem?

YUI also has bugs. Has this issue been reported in YUI’s bug tracker? Which version of YUI is Launchpad using? Does upgrading fix the issue? Maybe an issue needs to be reported?

There were several hacks in the Launchpad bug pages to load scripts during parsing or after the load event because DOMReady was buggy during YUI3 preview releases. Both Opera and IE had difficulty during running scripts during parse time, and running the scripts after the load event caused the page to flash. Launchpad has been running YUI3+ for almost two years, the hacks should have been removed. The code required a proper XXX comment to remind developers to update the scripts.

# XXX: 2010-03-12 bingo bug=234567:
# DOMReady is bug in YUI3 PR3. Test DOMReady again in YUI 3.0.

There was also a case where Launchpad ran a version of YUI that miswired the event handling in IE. Several if ( guards were added without a comment explaining the issue, nor a stating the bug number in YUI’s bug tracker. I do not know when when these issues were fixed, but they are fixed in YUI 3.3.

Is This a EcmaScript or DOM Problem?

Launchpad was created in the age of EcmaScript 3 (ES3). This is now the age of ES5, and Launchpad developers are using ES5 browsers like Firefox and Chromium. Browser that the developers once used to build launchpad stopped working. Launchpad stopped working with the browsers available to Ubuntu LTS such as 08.04. Launchpad developers use jslint and YUI to ensure that code works in old and new browsers.

Was the lint report ignored? Launchpad’s make lint command uses jslint to prevent known issues before they are merged into trunk. There are still a lot is issues reported in the old lazr modules that need fixing. We fixed coercion mistakes and unnested functions created in loops to address bugs in older browsers.

Which version of EcmaScript? There were a lot of failures caused by unsafe ES5 calls. William fixed many script failures in older browsers by ensuring that Y.Array() is called properly. It is very important to understand that Y.Array() returns a native array. This call is pointless:

var finklesnarks = Y.Array([]);

This call is dangerous because it creates a native ES3 array in older browsers, then calls an ES5 method:


I was the author of some of these bad calls. The safe way to use ES5 array with YUI is:

Y.Array.indexOf(finklesnarks, fnord);

Are events happening in a different order? Are links doing both script actions and standard actions? YUI docs describe event bubbling, capturing is never mentioned. I thought this meant that YUI had disabled the capture phase of event propagation, simplifying how we work with events. No, this is not the case. Firefox and Chromium still prefer capture propagation, and IE uses bubbling propagation exclusively. Actions that work in the former browsers often have secondary, and wrong, actions happening in IE. This is because Firefox and Chromium are done propagating, but IE is just starting. Most Launchpad click handlers use e.preventDefault(), which means “stop the default action, continue propagating”. IE does continue propagating and unexpected things happen. A lot of Launchpad links work more by accident rather than by design. The correct method to call is e.halt() which stops the default action and propagation. We want to use halt() most of the time. In cases where we want multiple subscribers to respond to an event, we are using a custom event.

Does YUI docs demonstrate that the code will work? This is an odd point to check, but a lot of code was merged into Launchpad trunk without regard to YUI documentation. The bug status widgets did not appear on click in some browsers because they are designed to only display if you used the left mouse button. But YUI documentation states that the button can only be known in the mouseup event…the mouse property explicitly states that it is not for use with click. Removing the unsupported check of event.button fixed a bug that has been open for 3 years.

Is This a Broken Browser Problem?

These are the cases where developers must write code to reconcile the differences between browser implementations, or choose to not support the browser.

Non-conformant surprises are cases where browsers claim do support a feature but do not. Launchpad new bug listing UI depends on history management to sort columns and show batches. History management depends on URLs, but IE’s Link.pathname is missing the leading slash so URLs were not relative from root. The fix is trivial once you see the invalid paths in Launchpad’s oops reports and then look at the code that makes URLs. As we do in Launchpad’s python code, the method that wraps the pathname ensures there is a leading slash.

Alternate API nuisances are cases where the browser doesn’t support the methods the script uses, and neither does YUI. The bug tag autocomplete feature never worked in IE, but there was code checking for it. I first refactored the method to check for the methods Launchpad prefers, then checks for the alternate methods. This was hard to fix, I spent an hour reading about how IE creates selection ranges before I could write a 5-line block to do what element.endSelection does.

Unsupported nightmares are cases where neither the browser or YUI support the method. There is one case in Launchpad were this happens. The series timelines use canvas to draw the series, milestones, and releases. It was disabled for all IE version, but IE 9 does support canvas if the page states it is HTML5 . Instead of checking for browser version, I created a method that checks if the browser can create the canvas, and if so, the series timeline is rendered. IE 8 and below do not get the interactive image. Other browsers that I have not planned for will show the canvas if they pass the check.

How do I ensure a browser falls back to working code?

At this moment, there is only one guard that prevents KHtml browsers using a feature. There might come a time to add more guards because Launchpad, YUI, and a kind of browser do not work, and no one has time to make it work. We need to know the browser version that was tested and the version of YUI, for example:

if ( < 6 && Y.version <= 3.3)

I think we want several experienced YUI developers to agree that this is the proper course before giving up though. When a new version of the browser or YUI is released, we know to check the feature. Does the guard need to be revised, or can it be removed?

Face painting and historical Web development

I spent my last two weekends working with ancient web technologies. Anne decided she wanted to update her Happy Faces web to attract more face painting business. I agreed to create a form for customers to inquire about rates and availability. The hosting service only offered Python 2.4,  CGI, and sendmail.

I really did not want to write a script that I knew others had written. I just wanted to make a pretty little form to help Anne. I found one library that almost worked.  I knew I could write a better library, and I would not trust any library without a test suite. I decided to write my own that would manage the request and response form, sending emails, and storing them if sendmail failed. I wrote a simple means to define a schema of what fields are in the form and how to validate them. The test module even provides a test web server that runs the CGIs you place in the root directory. This was a fine meditative exercise, but I would not describe it as fun.

I did have fun creating Anne’s contact form. Defining the schema for her form took minutes, and it was painless to make changes as we discussed what was optional and what the wording should be. The web page is not historical, it is very modern in fact. It uses HTML 5 form inputs and JavaScript to collect the information. The new  form inputs work in HTML 4 browsers like IE and FireFox, but HTML 5 browsers like Chromium and Safari see widgets that require less typing, less thinking about what to input.

I posted my CGI contact library on Launchpad for anyone who finds they need to work with historical technology.

Karma means action

I not only hack on Launchpad, I use it everyday for my personal projects and for the 1100+ projects I maintain as a registry administrator. I see hundreds of pages each week where my first question is “what has this user or project done recently”? Launchpad cannot easily tell me that, and what it does say requires interpretation. I am very disappointed. I think the most important information I need to see on Launchpad pages is an activity log.  I want to see something like a wall or stream presentation to understand how a person contributes to projects, what kinds of contributions are made, and is a project vital or dormant? My disappointment is compounded by Launchpad’s karma mechanism. I happen to know that “karma” is a Sanskrit word meaning “action”, which is not how Launchpad users or engineers interpret it.

I not writing an argument to fix Launchpad’s karma. It is fundamentally corrupt. Discussions about it become bike shed arguments. I like the name “karma” because I know it’s essential meaning, and that is all that I want to preserve.

For your edification, the value attributed to your action in Launchpad was arbitrarily set by an engineer, then a mechanism “balanced” the number against values in rarely used features.  These rules were created to in an effort to bring fairness to actions. We know that some actions help more people than other actions, but what is happening is that someone was awarded a tremendous number of points for some action that will never help someone else. For example, You can work for a year to build and release an app to earn 10 points, then Launchpad slaps you in the face when I register a blueprint that will never be implemented…awarding me with 300 points. Launchpad is misinforming you and everyone else about the value of your contributions. The numbers will always be subjective. Stop reading those karma numbers, stop trying to raise them; you will not win anything for having a high score.

  • I want to stop recording values for action, at the very least, never show them.
  • I want to stop expending valuable CPU time balancing karma.
  • I want to stop expiring karma.

Once we stop talking about value and fairness, we are free to add new kinds of actions that we want Launchpad to record. I hope to know that someone has registered a project, set the branch used as development focus, added a recipe to build it, and made an official release.

  • I want to see a listing of all the things I register or change in Launchpad.
  • I want to see a list of actions that someone has done today, this week, this month, this year, and maybe even last year.
  • I want to filter the actions by to specific categories that pertain to bugs, code, or planning.
  • I want see a link to the thing the action was about.


Misadventures with gnome Javascript

I recently updated pocket-lint to require gjs instead of seed because the latter has been 100% broken for 6 weeks. The best thing I can say about this experience is that I am very happy there are two competing implementations of GObject Introspection bindings for JavaScript. I am disappointed that the two implementations are not 100% compatible. Most of my changes to pocket-lint were to hide the deficiencies of gjc.

There are small but important differences between gjc and seed that developers will want to be mindful of. Gjs is older, based on Spider Monkey, and servers a simple need: JavaScript is the most common development language, and GNOME should support it. Seed is newer, based on Webkit, and serves subtly different need: I know JavaScript and I want to develop GNOME applications. Gjs works though you might not know it given the lack of documentation, and you might be disappointed to see that it recommit’s JavaScript’s original sin of namespace pollution. Seed is well documented and its extensions are in a separate namespace. Seed’s documentation is 99% compatible with gjs, so I can forgive the oversight. I learned missing crucial 1% of gjs by reading the C source. I was appalled to discover that gjs creates print, log, and argv functions in the global namespace. Seed, being an implementation for developers who really use JS, place the extensions in the Seed object.

I want to use seed, but given that it is broken in Ubuntu Oneiric, I decided to support gjs. I chose to check for the existence of the Seed object, and construct one from gjs when it is not present. The namespace is still polluted. The interpreters have different command lines, though they are similar enough for my purposes.

I think the GNOME community could do better by updating the gjs and seed to share the same command line, and add extensions to a common namespace, such as System. Thus an app that wants to call System.print() will not care about which interpreter is used. This permits developer and distros to choose the backend of their choice. It also lets us change our minds.

PS. I know that some developers want to use gjs to use the JavaScript advances developed by the Mozilla community, but this is a fools pursuit. JS has been fragmented since the release of IE 3.0 in 1996. JS developers know they have to write for a common language, and use libs to provide extensions. I am very aware of the failure of ES4. I hope Mozilla can get its extensions into the official language.

Recovering Ubuntu after an OS X Lion upgrade

I updated my Macbook to OS X Lion over the weekend and had a nasty scare. I could not boot back to Ubuntu, nor could I see my Ubuntu partition. That is the moment where you think, “Is everything baked up? What will I loose?”. I remember that I switched from Evolution to Thunderbird last month; my email was lost. The truth is that everything was okay, and if this happens to you, you can fix this in a few minutes like I did.

I do not know what the OS X installer did. Maybe it resized the partitions. rEFIt did not report an issue. It verified that the the partition tables were synced. Grub did not know about Ubuntu though. I saw the grub prompt (grub>) instead of a message about the selected partition. I followed ubuntu mactel instructions to install Ubuntu, so I know Ubuntu should be on located on hd0,3. I typed:

grub> root (hd0,3)

grub> find /boot/grub/stage1

Find will report if it finds the boot information. If it does not find stage1, then you can try an alternate disk and partition, such as root (hd1,1) or root (hd0,4). When you find stage1 you can configure grub to fix boot:

grub setup (hd0,3)

I restarted and Ubuntu booted correctly. No data was lost.

The reason that OS X could not see my Ubuntu partition was because MacFuse was compiled for 32bit. Lion is 64bit. I installed the 64bit version of MacFuse to see Ubuntu and my data.

Creating a signed release file with

I recently created for some videos I am making about how to use Launchpad. One aspect of creating a release is to upload the source tarball and a detached gpg signature verifying it. This is somewhat ironic, since lp-release-manager-tools exists to automate repetitive tasks that release managers do in Launchpad. I really do not like creating the signature. I cannot remember how to do it. I need to read the instruction on the form to upload the tarball each time. So I added a feature to my example project that any project hat uses python distutils can copy to make the signature with the source tarball.

I subclassed the sdist command and added an extra step to create the detached signature of the tarball. I then register the new command as signed_dist. This is the content of my

import subprocess

from distutils.core import setup
from distutils.command.sdist import sdist

class SignedSDistCommand(sdist):
    """Sign the source archive with a detached signature."""

    description = "Sign the source archive after it is generated."

    def run(self):
        gpg_args = [
            'gpg', '--armor', '--sign', '--detach-sig', self.archive_files[0]]
        gpg = subprocess.Popen(
            gpg_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    description="Launchpad release manager API scripts.",
    maintainer="Curtis C. Hovey",
        'signed_sdist': SignedSDistCommand,