Takip et

Example: Check for a condition before installation

FPM (Effing Package Management) is a fantastic tool for creating packages quickly and easily for various operating systems from a single source. It’s

FPM (Effing Package Management) is a fantastic tool for creating packages quickly and easily for various operating systems from a single source. It’s written in Ruby and acts as a package converter, letting you go from one package type (like a directory of files, a Ruby gem, a Python package) to another (like a .deb, .rpm, .tar.gz, etc.).

The “multiple operating systems” part refers to the output package types. You’ll typically run FPM on a single build host (e.g., a Linux machine, or even a Docker container) to generate packages for Debian/Ubuntu, RedHat/CentOS, Arch Linux, etc.

Here’s a comprehensive guide on how to use FPM effectively.

—

What is FPM and Why Use It?

* Simplicity: Drastically simplifies the process of creating .deb or .rpm packages, which can otherwise be quite complex with dpkg-buildpackage or rpmbuild.
* Speed: Create a package in minutes, not hours.
* Flexibility: Package almost anything: a directory of files, a Python application, a Ruby gem, a pre-built binary, etc.
* Cross-Platform Output: Generate packages for Debian, Ubuntu, CentOS, RHEL, Fedora, Arch Linux, Solaris, macOS, and more, all from one command.
* Automation: Easily integrate into CI/CD pipelines.

Prerequisites

1. Ruby: FPM is a Ruby gem, so you need Ruby installed.
* On Debian/Ubuntu: sudo apt update && sudo apt install ruby ruby-dev build-essential
* On CentOS/RHEL: sudo yum install ruby ruby-devel @development (or dnf for newer versions)
* On macOS: Ruby is usually pre-installed, but it’s recommended to use a version manager like rbenv or rvm.

2. RubyGems: Ruby’s package manager, usually installed with Ruby.

Installation

Once Ruby is set up, install FPM:

sudo gem install fpm

(You might need sudo depending on your Ruby installation. If you’re using rbenv or rvm, you likely won’t need sudo.)

Core Concepts and Essential Flags

FPM’s command structure is generally:

fpm -s  -t  [options] 

-s: What are you packaging from*?
* dir: (Most common) Package files from a directory.
* gem: Package a Ruby gem.
* python: Package a Python application (requires setuptools).
* rpm: Convert an RPM to another format.
* deb: Convert a DEB to another format.
* cpan: Package a Perl module.
* npm: Package a Node.js module.
* tar: Package a tarball.
* … and more.
* -t : What package format do you want to create?
* deb: Debian/Ubuntu package.
* rpm: RedHat/CentOS/Fedora package.
* tar: Generic tarball.
* zip: Generic zip archive.
* pkg: macOS package.
* pacman: Arch Linux package.
* msi: (Experimental) Windows Installer package.
* … and more.
* -n : The name of your package (e.g., my-custom-app).
* -v : The version of your package (e.g., 1.0.0).
* -p : (Optional) Where to put the generated package file. Defaults to current directory.
* -d "": Add a dependency (e.g., -d "nginx" or -d "python3 >= 3.6"). You can specify multiple -d flags.
* --description "": A long description for your package.
* --url "": The URL for your project.
* --license "": The license under which your software is distributed (e.g., MIT, GPLv3).
* --maintainer "": The person or team maintaining the package.
* --architecture : The target architecture (e.g., amd64, x86_64, all, noarch).
* --iteration : The package release number (e.g., 1, 2.el7). Useful for distinguishing package builds of the same software version.
* --prefix : Install files into this directory within the package (e.g., /opt/my-app). Defaults to / for dir source.
* -C or --chdir : Change to this directory before packaging. Very useful when packaging a directory.
* --input-type and --output-type: Aliases for -s and -t.

Step-by-Step Examples

Let’s say you have a simple application structure:

my-app-src/
├── bin/
│   └── myapp.sh
├── etc/
│   └── myapp.conf
└── lib/
    └── myapp.py

And myapp.sh looks like this:

#!/bin/bash
echo "Hello from My App v1.0.0!"
cat /etc/myapp/myapp.conf

