Skip to content
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

Building DEB packages with podman & pbuilder #8

Merged
merged 45 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from 19 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
120 changes: 120 additions & 0 deletions lib/pgpm/deb/builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# frozen_string_literal: true

module Pgpm
module Deb
class Builder

def initialize(spec)
@spec = spec
@container_name = "pgpm-debian_build-#{Time.now.to_i}_#{rand(10000)}"
end

def build
prepare
generate_deb_src_files
pull_image
run_build
copy_build_from_container
cleanup
end

private

# Depends on postgres version and arch
def image_name
"quay.io/qount25/pgpm-debian-pg#{@spec.package.postgres_major_version}-#{@spec.arch}"
end

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 pull_image
puts "Checking if podman image exists..."
# 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 " No. Pulling image #{image_name}..."
system("podman pull #{image_name}")
else
puts " Yes, image #{image_name} already exists! OK"
end
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_build
# podman run options
create_opts = " -v #{@pgpm_dir}:/root/pgpm"
create_opts += ":z" if selinux_enabled?
create_opts += " --privileged"
create_opts += " --name #{@container_name} #{image_name}"

dsc_fn = "#{@spec.package.name}-#{@spec.package.version.to_s}_0-1.dsc"
deb_fn = "#{@spec.full_pkg_name}.deb"

# commands to run
cmds = " /bin/bash -c 'cd /root/pgpm/source"
cmds += " && dpkg-buildpackage --build=source -d" # -d flag helps with dependencies error
cmds += " && fakeroot pbuilder build ../#{dsc_fn}"
cmds += " && mv /var/cache/pbuilder/result/#{deb_fn} /root/pgpm/out/'"

puts " Creating and starting container #{@container_name} & running pbuilder"
puts "podman run -it #{create_opts} #{cmds}"
system("podman run -it #{create_opts} #{cmds}")
exit(1) if $?.to_i > 0
end

def copy_build_from_container
puts "Moving .deb file from podman container into current directory..."
deb_fn = "#{@spec.full_pkg_name}.deb"
FileUtils.mv("#{@pgpm_dir}/out/#{deb_fn}", Dir.pwd)
end

def cleanup
puts "Cleaning up..."

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
52 changes: 52 additions & 0 deletions lib/pgpm/deb/spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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)
@postgres_distribution = Pgpm::Postgres::Distribution.in_scope
@package = package
@package.postgres_major_version = @postgres_distribution.version.split(".")[0]
@package.os = "debian"
@release = 1
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 source_version
@package.version.to_s
end

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

def arch
# https://memgraph.com/blog/ship-it-on-arm64-or-is-it-aarch64
# Debian suffixes are "amd64" and "arm64". Here we translate:
case Pgpm::Arch.in_scope.name
when "amd64", "x86_64"
"amd64"
when "aarch64", "arm64"
"arm64"
end
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 @@
<%= self.package.name %>-<%= source_version %> (0-1) UNRELEASED; urgency=medium

* Version <%= source_version %> package release.

-- PGPM Debian maintainer <[email protected]> <%= Time.now.to_s %>
14 changes: 14 additions & 0 deletions lib/pgpm/deb/templates/control.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Source: <%= self.package.name %>-<%= source_version %>
Description: <%= self.package.description %>
Section: libs
Priority: optional
Maintainer: PGPM Debian maintainer <[email protected]>
Rules-Requires-Root: no
Build-Depends: debhelper-compat (= 13), <%= self.package.build_dependencies.join(", ") %>
Standards-Version: 4.6.2

Package: <%= self.package.name %>-<%= source_version %>
Depends: <%= self.package.dependencies.join(", ") %>
Section: libdevel
Architecture: <%= arch %>
Description: <%= self.package.description %>
9 changes: 9 additions & 0 deletions lib/pgpm/deb/templates/copyright.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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: <%= self.package.name %>-<%= self.package.name %>
Upstream-Contact: <preferred name and address to reach the upstream project>

Files:
*
Copyright:
<%= self.package.license %>
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 @@
<%= self.package.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.package.build_info[:rules] %>
35 changes: 35 additions & 0 deletions lib/pgpm/os/debian.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require "rbconfig"

module Pgpm
module OS
class Debian < Pgpm::OS::Linux
def self.auto_detect
# TODO: distinguish between flavors of Debian
Debian12.new
end

def self.name
"debian"
end

def mock_config; end
end

class Debian12 < Pgpm::OS::Debian
def self.name
"debian-12"
end

def self.builder
Pgpm::Debian::Builder
end

def mock_config
"debian-12-#{Pgpm::Arch.in_scope.name}+pgdg"
end

end
end
end
13 changes: 7 additions & 6 deletions lib/pgpm/package/building.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ def configure_steps
[]
end

def build_steps
[]
end

def install_steps
[]
def build_info
case @os
when "debian", "ubuntu"
{ rules: "" }
when "rocky", "redhat", "fedora"
{ build_steps: [], install_steps: [] }
end
end

def source_url_directory_name
Expand Down
Loading