At Seznam.cz we use Debian packages for packaging and distribution of a decent amount of our applications. While we are in the process of transitioning to solutions using Docker, Debian packages still play a significant role.
In this article, I will tell you the basics about how to make a simple Debian package with a static website. I wrote this article from the frontend developer point of view, so not all parts of Debian packaging are covered.
Basics
- Create a directory named
debian
in the root of your project. - We will use
debhelper
which is a collection of small tools that are used to automate various aspects of building a Debian package. - Add
DEBEMAIL
andDEBFULLNAME
environment variables to your.bashrc
to have your name and email automatically filled when you will manipulate the changelog with thedch
.
export DEBFULLNAME="Forename Surname"
export DEBEMAIL="name@email.com"
- You will need
debhelper
anddch
. These tools are inbuild-essential
anddevscripts
packages. apt-get install build-essential devscripts
Folder structure
Inside the debian
folder you will need to create these five files:
Filename | Description |
---|---|
changelog | Changes and release dates of each version of your package. |
compat | Defines the debhelper compatibility level. |
control | Metadata about the package, like its name, maintainers or dependencies. |
rules | Specifies how to build the package. |
package-name.install | What files to include in the package. |
Changelog file
Create debian/changelog
file, either by running dch --create
or manually.
package-name (0.0.1) UNRELEASED; urgency=medium
* Initial release.
-- Forename Surname <name@email.com> Mon, 15 May 2017 20:00:00 +0200
Working with the changelog
Command | Description |
---|---|
dch --create | Creates new changelog file with Initial release entry. |
dch | Adds new version if the latest one is not UNRELEASED. Otherwise, it adds a new item to the current version and updates the date. |
dch -r | Changes from UNRELEASED to released state and updates the date. |
Just use your editor | Works fine most of the time. |
At Seznam.cz when we release a package we do not use the official distributions, instead we do something like dch -r --force-distribution --distribution Seznam
.
Compat file
The compat file defines the debhelper compatibility level. Currently, you should set it to the debhelper v9.
echo 9 > debian/compat
Control file
- In this file, we specify all the metadata about our packages, like their names, dependencies, and maintainers.
- There are two types of packages:
- Source releases: contain a human readable version of the application, meaning they have to be compiled before they can be used.
- Binary releases: include computer readable version of the app, meaning they are already compiled.
- For our purposes, we will only build the binary package, but fields for the source release also need to be specified.
Minimal control file
Source: package-name
Maintainer: Forename Surname <name@email.com>
Section: fulltext/Seznam
Priority: extra
Build-Depends: debhelper (>= 9.0.0)
Standards-Version: 3.9.8
Package: package-name
Architecture: all
Section: fulltext/Seznam
Priority: extra
Depends: ${misc:Depends}
Description: Package description
Source package fields
Field | Type | Description |
---|---|---|
Source | mandatory | The source package name. Must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.).package-name |
Mantainer | mandatory | The package maintainer’s name and email address.Forename Surname <name@email.com> . |
Section | recommended | An application area into which the package has been classified. At Seznam.cz we use something like fulltext/Seznam . |
Priority | recommended | How important it is that the user have the package installed. The priorities are (in descending order): required , important , standard , optional , extra . |
Build-Depends | optional | What dependencies need to be installed before we can start building the package. |
Standards-Version | recommended | The most recent version of the Debian Policy Manual with which the package complies. You can find the lates version at the bottom of every Debian policy page. |
Binary package fields
Field | Type | Description |
---|---|---|
Package | mandatory | Name of the binary package. Binary package names must follow the same syntax and restrictions as source package names. |
Architecture | mandatory | Usually one of the following:all - Architecture independent, usually consisting of text, images, or scripts in an interpreted language.any - Architecture dependent, usually in a compiled language. |
Section | recommended | Same as Section of source package. |
Priority | recommended | Same as Priority of source package. |
Depends | optional | Dependencies of the package. Some debhelper commands may cause the generated package to depend on some additional packages. All such commands produce a list of required packages for each binary package. This list will then substitute the ${misc:Depends} string. |
Description | mandatory | Description. |
Rules file
- Create
debian/rules
. This file is Makefile so make sure to:- Mark it as executable
chmod u+x debian/rules
. - Use only tabs for indentation.
- Mark it as executable
- This file contains rules about how to build your package. We will be using the
dh
command from thedebhelper
package to help us with the build. Here is minimal rules file that only calls thedh
without any other configuration.
#!/usr/bin/make -f
%:
dh $@
- The
dh
command has many build stages, and you will see them mentioned in the console once you start building your package. If you need to override or extend parts of thedh
build steps, here is how you do it:
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_build:
dh_auto_build # We can omit this call and only use our scripts
./my-script
Debian install script
- Create file named
debian/package-name.install
. - In this file, we specify what files or folders will be copied into the package. Then once the package is installed, these files will be copied from the package onto the filesystem.
- Directories are relative to the parent of debian directory, for example, if you have
project/debian/package-name.install
, paths will be relative to theproject
dir.
dist/* /www/package-name/
Build the package
- In the root of your project directory run
dpkg-buildpackage -b -uc
.-b
only make a binary package and do not create a package with source files.-uc
skip the signing stage of the.changes
file with your PGP key.
- If everything went ok, you should see file named
package-name_1.0.0_all.deb
one level up from the root directory of your project.
Build artefacts
After the build, you will see some build artifacts inside the debian
directory. They are useful for your build debugging, but if everything goes ok, you can safely remove them with something like:
git clean -df debian
Inspecting the package
- To get basic metadata about package (mostly stuff from
debian/control
)dpkg --info <file>.deb
- Midnight Commander program is pretty helpful if we want to take a look inside of the package
sudo apt-get install mc
- Press
F3
or theView
command while having the package selected to see basic info. - Or press enter to open the package and inspect its content.
- Trying to install the package is also useful to see if there are not any conflicts or dependency problems
sudo dpkg -i <file>.deb
Static files build dependencies
So far we have only covered how to take already existing dist
directory and package it up, but how do we express dependencies needed for building the dist
directory? I think there are two reasonable approaches.
Express everything in the debian/control and debian/rules
In case we would want to express the static files build dependencies and the build itself in the debian directory, here is how it could look like.
Control file:
- From
Build-Depends: debhelper (>= 9.0.0)
. - To
Build-Depends: debhelper (>= 9.0.0), nodejs (>= 6.0.0)
(or whatever dependencis you need and can install withapt
).
Rules file:
- Add an override to the build step where we will do the installation of additional dependencies from other package managers and also the building itself.
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_build:
cd src && npm install && ./build-script
CI Build pipeline
If we have some CI build pipeline, it makes more sense to have one CI task for the build of the static files, and another one for the creation of the Debian package. Then we just pass the build artifacts from the build task to the package task. This will allow use to use the right Docker image for given task (e.g. node:7
for the static files, and debian:8
for the packaging) and worry less about installing additional dependencies.
Example of GitLab CI file
stages:
- build
- package
build:
stage: build
image: node:7.8.0
script:
- cd app
- npm install
- ./scripts/build
artifacts:
expire_in: "1 week"
paths:
- ./app/build
package:
stage: package
image: debian:8.8
script:
- apt-get update
- apt-get install --yes build-essential devscripts
- dpkg-buildpackage -b -uc
- mv ../*.deb .
artifacts:
expire_in: "1 week"
paths:
- ./*.deb