And myapp.conf:

APP_SETTING="default_value"

### 1. Preparing Your Files (Staging Directory)

It’s best practice to organize your files in a “staging” directory that mirrors the final installation path within the target system.

mkdir -p /tmp/my-app-staging/opt/myapp/bin
mkdir -p /tmp/my-app-staging/etc/myapp

cp my-app-src/bin/myapp.sh /tmp/my-app-staging/opt/myapp/bin/
cp my-app-src/etc/myapp.conf /tmp/my-app-staging/etc/myapp/
cp my-app-src/lib/myapp.py /tmp/my-app-staging/opt/myapp/lib/

Now, /tmp/my-app-staging looks like this:

/tmp/my-app-staging/
├── etc/
│   └── myapp/
│       └── myapp.conf
└── opt/
    └── myapp/
        ├── bin/
        │   └── myapp.sh
        └── lib/
            └── myapp.py

### 2. Creating a Debian (.deb) Package

fpm -s dir -t deb \
  -n "my-custom-app" \
  -v "1.0.0" \
  --iteration "1" \
  --architecture "amd64" \
  --description "A simple custom application." \
  --url "https://example.com/my-app" \
  --license "MIT" \
  --maintainer "Your Name " \
  -d "bash" \
  -d "python3" \
  -C /tmp/my-app-staging/ \
  .

-C /tmp/my-app-staging/: Tells FPM to change into this directory before* packaging.
* .: This tells FPM to package everything in the current directory (which is now /tmp/my-app-staging due to -C).

This will output a file like my-custom-app_1.0.0-1_amd64.deb.

To install and test:

sudo dpkg -i my-custom-app_1.0.0-1_amd64.deb
/opt/myapp/bin/myapp.sh

### 3. Creating a RedHat/CentOS (.rpm) Package

Using the exact same staging directory and almost the same command:

fpm -s dir -t rpm \
  -n "my-custom-app" \
  -v "1.0.0" \
  --iteration "1.el7" \
  --architecture "x86_64" \
  --description "A simple custom application." \
  --url "https://example.com/my-app" \
  --license "MIT" \
  --maintainer "Your Name " \
  -d "bash" \
  -d "python3" \
  -C /tmp/my-app-staging/ \
  .

* --iteration "1.el7": A common convention for RPMs indicating the release number and the target OS (Enterprise Linux 7).
* --architecture "x86_64": Standard for 64-bit RPMs.

This will output a file like my-custom-app-1.0.0-1.el7.x86_64.rpm.

To install and test:

sudo yum install my-custom-app-1.0.0-1.el7.x86_64.rpm
/opt/myapp/bin/myapp.sh

(Note: yum or dnf will handle dependencies automatically if they are in configured repositories.)

### 4. Adding Pre/Post-Install Scripts

You can execute scripts at various stages of the package lifecycle.

Create script files (e.g., preinstall.sh, postinstall.sh):

preinstall.sh:

#!/bin/bash
echo "Running pre-install script for my-custom-app..."

Example: Check for a condition before installation

if [ -f /opt/myapp/bin/myapp.sh ]; then echo "My App already exists, upgrading." else echo "Installing My App for the first time." fi exit 0

postinstall.sh:

#!/bin/bash
echo "Running post-install script for my-custom-app..."

Example: Create a symlink or start a service

if [ ! -f /usr/local/bin/myapp ]; then sudo ln -s /opt/myapp/bin/myapp.sh /usr/local/bin/myapp echo "Created symlink /usr/local/bin/myapp" fi echo "Installation complete!" exit 0

Now, add them to your FPM command:

fpm -s dir -t deb \
  -n "my-custom-app" -v "1.0.0" --iteration "1" --architecture "amd64" \
  --description "A simple custom application with scripts." \
  --url "https://example.com/my-app" --license "MIT" \
  --maintainer "Your Name " \
  -d "bash" -d "python3" \
  --before-install preinstall.sh \
  --after-install postinstall.sh \
  -C /tmp/my-app-staging/ \
  .

