Packaging Software with ‘fpm’
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 JRuby 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.
This created a DEB file ./build/opt_tools_fpm/opt-tools-fpm_1.11.0-1_all.deb
, with 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 using 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
Read more about ‘fpm’ on its github wiki, or watch this slide deck.
Credits: Package icon by Breathe Icon Team: Sebastian Porta, Cory Kontros, Andrew Starr-Bochicchio