Debian Packaging📦

Debian packaging refers to the process of building and distributing software packages in the .deb format, which Debian uses and its derivatives like Ubuntu. A Debian package contains all the files, metadata, and instructions necessary to install software on a system using the APT (Advanced Package Tool) or similar package managers.

Creating a Python File and Making it Executable 🐍

sudo apt update
vi greet-koushal.py
print("Hi This is koushal,Let's crack it up!")
sudo apt-get -y install python3-pip
sudo pip3 install pyinstaller --break-system-packages
pyinstaller --onefile greet-koushal.py

Packaging it as a Debian Package 📦

Before we can start packaging, we need a directory structure that follows Debian's packaging conventions. This naming format helps track software versions and architecture, and it looks like this:

<package-name>_<version>-<release-number>_<architecture>

Where:

  • package-name: The name of your package, which is hello-world in our case.

  • version: The version of the software. We’ll use 0.0.1 here.

  • release-number: Tracks different releases of the same version. Typically, this starts at 1.

  • architecture: The target architecture, such as amd64. If the package is platform-independent (like Python scripts), you can use all.

Let’s create the directory now:

mkdir -p ~/example/hello-world_0.0.1-1_amd64

This directory will be our package's root. Since we want our "hello-world" program installed system-wide, we’ll place the binary in the appropriate location (/usr/bin/hello-world).


2. Adding the Binary to the Package

Now, we’ll create the directories for the binary and copy our "hello-world" program:

cd ~/example/greet-koushal_0.0.1-1_amd64
mkdir -p usr/bin
cp /home/ubuntu/dist/greet-koushal usr/bin/.

This places our executable in usr/bin, where it will be accessible system-wide after installation.


3. Creating the Control File

Every Debian package needs a control file that contains metadata about the package, such as its name, version, maintainer, and dependencies. This file will go into a special DEBIAN folder.

Let’s create the control file:

mkdir -p ~/example/greet-koushal_0.0.1-1_amd64/DEBIAN

Now, we’ll add the following information to the control file:

echo "Package: greet-koushal
Version: 0.0.1
Maintainer: example <example@example.com>
Depends: python3.12
Architecture: amd64
Homepage: http://example.com
Description: Greetings from koushal" \
> ~/example/greet-koushal_0.0.1-1_amd64/DEBIAN/control
  • Package: The name of the package (hello-world).

  • Version: The version of the software (0.0.1).

  • Maintainer: The person responsible for the package.

  • Depends: Any dependencies needed for the program to run (e.g., libc6).

  • Architecture: The platform architecture (amd64 in this case).

  • Homepage: The official site for the program.

  • Description: A brief description of the software.


4. Building the .deb Package

With the directory structure and control file in place, it’s time to build the .deb package! 🎉

dpkg --build ~/example/greet-koushal_0.0.1-1_amd64

This command will generate the .deb package under the ~/example/ directory. You should now have greet-koushal_0.0.1.deb ready to distribute

Creating an APT Repository 🗂️

Let’s kick things off by creating a directory structure to hold our .deb packages. We'll be following the standard APT repository format, which organizes packages into a pool directory.

mkdir -p ~/example/apt-repo/pool/main/

Next, we’ll copy our .deb package (in this case, hello-world) into the directory we just created:

cp ~/example/greet-koushal_0.0.1-1_amd64.deb ~/example/apt-repo/pool/main/.

This pool will store all the .deb files that will be available in your repository. Larger repositories, like the official Ubuntu ones, further organize packages into sub-directories. For instance, Vim-related packages can be found under /ubuntu/pool/main/v/vim/.


2. Generating Package Metadata

APT repositories require a list of available packages and metadata about each package. This is done by creating a Packages file.

Let’s first create a directory for this metadata:

mkdir -p ~/example/apt-repo/dists/stable/main/binary-amd64

If you want to support multiple architectures (like i386, arm64, etc.), create a similar directory for each.

Now, we’ll generate a Packages file using the dpkg-scanpackages command:

cd ~/example/apt-repo
dpkg-scanpackages --arch amd64 pool/ > dists/stable/main/binary-amd64/Packages

This Packages file contains a list of all the .deb files in your repository, along with metadata about each.


3. Compressing the Packages File

To make things more efficient, APT prefers downloading compressed metadata. So let’s compress the Packages file:

cat dists/stable/main/binary-amd64/Packages | gzip -9 > dists/stable/main/binary-amd64/Packages.gz

