loading...
Cover image for Packaging Software with ‘fpm’

Packaging Software with ‘fpm’

jhermann profile image Jürgen Hermann Originally published at jhermann.github.io on ・4 min read

fpm (eFfing Package Management) enables you to build all sorts of Linux packages with great ease and sanity.

What it does

Basically, ‘fpm’ allows you to deploy any software via OS packages, from an installation tree on disk or from already built artifacts specific to the chosen implementation language.

The main advantage over native tooling is you do not need to know about every minute detail of the involved commands and metafile formats for every platform. In case of Debian, that is at least the “control” and “rules” files, and tools like “buildpackage” and “debhelpers”.

How to install it

‘fpm’ is written in Ruby and can thus be installed via gem install. But you can also run it with JRuby in a JVM, that you might happen to already have on a host anyway. Using JRuny spares you the headache of juggling multiple Ruby versions and isolating gems against other Ruby applications.

If you package it up that way, it also can be installed on any Linux release because you only need a Java8 JRE installed to run it – no native code involved. You can use the fpm.sh script in the priscilla project on GitHub to package ‘fpm’ with itself. As written, it works for Debian derivatives, but should be adaptable to other distros with a few changes (remember, ‘fpm’ makes that easy).

If you call ./fpm.sh pkg, the package contents is created in build as a staging area, and when everything is ready, fpm is called from within that staging area to create the final package:

build/opt_tools_fpm/opt/tools/fpm/bin/fpm \
    -s dir -t deb -n opt-tools-fpm -v 1.11.0 \
    --iteration 1 --category tools \
    --deb-user root --deb-group root \
    -m '"Juergen Hermann" <jh@web.de>' \
    --license 'See contained license, or homepage' \
    --vendor github.com/jhermann/priscilla \
    --description 'fpm helps you build packages quickly and easily' \
    --url http://fpm.readthedocs.io/ \
    --workdir $PWD/build/opt_tools_fpm/tmp \
    -a all -d 'openjdk-8-jre|…|java8-runtime-headless' \
    opt usr

Yes, this is a mouthful, but still shorter than a control or .spec file, and easily adapted to other package managers.

The created DEB file ./build/opt_tools_fpm/opt-tools-fpm_1.11.0-1_all.deb has this metadata:

 new debian package, version 2.0.
 size 23763108 bytes: control archive= 30446 bytes.
     462 bytes, 12 lines control              
  119039 bytes, 1118 lines md5sums              
 Package: opt-tools-fpm
 Version: 1.11.0-1
 License: See contained license, or homepage
 Vendor: github.com/jhermann/priscilla
 Architecture: all
 Maintainer: "Juergen Hermann" <jh@web.de>
 Installed-Size: 27046
 Depends: openjdk-8-jre|zulu8|…|java8-runtime-headless
 Section: tools
 Priority: extra
 Homepage: http://fpm.readthedocs.io/
 Description: fpm helps you build packages quickly and easily

The major part of files is installed into /opt/tools/fpm, but a symlink at /usr/bin/fpm makes the command available on the path.

Building fpm 1.11.0 that way was tested on Ubuntu Bionic with openjdk-8-jre-headless 8u242.

How to use it

There are a lot of source and target types available in fpm (dir, gem, deb, npm, rpm, tar, cpan, pear, empty, puppet, python, osxpkg, solaris, p5p, pkgin, freebsd, apk, snap, pleaserun, zip, virtualenv, pacman, sh), this example converts a Python workdir into a DEB package file.

The rudiments project serves as the example here, but you can use any pure Python project built with setuptools. You have to clone the project and then call fpm like so:

( deactivate 2>/dev/null; py=/usr/bin/python3; \
  fpm -s python -t deb --category python \
    --python-bin $py \
    --python-pip "$py -m pip" \
    --python-package-name-prefix "$(basename $py)" \
    --python-obey-requirements-txt \
    --python-install-data "/usr/local/share/$(basename $py)/$($py ./setup.py --name)" \
    -m "\"$($py ./setup.py --author)\" <$($py ./setup.py --author-email)>" \
    --vendor "$($py ./setup.py --url | cut -f3-4 -d/)" \
    --force $PWD/setup.py )

The --force option overwrites an existing package file, so you can call the command multiple times without an error.

If you inspect the built package with dpkg-deb -I python3-rudiments_*_all.deb, this is the output:

 new Debian package, version 2.0.
 size 31062 bytes: control archive=1417 bytes.
     366 bytes, 12 lines control              
    3789 bytes, 30 lines md5sums              
 Package: python3-rudiments
 Version: 0.3.1
 License: Apache 2.0
 Vendor: github.com/jhermann
 Architecture: all
 Maintainer: "Jürgen Hermann" <jh@web.de>
 Installed-Size: 79
 Depends: python3-requests (>= 2.6)
 Section: python
 Priority: extra
 Homepage: https://github.com/jhermann/rudiments
 Description: Rudiments – Fundamental elements for any Python project.

The package's content is placed into these directories:

/usr/local/lib/python3.6
/usr/local/share/python3/rudiments
/usr/share/doc/python3-rudiments

You can install it using dpkg -i … and then do a quick import test with this command:

python3 -c "import rudiments; print(rudiments)"

Where to go from here

fpm is available on GitHub.

GitHub logo jordansissel / fpm

Effing package management! Build packages for multiple platforms (deb, rpm, etc) with great ease and sanity.

Read more about it on the project's GitHub wiki, or watch this slide deck.

Credits: Package icon by Breathe Icon Team: Sebastian Porta, Cory Kontros, Andrew Starr-Bochicchio

Posted on by:

jhermann profile

Jürgen Hermann

@jhermann

A long long time ago, I started with Python 1.5 at web.de, founded the MoinMoin wiki project shortly thereafter, and am now enjoying the increased traction that Python recently gets from data science.

Discussion

pic
Editor guide