--before-install : Runs before* files are installed.
--after-install : Runs after* files are installed.
* Other options: --before-remove, --after-remove, --before-upgrade, --after-upgrade.

### 5. Packaging a Python Application

If you have a Python project with a setup.py or pyproject.toml:

# In your python_app_root_dir/
fpm -s python -t deb \
  -n "my-python-app" \
  -v "0.5.0" \
  --iteration "1" \
  --architecture "all" \
  --description "A Python application packaged by FPM." \
  --python-bin /usr/bin/python3 \
  --python-pip /usr/bin/pip3 \
  . # Packages the current directory as a Python source

* --python-bin and --python-pip: Specify the Python executable and pip to use for building.
* --architecture "all": Often appropriate for pure Python apps.

Alternative (and often more robust) for Python apps:
1. Install your Python app into a staging directory:

mkdir -p /tmp/python-staging
    pip install . --prefix=/usr/local --root=/tmp/python-staging

2. Then package the staging directory using fpm -s dir:

fpm -s dir -t deb \
      -n "my-python-app" -v "0.5.0" --iteration "1" --architecture "all" \
      --description "A Python application via dir source." \
      -d "python3" \
      -C /tmp/python-staging/ \
      .

This approach gives you more control over the final file layout.

### 6. Marking Configuration Files

For files that users might modify (like myapp.conf), you want the package manager to handle upgrades gracefully (e.g., prompt to merge changes or preserve the user’s version).

fpm -s dir -t deb \
  -n "my-custom-app" -v "1.0.0" --iteration "1" --architecture "amd64" \
  --description "App with a config file." \
  --url "https://example.com/my-app" --license "MIT" \
  --maintainer "Your Name " \
  -d "bash" -d "python3" \
  --config-files /etc/myapp/myapp.conf \
  -C /tmp/my-app-staging/ \
  .

* --config-files : Marks the specified file as a configuration file.

Best Practices and Advanced Tips

* Use a Staging Directory (-C): Always build your package contents in a temporary directory (/tmp/my-app-staging in our example) that directly reflects the final installation path. This makes your FPM commands cleaner and less error-prone.
* Version Control: Keep your my-app-src and any build scripts under version control.
* Automation: Integrate FPM into your build scripts (e.g., Makefile, Rakefile, shell scripts) or CI/CD pipeline (Jenkins, GitLab CI, GitHub Actions) to automate package creation on every release or commit.
* Docker for Build Consistency: To ensure your packages are built consistently and don’t pick up host-specific dependencies, use Docker.

# Dockerfile for building packages
    FROM ruby:3.1-slim-buster
    RUN apt-get update && apt-get install -y build-essential
    RUN gem install fpm
    WORKDIR /build
    COPY . /build
    # Now run your fpm command inside the container
    CMD ["fpm", "-s", "dir", "-t", "deb", "-n", "my-custom-app", ...]

* --prefix: If you want all your files to be installed under a specific directory (e.g., /opt/your-company/my-app), use --prefix /opt/your-company/my-app and then fpm -C /tmp/my-app-staging/opt/your-company/my-app ..
--exclude: For dir sources, you can exclude files or directories using --exclude ".git" --exclude ".log".
* Signing Packages: For production environments, you’ll want to sign your .deb and .rpm packages for security and integrity verification. FPM itself doesn’t directly sign, but you can integrate dpkg-sig or rpmsign into your post-FPM build steps.
* Complex Dependencies: For very complex dependency scenarios, you might still need to consult the native packaging tools’ documentation (dpkg or rpm). FPM handles most common cases well.
* Debugging: If your package isn’t working as expected, examine its contents:
* For .deb: dpkg -c package.deb (list contents), dpkg -I package.deb (show info).
* For .rpm: rpm -qpl package.rpm (list contents), rpm -qpi package.rpm (show info).

—

FPM is a powerful tool that significantly lowers the barrier to entry for creating system packages. By mastering the dir source type and understanding the common flags, you can efficiently package almost any application for multiple Linux distributions.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.