Maintenance: Use precompiled native gems for common platforms.

Approved-by: Martin Gruner <mg@zammad.com>

Co-authored-by: Martin Gruner <mg@zammad.com>
This commit is contained in:
Florian Liebe
2026-09-25 08:49:37 +02:00
parent 5b396c16c1
commit d675cde614
4 changed files with 134 additions and 2 deletions

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env ruby
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
# Ensures that all precompiled gems in Gemfile.lock are available for every platform
# Zammad is built for (packager.io, Docker, CI, development machines), so that none of them
# falls back to compiling its native extension from source.
#
# .gitlab/check_native_gem_platforms.rb [path/to/Gemfile.lock]
require 'bundler'
class CheckNativeGemPlatforms
# `ruby` is the fallback for all unlisted platforms. Bundler silently drops it when a new gem
# is added to the Gemfile, which would break installations on those platforms.
REQUIRED_PLATFORMS = %w[ruby x86_64-linux-gnu aarch64-linux-gnu x86_64-darwin arm64-darwin].freeze
# An update can leave only the source variant of a gem in the lockfile, e.g. when its precompiled
# gems are not published yet. Listing the gems keeps them checked even then.
PRECOMPILED_GEMS = %w[ffi google-protobuf nokogiri pg sass-embedded].freeze
def self.run(lockfile_path)
new(lockfile_path).run
end
def initialize(lockfile_path)
@lockfile = Bundler::LockfileParser.new(File.read(lockfile_path))
@errors = []
end
def run
puts 'Checking native gem platforms in Gemfile.lock…'
check_required_platforms
check_precompiled_gems
if @errors.any?
puts(@errors.map { |error| " #{error}" })
exit 1
end
puts " #{@lockfile.platforms.size} platforms present, #{precompiled_gems.size} precompiled gems (#{precompiled_gems.keys.sort.join(', ')}) available for all #{binary_platforms.size} binary platforms."
puts 'done.'
end
private
def check_required_platforms
platforms = @lockfile.platforms.map(&:to_s)
(REQUIRED_PLATFORMS - platforms).each do |platform|
@errors << "Platform '#{platform}' is missing, restore it with: bundle lock --add-platform #{platform}"
end
end
def check_precompiled_gems
precompiled_gems.each do |name, variants|
# This script runs without ActiveSupport, so Array#exclude? is not available.
@errors << "#{name} is a precompiled gem, add it to PRECOMPILED_GEMS in #{__FILE__}." if !PRECOMPILED_GEMS.include?(name) # rubocop:disable Rails/NegateInclude
binary_platforms.each do |platform|
next if variants.any? { |spec| binary?(spec) && Gem::Platform.new(spec.platform) === platform } # rubocop:disable Style/CaseEquality
@errors << "#{name} #{variants.first.version} has no precompiled gem for #{platform}#{' and would need a Rust toolchain' if rust?(variants)}."
end
end
end
# A gem counts as precompiled if it is listed, or ships at least one platform variant. Rust-based
# gems count even without either, their source variant is recognizable by its rb_sys dependency.
def precompiled_gems
@precompiled_gems ||= @lockfile.specs.group_by(&:name).select do |name, variants|
PRECOMPILED_GEMS.include?(name) || variants.any? { |spec| binary?(spec) } || rust?(variants)
end
end
def rust?(variants)
variants.any? { |spec| !binary?(spec) && spec.dependencies.any? { |dependency| dependency.name == 'rb_sys' } }
end
def binary?(spec)
spec.platform != Gem::Platform::RUBY
end
def binary_platforms
@binary_platforms ||= @lockfile.platforms.reject { |platform| platform == Gem::Platform::RUBY }
end
end
CheckNativeGemPlatforms.run(ARGV[0] || File.expand_path('../Gemfile.lock', __dir__))

View File

@@ -63,6 +63,8 @@
- echo "bundler-audit security check…"
- gem exec bundle-audit update
- gem exec bundle-audit
- echo "Native gem platforms check…"
- .gitlab/check_native_gem_platforms.rb
- echo "Brakeman security check…"
- bin/brakeman -o /dev/stdout -o tmp/brakeman-report.html
- echo "Checking if yard can generate documentation…"

View File

@@ -18,7 +18,7 @@ WORKDIR /opt/zammad
RUN apt-get update -qq && \
apt-get install -y postgresql-common && \
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
apt-get install -y --no-install-recommends libjemalloc2 curl libimlib2 libpq5 nginx gnupg postgresql-client && \
apt-get install -y --no-install-recommends libjemalloc2 curl libimlib2 nginx gnupg postgresql-client && \
apt-get remove -y --purge exim4-base exim4-config bsd-mailx && \
apt-get autoremove -y --purge && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
@@ -48,7 +48,7 @@ SHELL ["/bin/bash", "-o", "errexit", "-o", "pipefail", "-c"]
# Install packages needed to build gems and node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libimlib2-dev libpq-dev libyaml-dev && \
apt-get install --no-install-recommends -y build-essential git libimlib2-dev libssl-dev libyaml-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems

View File

@@ -304,6 +304,10 @@ GEM
faraday-net_http (3.4.4)
net-http (~> 0.5)
ffi (1.17.4)
ffi (1.17.4-aarch64-linux-gnu)
ffi (1.17.4-arm64-darwin)
ffi (1.17.4-x86_64-darwin)
ffi (1.17.4-x86_64-linux-gnu)
ffi-compiler (1.4.2)
ffi (>= 1.15.5)
rake
@@ -318,6 +322,18 @@ GEM
google-protobuf (4.36.1)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.1-aarch64-linux-gnu)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.1-arm64-darwin)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.1-x86_64-darwin)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.1-x86_64-linux-gnu)
bigdecimal
rake (~> 13.3)
graphql (2.6.10)
base64
fiber-storage
@@ -446,6 +462,14 @@ GEM
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-arm64-darwin)
racc (~> 1.4)
nokogiri (1.19.4-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.19.4-x86_64-linux-gnu)
racc (~> 1.4)
nori (2.9.1)
bigdecimal
stringio
@@ -542,6 +566,10 @@ GEM
ast (~> 2.4.1)
racc
pg (1.6.3)
pg (1.6.3-aarch64-linux)
pg (1.6.3-arm64-darwin)
pg (1.6.3-x86_64-darwin)
pg (1.6.3-x86_64-linux)
playwright-ruby-client (1.63.0)
base64
concurrent-ruby (>= 1.1.6)
@@ -756,6 +784,14 @@ GEM
sass-embedded (1.104.1)
google-protobuf (~> 4.31)
rake (>= 13)
sass-embedded (1.104.1-aarch64-linux-gnu)
google-protobuf (~> 4.31)
sass-embedded (1.104.1-arm64-darwin)
google-protobuf (~> 4.31)
sass-embedded (1.104.1-x86_64-darwin)
google-protobuf (~> 4.31)
sass-embedded (1.104.1-x86_64-linux-gnu)
google-protobuf (~> 4.31)
securerandom (0.4.1)
selenium-webdriver (4.49.0)
base64 (~> 0.2)
@@ -892,7 +928,11 @@ GEM
multipart-post (~> 2.0)
PLATFORMS
aarch64-linux-gnu
arm64-darwin
ruby
x86_64-darwin
x86_64-linux-gnu
DEPENDENCIES
PoParser