Skip to content

Building DEB packages with podman & pbuilder #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
06df6f3
Working scaffold for Pgpm::Deb::Builder where every method is a build…
Mar 1, 2025
0a1c5f2
Pgpm::Deb::Builder #prepare and #create_container methods
Mar 2, 2025
676ac4d
Generating debian files used when building a deb package with pbuilder
Mar 2, 2025
11cc4de
Pgpm::Deb::Buider#cleanup stops & removes podman container, removes t…
Mar 2, 2025
0b2dd51
Pgpm::Deb::Builder build with pbuilder, then move .deb file to the host
Mar 2, 2025
62748ff
Pgpb::Deb:Builder pull image unless it exists
Mar 2, 2025
aa0d915
Refactor: replace most hardcoded values for Pgpm::Deb::Spec with valu…
Mar 6, 2025
97d5506
Refactor: make build run all podman commands in one go, then exit
Mar 6, 2025
e26594f
Fix: hardocoded podman image name instead of using Pgpm::Deb::Builder…
Mar 7, 2025
29b258b
Choosing arch suffix for debian packages (it's not the same as in RPM!)
Mar 7, 2025
1f8b9b2
WIP: Timescaledb package with #build_info_for() for Debian and RedHat
Mar 7, 2025
3165c10
Add Package#licence which attempts to read LICENCE file in pkg root a…
Mar 7, 2025
70cb28a
Fix: email address domain name for debian packages
Mar 8, 2025
7dec4dc
Refactor: use Pgpm::Arch.in_scope to determine architecture
Mar 8, 2025
998546d
Rename Pgpm::Package::PGXN#license into #license_text, restore old #l…
Mar 8, 2025
4ac7c0f
Specify default & custom dependencies in packages
Mar 8, 2025
97581e7
Fix: dpkg-buildpackage --build=source step in Pgpm::Deb::Builder shou…
Mar 8, 2025
3b02c92
Proper changelog template for pbuilder packaging
Mar 8, 2025
0a84b1d
Assign .os and .postgres_major_version to @package in Pgpm::RPM::Spec…
Mar 8, 2025
d39f047
Refactor: simpler way to extract postgres major version
Mar 9, 2025
164cc59
Add Dockerfile to build Debian images
Mar 10, 2025
3122b37
Run build commands in podman through `podman exec` so later failed co…
Mar 11, 2025
a1ca046
Patch pbuilder for before running it: prevent cleanup
Mar 11, 2025
0b62193
Fix: use Pgpm::OS.in_scope to determine OS name at the package level
Mar 11, 2025
0e9ce56
Remove 'require "debug"'
Mar 11, 2025
f2339f8
Refactor: use @spec.sources to download and extract sources when buil…
Mar 12, 2025
71bcfdd
Rename & package versioned files into a DEB package, assign proper pa…
Mar 15, 2025
cb58e0a
Create second .deb package (default) which depends on the versioned p…
Mar 16, 2025
8de62ed
Refactor: prepare_artifacts in deb/
Mar 17, 2025
6b56207
Merge branch 'master' into deb
Mar 17, 2025
7b2aee8
Restore accidentally deleted pgpm.spec
Mar 20, 2025
775da7c
Use configure_steps, make_steps & install_steps instead of build_info…
Mar 20, 2025
4bd0cbf
Remove build-essential from the list of default build dependencies
Mar 20, 2025
a99f00b
Accept a larger diversity of filenames for License
Mar 20, 2025
2a4a708
Oops: spelling of "ubuntu"
Mar 21, 2025
de2e49d
Merge branch 'master' into deb
Mar 21, 2025
e14be75
Use Package#native? as condition to add "build-essential" to build_de…
Mar 21, 2025
8d0c175
Refactor: use completely customized debian rules file -- without call…
Mar 26, 2025
8eb2766
Fix: better way to handle custom configure, build and install steps i…
Mar 27, 2025
6fb0482
Fix: timescaledb #build_steps method with custom steps for debian
Mar 27, 2025
a9ac637
Fix: deb package names should not include _, replace them with -
Mar 28, 2025
7f4217c
Fix: os-specific dependency names for pgsodium package
Mar 28, 2025
d0a7907
Oops: remove `require "debug"`
Mar 28, 2025
79e1172
Fix: remove default dependencies for rocky/redheat packages + update …
Mar 28, 2025
3073f73
Fixing rubocop issues
Mar 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 43 additions & 22 deletions exe/pgpm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ require "dry/cli"
require "parallel"
require "etc"

require "debug"

module Pgpm
module CLI
module Commands
Expand Down Expand Up @@ -85,34 +87,53 @@ module Pgpm
exit(1)
end

unless os.is_a?(Pgpm::OS::RedHat)
puts "#{os.name} is not a supported OS at this moment"
exit(1)
end
puts "Building #{pkgs.map { |p| "#{p.name}@#{p.version}" }.join(", ")} for Postgres #{matching_pgver}"
selected_pgdist = Postgres::RedhatBasedPgdg.new(matching_pgver.to_s)

os.with_scope do
arch.with_scope do
selected_pgdist.with_scope do
b = pkgs.reduce(nil) do |c, p|
if p.broken?
puts "Can't build a broken package #{p.name}@#{p.version}"
exit(1)
if os.is_a? Pgpm::OS::Debian
puts "Building #{pkgs.map { |p| "#{p.name}@#{p.version}" }.join(", ")} for Postgres #{matching_pgver}"
selected_pgdist = Postgres::RedhatBasedPgdg.new(matching_pgver.to_s)

os.with_scope do
arch.with_scope do
selected_pgdist.with_scope do
spec = nil
b = pkgs.reduce(nil) do |c, p|
p = Pgpm::ScopedObject.new(p, os, arch)
spec = p.to_deb_spec
end
p = Pgpm::ScopedObject.new(p, os, arch)
spec = p.to_rpm_spec
builder = Pgpm::RPM::Builder.new(spec)
src_builder = builder.source_builder
p = c.nil? ? src_builder : c.and_then(src_builder)
p.and_then(builder.versionless_builder)
builder = Pgpm::Deb::Builder.new(spec)
builder.build
end
end
end
elsif os.is_a? Pgpm::OS::RedHat
puts "Building #{pkgs.map { |p| "#{p.name}@#{p.version}" }.join(", ")} for Postgres #{matching_pgver}"
selected_pgdist = Postgres::RedhatBasedPgdg.new(matching_pgver.to_s)

os.with_scope do
arch.with_scope do
selected_pgdist.with_scope do
b = pkgs.reduce(nil) do |c, p|
if p.broken?
puts "Can't build a broken package #{p.name}@#{p.version}"
exit(1)
end
p = Pgpm::ScopedObject.new(p, os, arch)
spec = p.to_rpm_spec
builder = Pgpm::RedHat::Builder.new(spec)
src_builder = builder.source_builder
p = c.nil? ? src_builder : c.and_then(src_builder)
p.and_then(builder.versionless_builder)
end

srpms = b.call
Pgpm::RPM::Builder.builder(srpms).call
srpms = b.call
Pgpm::RedHat::Builder.builder(srpms).call
end
end
end
else
puts "#{os.name} is not a supported OS at this moment"
exit(1)
end

end

# rubocop:enable Metrics/ParameterLists:
Expand Down
123 changes: 123 additions & 0 deletions lib/pgpm/deb/builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# frozen_string_literal: true

module Pgpm
module Deb
class Builder

def initialize(spec)
@spec = spec
@image_name = "quay.io/qount25/pgpm-debian12"
@container_name = "pgpm-debian12_build-#{Time.now.to_i}_#{rand(10000)}"
end

def build
puts "build()"
prepare
generate_deb_src_files
create_container
run_pbuilder
copy_build_from_container
cleanup
end

private

def prepare
puts "Preparing build..."
puts " Creating container dir structure..."
@pgpm_dir = Dir.mktmpdir
Dir.mkdir "#{@pgpm_dir}/source"
Dir.mkdir "#{@pgpm_dir}/out"
puts " Copying #{@spec.package.source.to_s} to #{@pgpm_dir}/source/"
FileUtils.copy_entry @spec.package.source.to_s, "#{@pgpm_dir}/source/"
end

def create_container
puts "Creating a podman container..."
# Check if image exists
system("podman image exists #{@image_name}")
if $?.to_i > 0 # image doesn't exist -- pull image from a remote repository
puts " Pulling image #{@image_name}..."
system("podman pull quay.io/qount25/pgpm-debian12")
else
puts " Image #{@image_name} already exists! OK"
end

create_opts = " -v #{@pgpm_dir}:/root/pgpm"
create_opts += ":z" if selinux_enabled?
create_opts += " --privileged --annotation run.oci.keep_original_groups=1"
create_opts += " --name #{@container_name} #{@image_name}"

puts " Creating and starting container #{@container_name}"
puts " podman run -dti #{create_opts}"
system("podman run -dti #{create_opts}")
end

def generate_deb_src_files
puts "Generating debian files..."
Dir.mkdir "#{@pgpm_dir}/source/debian"
[:changelog, :control, :copyright, :files, :rules].each do |f|
puts " -> #{@pgpm_dir}/source/debian/#{f}"
File.write "#{@pgpm_dir}/source/debian/#{f}", @spec.generate(f)
end
File.chmod 0740, "#{@pgpm_dir}/source/debian/rules" # rules file must be executable
end

def run_pbuilder
puts "Building a .deb package with pbuilder..."
cmd_pref = "podman exec -w /root/pgpm/source #{@container_name} "
system("#{cmd_pref} dpkg-buildpackage --build=source")
exit(1) if $?.to_i > 0
dsc_fn = "#{@spec.package.name}-#{@spec.package.version.to_s}_0-1.dsc"
system("#{cmd_pref} fakeroot pbuilder build ../#{dsc_fn}")
exit(1) if $?.to_i > 0
end

def copy_build_from_container
puts "Moving .deb file from podman container into current directory..."
cmd_pref = "podman exec #{@container_name} "
arch = "amd64"
deb_fn = "#{@spec.full_pkg_name}.deb"
system("#{cmd_pref} mv /var/cache/pbuilder/result/#{deb_fn} /root/pgpm/out/")
FileUtils.mv("#{@pgpm_dir}/out/#{deb_fn}", Dir.pwd)
end

def run_container_command(cmd)
end

def cleanup
puts "Cleaning up..."

# Stop and destroy podman container
puts " Stopping podman container: #{@container_name}"
system("podman stop #{@container_name}")
puts " Destroying podman container: #{@container_name}"
system("podman container rm #{@container_name}")

# Remove temporary files
#
# Make sure @pgpm_dir starts with "/tmp/" or we may accidentally
# delete something everything! You can never be sure!
if @pgpm_dir.start_with?("/tmp/")
puts " Removing temporary files in #{@pgpm_dir}"
FileUtils.rm_rf(@pgpm_dir)
else
puts "WARNING: will not remove temporary files, strange path: \"#{@pgpm_dir}\""
end
end

# Needed because SELinux requires :z suffix for mounted directories to
# be accessible -- otherwise we get "Permission denied" when cd into a
# mounted dir inside the container.
def selinux_enabled?
# This returns true or false by itself
system("sestatus | grep 'SELinux status' | grep -o 'enabled'")
end

def safe_package_name
@spec.package.name.gsub(%r{/}, "__")
end

end
end
end
79 changes: 79 additions & 0 deletions lib/pgpm/deb/spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

require "digest"
require "open-uri"
require "erb"

module Pgpm
module Deb
class Spec
attr_reader :package, :release, :postgres_version, :postgres_distribution

def initialize(package)
@package = package
@release = 1

@postgres_distribution = Pgpm::Postgres::Distribution.in_scope
end

def sources
@package.sources
end

def generate(template_name)
fn = "#{__dir__}/templates/#{template_name}.erb"
raise "No such template: #{fn}" unless File.exist?(fn)
erb = ERB.new(File.read(fn))
erb.result(binding)
end

def deps
["postgresql-#{postgres_version}"]
end

def build_deps
[
"postgresql-#{postgres_version}",
"build-essential",
"postgresql-#{postgres_version}",
"postgresql-server-dev-#{postgres_version}",
"postgresql-common"
]
end

def postgres_version
17
end

def source_version
@package.version.to_s
end

def source_name
@package.name
end

def full_pkg_name
"#{@package.name}-#{@package.version.to_s}_0-1_#{arch}"
end

def deb_version
"0.1.0"
end

def arch
"amd64"
end

def description
@package.description
end

# Whatever is returned from this method gets added to the "rules" file.
def rules_amendments
"#"
end

end
end
end
5 changes: 5 additions & 0 deletions lib/pgpm/deb/templates/changelog.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= source_name %>-<%= source_version %> (0-1) UNRELEASED; urgency=medium

* Initial release. (Closes: #nnnn) <nnnn is the bug number of your ITP>

-- PGPM Debian maintainer <[email protected]> Fri, 21 Feb 2025 21:08:08 +0000
13 changes: 13 additions & 0 deletions lib/pgpm/deb/templates/control.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Source: <%= source_name %>-<%= source_version %>
Section: libs
Priority: optional
Maintainer: PGPM Debian maintainer <[email protected]>
Rules-Requires-Root: no
Build-Depends: debhelper-compat (= 13), <%= build_deps.join(", ") %>
Standards-Version: 4.6.2

Package: <%= source_name %>-<%= source_version %>
Depends: <%= deps.join(", ") %>
Section: libdevel
Architecture: <%= arch %>
Description: <%= description %>
48 changes: 48 additions & 0 deletions lib/pgpm/deb/templates/copyright.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of this information should be extractable from the package.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean. This line looks like some XML schema definition that should always be the same. No?

Source: <url://example.com>
Upstream-Name: <%= source_name %>-<%= source_version %>
Upstream-Contact: <preferred name and address to reach the upstream project>

Files:
*
Copyright:
<years> <put author's name and email here>
<years> <likewise for another author>
License: <special license>
<Put the license of the package here indented by 1 space>
<This follows the format of Description: lines in control file>
.
<Including paragraphs>

# If you want to use GPL v2 or later for the /debian/* files use
# the following clauses, or change it to suit. Delete these two lines
Files:
debian/*
Copyright:
2025 PGPM Debian maintainer <[email protected]>
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
Comment:
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
# Please avoid picking licenses with terms that are more restrictive than the
# packaged work, as it may make Debian's contributions unacceptable upstream.
#
# If you need, there are some extra license texts available in two places:
# /usr/share/debhelper/dh_make/licenses/
# /usr/share/common-licenses/

1 change: 1 addition & 0 deletions lib/pgpm/deb/templates/files.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= source_name %>-<%= source_version %>_0-1_source.buildinfo libs optional
21 changes: 21 additions & 0 deletions lib/pgpm/deb/templates/rules.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/make -f

# See debhelper(7) (uncomment to enable).
# Output every command that modifies files on the build system.
#export DH_VERBOSE = 1


# See FEATURE AREAS in dpkg-buildflags(1).
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all

# See ENVIRONMENT in dpkg-buildflags(1).
# Package maintainers to append CFLAGS.
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# Package maintainers to append LDFLAGS.
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed


%:
dh $@

<%= self.rules_amendments %>
Loading
Loading