Create and distribute a .deb package in your company

Published: 2014-11-15
Tagged: python linux

There will come a time when you want to have a simple service serving a single file or maybe even a few of them. Today I had a simple idea that might shave off a few precious minutes off of server deployment time. What caught my eye was that a step of this process involved installing the latest stable version of ruby from source, which involves a few commands and takes up time. Why not make a .deb out of it? That's good and all, but how am I going to make sure it gets into the right hands?

The answer to the first question is checkinstall. This tool will build a .deb package from source that we can then dpkg -i on a new system. The answer to the second question is Python's BaseHTTPServer. Let's start from the back!

Distribution

We'll use BaseHTTPServer to listen on a port and answer every request with the file we want to server. This is an extremely simple script that blocks, so it can only server a file at a time. We might want to use Tornado if this file is really popular or just really big. This script can be easily modified to server a couple of files by using regexp and accessing the self.path property in the do_GET() method of the handler. Just a warning though: regular expressions are a helluva a drug.

Ok, here's the code:

#! /usr/bin/env python

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

class Handler(BaseHTTPequestHandler):
    def do_GET(self):
        with open('ruby.deb', 'rb') as f:
            self.send_response(200)
            self.send_header('Content-type', 'application/octet-stream')
            self.send_header('Content-disposition', 'attachment; filename="%s"' % f.name)
            self.end_headers()
            self.wfile.write(f.read())
            return
try:
    server = HTTPServer(('', 31337), Handler)
    server.serve_forever()
except KeyboardInterrupt:
    server.socket.close()

It doesn't get any simpler than this. The script listens on port 31337 and answers every request with the file specified in the with block. It sets two headers which make downloading it a bit easier. It also listens for a keyboard interrupt (ctrl + c) to exit the server.

The fun ain't over yet. We still have to make sure the script run and that it serves the right file. Again, starting from the second question (too much functional programming lately), we use ln -s to create a soft link to the most current version and name it simple ruby.deb. That means that every time we update the ruby.deb package, all we have to do is run ln -s /home/user/ruby-2.1.5.deb /home/user/ruby.deb. No need to change the code at all.

Ideally, we'd want to use a robust system live supervisord to ensure that this script is running and that it starts along with the system in case our server has to restart for whatever reason. But let's not get too fancy too fast here, alright?

We could run this script in the background, but we soon discover a problem - as soon as we log off the server, the script dies. Uh oh. Never fear, GNU/Linux is here! We use the super simple nohup command to make sure that the script ignores the hangup signal sent by us logging off:

nohup ./serve-ruby.py &

And there we have it!

Packing a .deb

I knew this was possible, but I had no idea how. Luckily, we have checkinstall. These are the simple steps needed to package ruby in its own nice little package:

  1. Download the latest tarball
  2. Untar it
  3. ./configure && make && checkinstall --install=no
  4. Follow the onscreen instructions
  5. Enjoy the newly created redistributable package!

I'm not an expert on this and I didn't even skim the checkinstall documentation, so use this at your own risk. To avoid the most difficulties, I ran this on a platform as similar to our production servers as possible, so that everything from the architecture to the file paths would be similar. In the end, running

dpkg -i ruby.deb
ruby -v

Returned the version string that I wanted. Enjoy!

Comments

There aren't any comments here.

Add new comment