By compressing it, you’re ensuring that your repository will work faster, and this is also often a required step. You can even use other compression types like bz2 or lzma depending on your needs. But for simplicity, we’ll stick with gzip.


4. Creating the Release File

Now, we need a Release file, which provides additional metadata about the repository itself, like its origin, architectures, and a list of hash sums for each Packages file.

Let’s look at a sample Release file format:

makefileCopy codeOrigin: Example Repository
Label: Example
Suite: stable
Codename: stable
Version: 1.0
Architectures: amd64 arm64 arm7
Components: main
Description: An example software repository

It also includes checksums (MD5, SHA1, SHA256) to validate the integrity of the repository’s Packages files. Rather than manually crafting this file, we’ll automate its creation with a bash script!


5. Automating the Release File Creation

Here’s a quick bash script to generate the Release file automatically:

echo '#!/bin/sh
set -e

do_hash() {
    HASH_NAME=$1
    HASH_CMD=$2
    echo "${HASH_NAME}:"
    for f in $(find -type f); do
        f=$(echo $f | cut -c3-) # remove ./ prefix
        if [ "$f" = "Release" ]; then
            continue
        fi
        echo " $(${HASH_CMD} ${f}  | cut -d" " -f1) $(wc -c $f)"
    done
}

cat << EOF
Origin: Example Repository
Label: Example
Suite: stable
Codename: stable
Version: 1.0
Architectures: amd64 arm64 arm7
Components: main
Description: An example software repository
Date: $(date -Ru)
EOF
do_hash "MD5Sum" "md5sum"
do_hash "SHA1" "sha1sum"
do_hash "SHA256" "sha256sum"
' > ~/example/generate-release.sh && chmod +x ~/example/generate-release.sh

This script will automatically generate a Release file with all the required hashes for your APT repository.

To run the script and create the Release file, use this command:

cd ~/example/apt-repo/dists/stable
~/example/generate-release.sh > Release

Signing your apt Repository With GPG🗝️

GPG keys are essential in APT repositories for ensuring security, authenticity, and integrity of packages. Here's why:

  1. Package Authenticity: GPG signatures verify that the package is from a trusted source, ensuring it hasn't been tampered with.

  2. Data Integrity: Signatures prevent malicious alterations by detecting any changes in the package since it was signed.

  3. Trust System: Users trust GPG-signed packages, creating a chain of trust between the repository and users.

  4. Preventing Malware: GPG keys protect against installing untrusted or harmful software.

  5. Maintaining Credibility: Signed packages reassure users of the repository's legitimacy and security.

echo "%echo Generating an example PGP key
Key-Type: RSA
Key-Length: 4096
Name-Real: koushal
Name-Email: koushalakash3@gmaail.com
Expire-Date: 0
%no-ask-passphrase
%no-protection
%commit" > /tmp/example-pgp-key.batch

export GNUPGHOME="$(mktemp -d ~/example/pgpkeys-XXXXXX)"
gpg --no-tty --batch --gen-key /tmp/example-pgp-key.batch

gpg --armor --export koushal > ~/example/pgp-key.public

gpg --armor --export-secret-keys koushal > ~/example/pgp-key.private

cat ~/example/pgp-key.private | gpg --import

cat ~/example/koushal-apt-repo/dists/stable/Release | gpg --default-key example -abs > ~/example/koushal-apt-repo/dists/stable/Release.gpg

cat ~/example/koushal-apt-repo/dists/stable/Release | gpg --default-key koushal -abs --clearsign > ~/example/koushal-apt-repo/dists/stable/InRelease

Hosting the APT Repository 🌐

you can host your Repository in Git hub for free or in some other way like Apache or Nginix. You could refer my APT repository attached here

Workflow between the APT client and APT Repository👈👉

  1. APT Client sends a request (apt update) for updated metadata.

  2. Repository sends the Release file and Release.gpg to verify the repository’s authenticity.

  3. APT verifies the GPG signature of the Release file.

  4. APT downloads the Packages file (or Packages.gz), containing package metadata.

  5. APT verifies the hash of the Packages file against the Release file.

  6. User requests a package (apt install <package-name>).

  7. Repository sends the .deb package file.

  8. APT verifies the hash of the .deb package using the hash from the Packages file.

  9. APT installs the package and resolves any dependencies.

This process ensures both the authenticity (via GPG signatures) and integrity (via hashes) of the packages downloaded and installed on your system.

10
Subscribe to my newsletter

Read articles from Koushal Akash RM directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Koushal Akash RM
Koushal Akash RM