Packaging Scripts for Debian

Table of Contents

Introduction
The debian/rules File
Other Packaging Files
VCS Repository Structure

Introduction

I prefer to stick to a discipline of managing everything of significance that I install on my systems in Debian packages. This gives me the full power of the package system to handle dependencies, makes it easier to ensure that I have the current version installed on all of my systems, and avoids multiple copies of files checked into different repositories and templates.

However, some of the software I use comes in the form of stand-alone scripts distributed as simple downloads rather than versioned tarballs. This requires some machinations to build a reasonable Debian package since the normal packaging workflows assume an upstream tarball with a Makefile and similar machinery.

This page summarizes my approach in the hope that it will save other people some time. Most of it is obvious, but it's sometimes still nice to have something to cut and paste.

All code examples on this page are hereby placed in the public domain if that's possible in your jurisdiction, and in any case may be treated as if they were in the public domain.

The debian/rules File

The first trick with scripts is to create a reasonable upstream tarball for use as the .orig.tar.gz file. I use the following code in debian/rules to implement a get-orig-source target, this for an example filter-syslog script.

    SCRIPT   = filter-syslog
    URL      = http://archives.eyrie.org/software/system/filter-syslog
    NEW      = `awk '/Id: / { print $$6; exit }' $(SCRIPT).new`

    get-orig-source:
            wget -O $(SCRIPT).new $(URL)
            chmod 755 $(SCRIPT).new
            mkdir $(SCRIPT)-$(NEW)
            cp -p $(SCRIPT).new $(SCRIPT)-$(NEW)/$(SCRIPT)
            tar cfz $(SCRIPT)_$(NEW).orig.tar.gz $(SCRIPT)-$(NEW)
            rm -r $(SCRIPT)-$(NEW) $(SCRIPT).new

The number of column to print in the awk command depends on how the Id string is represented in the script. This example assumes setting a variable with an our delcaration. If that's not the case, you may want $$5 instead. If you cut and paste this, be careful to change the leading spaces in the lines after the get-orig-source: target to tabs.

This rule downloads the script from a URL (which will need to be changed for the script that you're using) and creates an .orig.tar.gz file from it.

The above rule extracts the version number of the script from a CVS- or Subversion-style Id string, which matches my convention for script versioning. Depending on the script, you may have to do something else, such as run the script with -v and extract the version from the output. Unfortunately, you cannot use := or $(shell) to extract the version number since it can't run before the new script is downloaded.

The rest of debian/rules depends on whether you are installing a man page you wrote yourself, or whether you're generating one from the script. If you're installing one yourself, you can just put it into debian/manpages and then use the fully minimal debhelper 7 rules:

    #!/usr/bin/make -f
    %:
            dh $@

plus the get-orig-source target. If you need to generate the man page during the build, you need something more complex. Most of the scripts I package are in Perl with POD documentation, so I use something like:

    VERSION := $(shell awk '/Id: / { print $$6; exit }' $(SCRIPT))

    override_dh_auto_build:
            pod2man --release=$(VERSION) --center="$(SCRIPT)" $(SCRIPT) \
                > $(SCRIPT).1

    %:
            dh $@

for the rest of debian/rules. As above, replace the awk code with something else if needed to get the version of the script and replace leading spaces with tabs. You can do something similar if you're using help2man or some other system to generate the man page. Don't forget to add the generated man page to debian/clean and to debian/manpages.

Other Packaging Files

The other packaging files are very simple and similar to a normal Debian arch-all package. If you use the above techniques for debian/rules, you of course need to declare a build-dependency on debhelper (>= 7.0.50~) for override support and create a debian/compat file containing 7.

For Perl scripts, remember that you can use ${perl:Depends} in debian/control for the Perl dependency of the generated package, but you still have to separately declare the dependencies on the modules that the script uses.

If you're generating the man page during the build, remember to add a Build-Depends-Indep line for whatever tools generate the man page. In the case of pod2man, this is simply the perl package, but an explicit build dependency is still good form since perl-base may not include the pod2man components. (Yes, debhelper depends on perl so this isn't strictly necessary, but it's not good form to rely on the dependencies of your dependencies when you call the programs directly.)

I add a watch file containing something like this:

    # Since upstream makes this script available as a simple file download
    # and the download link doesn't contain the version number, there
    # isn't a way to write a meaningful watch file.
    #
    # To check the current upstream release, go to the package home page
    # and check the version number listed there.

for scripts whose download links aren't versioned. This silences Lintian and documents the situation for anyone running DEHS or uscan on the package.

VCS Repository Structure

I keep all of my Debian packaging in Git, even simple things like this. For scripts, I use a simplified version of my normal Git packaging structure and configuration that maintains all Debian packaging and changes in the master branch. The following details assume that you've already set up git-buildpackage and friends as described there.

First, to bootstrap the repository, you want to create an upstream branch containing just the script, a pristine-tar branch containing the metadata for the .orig.tar.gz files that you will create, and a master branch that will be used for releases, packaging, and Debian-specific changes. Start with:

    git init
    git branch upstream

in a new empty directory. This creates an empty upstream branch that you'll use later.

Then, what I find useful is to write the skeleton of the Debian packaging first, particularly including the get-orig-source target in debian/rules, and commit that to an empty repository created with git init. Then, the following commands will bootstrap things:

    debian/rules get-orig-source
    git checkout upstream
    tar xf <script>_<version>.orig.tar.gz
    mv <script>-<version>/<script> .
    rmdir <script>-<version>
    git add <script>
    git commit -m 'Import upstream version <version>'
    git tag upstream/<version>
    git checkout master
    pristine-tar commit <script>_<version>.orig.tar.gz
    rm <script>_<version>.orig.tar.gz
    git merge upstream

replacing <script> and <version> with the name of the script and its current version.

This process will create an upstream branch that contains just the current version of the script as distributed by upstream and a pristine-tar branch with the necessary information to recreate your tarball, and then merges the upstream branch into master. You can now finish whatever additional packaging bits you need to do and continue as normal.

Once you've bootstrapped the repository, importing a new upstream version is as easy as:

    git checkout master
    debian/rules get-orig-source
    mv <script>_<version>.orig.tar.gz ..
    git-import-orig ../<script>_<version>.orig.tar.gz

I've had better luck with keeping the .orig.tar.gz file outside of the working directory of the repository when running the git-import-orig magic.

Last spun 2013-07-01 from thread modified 2013-01-04