From 8c39b78c40157830136de72d32f7f651d9b6c0d4 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Sun, 12 Jan 2025 17:57:31 +0900 Subject: [PATCH] debian fedora: add support for APT/Yum repository "dnf module {enable,disable}" is also supported. --- README.md | 123 +++++++++++++++++- dockerfiles/run-test.sh | 19 ++- lib/rubygems-requirements-system/installer.rb | 51 +------- .../os-release.rb | 2 +- .../platform/base.rb | 21 ++- .../platform/debian.rb | 71 +++++++++- .../platform/fedora.rb | 80 +++++++++++- .../platform/red-hat-enterprise-linux.rb | 8 +- .../requirements-parser.rb | 39 +++++- .../system-repository.rb | 47 +++++++ .../dummy-postgresql-17.gemspec | 49 +++++++ .../ext/dummy-postgresql-17/extconf.rb | 22 ++++ test/test-requirements-parser.rb | 70 ++++++++++ 13 files changed, 531 insertions(+), 71 deletions(-) create mode 100644 lib/rubygems-requirements-system/system-repository.rb create mode 100644 test/fixture/dummy-postgresql-17/dummy-postgresql-17.gemspec create mode 100644 test/fixture/dummy-postgresql-17/ext/dummy-postgresql-17/extconf.rb diff --git a/README.md b/README.md index 2c94e26..7371e0b 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Gem::Specification.new do |spec| # # On Ubuntu 24.04: # https://packages.groonga.org/%{distribution}/groonga-apt-source-latest-%{code_name}.deb -> - # https://packages.groonga.org/ubuntu/groonga-apt-source-latest-nobole.deb + # https://packages.groonga.org/ubuntu/groonga-apt-source-latest-noble.deb spec.requirements << "system: groonga: debian: https://packages.groonga.org/%{distribution}/groonga-apt-source-latest-%{code_name}.deb" # Install libgroonga-dev from the registered repository. spec.requirements << "system: groonga: debian: libgroonga-dev" @@ -207,6 +207,127 @@ Gem::Specification.new do |spec| end ``` +### Install repositories + +You can install APT/Yum repositories by specifying metadata. + +You need to specify multiple metadata for one repository. So you need +to use multiple `spec.requirements` for one repository. Here is the +syntax for one repository: + +```ruby +spec.requirements << "system: #{package}: #{platform}: repository: #{key1}: #{value1}" +spec.requirements << "system: #{package}: #{platform}: repository: #{key2}: #{value2}" +# ... +``` + +You must specify at least `id` as `key`. For example: + +``` +spec.requirements << "system: libpq: debian: repository: id: pgdg" +``` + +You can start another repository metadata by starting `id` metadata +for another repository: + +```ruby +spec.requirements << "system: #{package}: #{platform}: repository: id: repository1 +spec.requirements << "system: #{package}: #{platform}: repository: #{key1_1}: #{value1_1}" +spec.requirements << "system: #{package}: #{platform}: repository: #{key1_2}: #{value1_2}" +# ... +spec.requirements << "system: #{package}: #{platform}: repository: id: repository2 +spec.requirements << "system: #{package}: #{platform}: repository: #{key2_1}: #{value2_1}" +spec.requirements << "system: #{package}: #{platform}: repository: #{key2_2}: #{value2_2}" +# ... +``` + +Here are metadata for a APT repository: + +* `compoennts`: Optional. The default is `main`. +* `signed-by`: Optional. The URL of armored keyring that is used for + signing this repository. +* `suites`: Optional. The default is `%{code_name}`. +* `types`: Optional. The default is `deb`. +* `uris`: Required. The URLs that provide this repository. + +See also: https://wiki.debian.org/SourcesList + +Here are metadata for a Yum repository: + +* `baseurl`: Required. The base URL that provides this repository. +* `gpgcheck`: Optional. No default. +* `gpgkey`: Optional. The URL of GPG key that is used for signing this + repository. +* `name`: Optional. The name of this repository. + +See also: TODO: Is there any URL that describes the specification of +`.repo` file? + +You can use placeholder for metadata values with `%{KEY}` format. + +Here are available placeholders: + +`debian` family platforms: + +* `distribution`: The `ID` value in `/etc/os-release`. It's `debian`, + `ubuntu` and so on. +* `code_name`: The `VERSION_CODENAME` value in `/etc/os-release`. It's + `bookworm`, `noble` and so on. +* `version`: The `VERSION_ID` value in `/etc/os-release`. It's `12`, + `24.04` and so on. + +`fedora` family platforms: + +* `distribution`: The `ID` value in `/etc/os-release`. It's `fedora`, + `rhel`, `almalinux` and so on. +* `major_version`: The major part of `VERSION_ID` value in + `/etc/os-release`. It's `41`, `9` and so on. +* `version`: The `VERSION_ID` value in `/etc/os-release`. It's `41`, + `9.5` and so on. + +Here is an example that uses this feature for adding a new repository: + +```ruby +Gem::Specification.new do |spec| + # ... + + # Install PostgreSQL's APT repository on Debian family platforms. + # + # %{code_name} is placeholders. + # + # On Debian GNU/Linux bookworm: + # %{code_name}-pgdg -> + # bookworm-pgdg + # + # On Ubuntu 24.04: + # %{code_name}-pgdg -> + # noble-pgdg + spec.requirements << "system: libpq: debian: repository: id: pgdg" + spec.requirements << "system: libpq: debian: repository: uris: https://apt.postgresql.org/pub/repos/apt" + spec.requirements << "system: libpq: debian: repository: signed-by: https://www.postgresql.org/media/keys/ACCC4CF8.asc" + spec.requirements << "system: libpq: debian: repository: suites: %{code_name}-pgdg" + spec.requirements << "system: libpq: debian: repository: components: main" + # Install libpq-dev from the registered repository. + spec.requirements << "system: libpq: debian: libpq-dev" + + # Install PostgreSQL's Yum repository on RHEL family platforms: + spec.requirements << "system: libpq: rhel: repository: id: pgdg17" + spec.requirements << "system: libpq: rhel: repository: name: PostgreSQL 17 $releasever - $basearch" + spec.requirements << "system: libpq: rhel: repository: baseurl: https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-$releasever-$basearch" + spec.requirements << "system: libpq: rhel: repository: gpgcheck: 1" + spec.requirements << "system: libpq: rhel: repository: gpgkey: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL" + # You can disable built-in "postgresql" module by "module: disable: + # postgresql". + spec.requirements << "system: libpq: rhel: module: disable: postgresql" + # Install postgresql17-devel from the registered repository. But + # users can't find "libpq.pc" provided by postgresql17-devel without + # PKG_CONFIG_PATH=/usr/pgsql-17/lib/pkgconfig ... + spec.requirements << "system: libpq: rhel: postgresql17-devel" + + # ... +end +``` + ## Configurations ### Opt-out diff --git a/dockerfiles/run-test.sh b/dockerfiles/run-test.sh index b0b913f..ecb9ddf 100755 --- a/dockerfiles/run-test.sh +++ b/dockerfiles/run-test.sh @@ -80,26 +80,37 @@ group_end for test_gem in "${test_gems[@]}"; do pushd ${test_gem} - group_begin "Test: $(basename ${test_gem}): Prepare" + gem_name=$(basename ${test_gem}) + group_begin "Test: ${gem_name}: Prepare" gem build *.gemspec + case "${gem_name}" in + dummy-postgresql-*) + # This is only for RHEL. PGDG's Yum repository uses + # /usr/pgsql-${VERSION}/lib/pkgconfig/ not /usr/lib64/pkgconfig/ + # for libpq.pc. + postgresql_version=${gem_name#dummy-postgresql-} + postgresql_pkg_config_path=/usr/pgsql-${postgresql_version}/lib/pkgconfig + export PKG_CONFIG_PATH="${postgresql_pkg_config_path}${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}" + ;; + esac group_end # Must be failed for disabled_value in 0 no NO false FALSE; do - group_begin "Test: $(basename ${test_gem}): Disable by env: ${disabled_value}" + group_begin "Test: ${gem_name}: Disable by env: ${disabled_value}" if RUBYGEMS_REQUIREMENTS_SYSTEM=${disabled_value} \ gem install "${gem_install_options[@]}" ./*.gem; then exit 1 fi group_end done - group_begin "Test: $(basename ${test_gem}): Disable by configuration" + group_begin "Test: ${gem_name}: Disable by configuration" # Must be failed if gem install "${gem_install_options[@]}" \ --config-file=${disable_gemrc} ./*.gem; then exit 1 fi group_end - group_begin "Test: $(basename ${test_gem}): Default" + group_begin "Test: ${gem_name}: Default" # Must be succeeded gem install "${gem_install_options[@]}" ./*.gem group_end diff --git a/lib/rubygems-requirements-system/installer.rb b/lib/rubygems-requirements-system/installer.rb index c1daeec..a09ace3 100644 --- a/lib/rubygems-requirements-system/installer.rb +++ b/lib/rubygems-requirements-system/installer.rb @@ -34,7 +34,8 @@ def initialize(gemspec) def install return true unless enabled? - requirements = parse_requirements(@gemspec.requirements) + parser = RequirementsParser.new(@gemspec.requirements, @platform) + requirements = parser.parse requirements.all? do |requirement| next true if requirement.satisfied? @platform.install(requirement) @@ -58,53 +59,5 @@ def enabled? true end - - def parse_requirements(gemspec_requirements) - all_packages_set = {} - requirements = {} - gemspec_requirements.each do |gemspec_requirement| - components = gemspec_requirement.split(/: +/, 4) - next unless components.size == 4 - - id, raw_packages, platform, system_package = components - next unless id == "system" - - packages = parse_packages(raw_packages) - next if packages.empty? - - all_packages_set[packages] = true - - next unless @platform.target?(platform) - requirements[packages] ||= [] - requirements[packages] << system_package - end - (all_packages_set.keys - requirements.keys).each do |not_used_packages| - system_packages = @platform.default_system_packages(not_used_packages) - next if system_packages.nil? - requirements[not_used_packages] = system_packages - end - requirements.collect do |packages, system_packages| - Requirement.new(packages, system_packages) - end - end - - def parse_packages(raw_packages) - packages = raw_packages.split(/\s*\|\s*/).collect do |raw_package| - Package.parse(raw_package) - end - # Ignore this requirement if any invalid package is included. - # We must not report an error for this because - # Gem::Specification#requirements is a free form - # configuration. So there are configuration values that use - # "system: ..." but not for this plugin. We can report a - # warning instead. - packages.each do |package| - unless package.valid? - # TODO: Report a warning - return [] - end - end - packages - end end end diff --git a/lib/rubygems-requirements-system/os-release.rb b/lib/rubygems-requirements-system/os-release.rb index e55de9f..d7de3f0 100644 --- a/lib/rubygems-requirements-system/os-release.rb +++ b/lib/rubygems-requirements-system/os-release.rb @@ -34,7 +34,7 @@ def id end def id_like - (self["ID_LIKE"] || "").split(/ /) + (self["ID_LIKE"] || "").split(/\s+/) end def version diff --git a/lib/rubygems-requirements-system/platform/base.rb b/lib/rubygems-requirements-system/platform/base.rb index 4eb2b5a..4269765 100644 --- a/lib/rubygems-requirements-system/platform/base.rb +++ b/lib/rubygems-requirements-system/platform/base.rb @@ -34,6 +34,14 @@ def default_system_packages(packages) nil end + def valid_system_package?(package) + true + end + + def valid_system_repository?(repository) + false + end + def install(requirement) synchronize do requirement.system_packages.any? do |package| @@ -109,12 +117,19 @@ def install_package(package) end def run_command_line(package, action, command_line) + if package.is_a?(SystemRepository) + package_label = "repository(#{package.id})" + else + package_label = package + end + prefix = "requirements: system: #{package_label}: #{action}" + say("#{prefix}: Start") failed_to_get_super_user_priviledge = false if have_priviledge? succeeded = system(*command_line) else if sudo - prompt = "[sudo] password for %u to #{action} <#{package}>: " + prompt = "[sudo] password for %u to #{action} <#{package_label}>: " command_line = [sudo, "-p", prompt, *command_line] succeeded = system(*command_line) else @@ -127,13 +142,13 @@ def run_command_line(package, action, command_line) else result_message = succeeded ? "succeeded" : "failed" end - say("#{action.capitalize} '#{package}' system package: #{result_message}") + say("#{prefix}: #{result_message}") unless succeeded escaped_command_line = command_line.collect do |part| Shellwords.escape(part) end - alert_warning("'#{package}' system package is required.") + alert_warning("#{prefix}: Failed.") alert_warning("Run the following command " + "to #{action} required system package: " + escaped_command_line.join(" ")) diff --git a/lib/rubygems-requirements-system/platform/debian.rb b/lib/rubygems-requirements-system/platform/debian.rb index 910fca3..d663f3b 100644 --- a/lib/rubygems-requirements-system/platform/debian.rb +++ b/lib/rubygems-requirements-system/platform/debian.rb @@ -42,6 +42,14 @@ def target?(platform) end end + def valid_system_repository?(repository) + uris = repository["uris"] + return false if uris.nil? + return false if uris.empty? + + true + end + private def prepare_command_lines(package) [ @@ -50,8 +58,62 @@ def prepare_command_lines(package) end def install_command_line(package) + if package.is_a?(SystemRepository) + install_command_line_repository(package) + else + install_command_line_package(package) + end + end + + def install_command_line_repository(repository) + temporary_file_base_name = [ + "rubygems-requirements-system-debian-#{repository.id}", + ".sources", + ] + sources = create_temporary_file(temporary_file_base_name) + signed_by_url = resolve_value_template(repository["signed-by"]) + if signed_by_url + signed_by = URI.open(signed_by_url) do |response| + response.read + end + else + signed_by = nil + end + suites = repository["suites"] || "%{code_name}" + components = repository["components"] || "main" + { + "Types" => repository["types"] || "deb", + "URIs" => resolve_value_template(repository["uris"]), + "Suites" => resolve_value_template(suites), + "Components" => resolve_value_template(components), + "Signed-By" => signed_by, + }.each do |key, value| + next if value.nil? + if value.include?("\n") + sources.puts("#{key}:") + value.each_line do |line| + if line.empty? + sources.puts(" .") + else + sources.puts(" #{line}") + end + end + else + sources.puts("#{key}: #{value}") + end + end + sources.close + FileUtils.chmod("go+r", sources.path) + [ + "cp", + sources.path, + "/etc/apt/sources.list.d/#{repository.id}.sources", + ] + end + + def install_command_line_package(package) if package.start_with?("https://") - package_url = resolve_package_url_template(package) + package_url = resolve_value_template(package) temporary_file_base_name = [ "rubygems-requirements-system-debian", File.extname(package), @@ -61,7 +123,6 @@ def install_command_line(package) IO.copy_stream(response, local_package) end local_package.close - @temporary_files << local_package FileUtils.chmod("go+r", local_package.path) package = local_package.path end @@ -80,9 +141,11 @@ def need_super_user_priviledge? true end - def resolve_package_url_template(package_url_template) + def resolve_value_template(template) + return nil if template.nil? + os_release = OSRelease.new - package_url_template % { + template % { distribution: os_release.id, code_name: os_release.version_codename, version: os_release.version_id, diff --git a/lib/rubygems-requirements-system/platform/fedora.rb b/lib/rubygems-requirements-system/platform/fedora.rb index cb7aa42..d9ebd6c 100644 --- a/lib/rubygems-requirements-system/platform/fedora.rb +++ b/lib/rubygems-requirements-system/platform/fedora.rb @@ -31,25 +31,97 @@ def target?(platform) platform == "fedora" end + def valid_system_package?(package) + return true unless package.start_with?("module:") + + action, target = parse_module_system_package(package) + case action + when "enable", "disable" + else + return false + end + return false if target.nil? + + true + end + + def valid_system_repository?(repository) + baseurl = repository["baseurl"] + return false if baseurl.nil? + return false if baseurl.empty? + + true + end + def default_system_packages(packages) packages.collect {|package| "pkgconfig(#{package.id})"} end private + def parse_module_system_package(package) + # "module: disable: postgresql" -> + # ["module", "disable", "postgresql"] + _, action, target = package.split(/:\s*/, 3) + [action, target] + end + def install_command_line(package) + if package.is_a?(SystemRepository) + install_command_line_repository(package) + elsif package.start_with?("module:") + install_command_line_module(package) + else + install_command_line_package(package) + end + end + + def install_command_line_repository(repository) + temporary_file_base_name = [ + "rubygems-requirements-system-fedora-#{repository.id}", + ".repo", + ] + repo = create_temporary_file(temporary_file_base_name) + repo.puts("[#{repository.id}]") + { + "name" => resolve_value_template(repository["name"]), + "baseurl" => resolve_value_template(repository["baseurl"]), + "enabled" => "1", + "gpgcheck" => repository["gpgcheck"], + "gpgkey" => resolve_value_template(repository["gpgkey"]), + }.each do |key, value| + next if value.nil? + repo.puts("#{key}=#{value}") + end + repo.close + FileUtils.chmod("go+r", repo.path) + [ + "cp", + repo.path, + "/etc/yum.repos.d/#{repository.id}.repo", + ] + end + + def install_command_line_module(package) + action, target = parse_module_system_package(package) + ["dnf", "-y", "module", action, target] + end + + def install_command_line_package(package) if package.start_with?("https://") - package = resolve_package_url_template(package) + package = resolve_value_template(package) end - ["dnf", "install", "-y", package] + ["dnf", "-y", "install", package] end def need_super_user_priviledge? true end - def resolve_package_url_template(package_url_template) + def resolve_value_template(template) + return nil if template.nil? + os_release = OSRelease.new - package_url_template % { + template % { distribution: os_release.id, major_version: major_version.to_s, version: os_release.version_id, diff --git a/lib/rubygems-requirements-system/platform/red-hat-enterprise-linux.rb b/lib/rubygems-requirements-system/platform/red-hat-enterprise-linux.rb index b2408d2..6dbb18f 100644 --- a/lib/rubygems-requirements-system/platform/red-hat-enterprise-linux.rb +++ b/lib/rubygems-requirements-system/platform/red-hat-enterprise-linux.rb @@ -39,16 +39,16 @@ def target?(platform) end private - def install_command_line(package) + def install_command_line_package(package) if package.start_with?("https://") package = resolve_package_url_template(package) end if major_version >= 9 - ["dnf", "install", "--enablerepo=crb", "-y", package] + ["dnf", "-y", "install", "--enablerepo=crb", package] elsif major_version >= 8 - ["dnf", "install", "--enablerepo=powertools", "-y", package] + ["dnf", "-y", "install", "--enablerepo=powertools", package] else - ["yum", "install", "-y", package] + ["yum", "-y", "install", package] end end diff --git a/lib/rubygems-requirements-system/requirements-parser.rb b/lib/rubygems-requirements-system/requirements-parser.rb index 69e65f4..032b5d7 100644 --- a/lib/rubygems-requirements-system/requirements-parser.rb +++ b/lib/rubygems-requirements-system/requirements-parser.rb @@ -15,6 +15,7 @@ require_relative "package" require_relative "requirement" +require_relative "system-repository" module RubyGemsRequirementsSystem class RequirementsParser @@ -27,7 +28,7 @@ def parse all_packages_set = {} requirements = {} @gemspec_requirements.each do |gemspec_requirement| - components = gemspec_requirement.split(/: +/, 4) + components = gemspec_requirement.split(/:\s*/, 4) next unless components.size == 4 id, raw_packages, platform, system_package = components @@ -48,6 +49,7 @@ def parse requirements[not_used_packages] = system_packages end requirements.collect do |packages, system_packages| + system_packages = detect_system_repositories(system_packages) Requirement.new(packages, system_packages) end end @@ -71,5 +73,40 @@ def parse_packages(raw_packages) end packages end + + def detect_system_repositories(original_system_packages) + system_packages = [] + repository_properties = {} + + flush_repository = lambda do + return if repository_properties.empty? + properties = repository_properties + repository_properties = {} + + id = properties.delete("id") + return if id.nil? + repository = SystemRepository.new(id, properties) + return unless @platform.valid_system_repository?(repository) + system_packages << repository + end + + original_system_packages.each do |system_package| + unless system_package.start_with?("repository:") + flush_repository.call + next unless @platform.valid_system_package?(system_package) + system_packages << system_package + next + end + _, key, value = system_package.strip.split(/:\s*/, 3) + next if value.empty? + if key == "id" and repository_properties.key?("id") + flush_repository.call + end + repository_properties[key] = value + end + flush_repository.call + + system_packages + end end end diff --git a/lib/rubygems-requirements-system/system-repository.rb b/lib/rubygems-requirements-system/system-repository.rb new file mode 100644 index 0000000..d786f92 --- /dev/null +++ b/lib/rubygems-requirements-system/system-repository.rb @@ -0,0 +1,47 @@ +# Copyright (C) 2025 Ruby-GNOME Project Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +module RubyGemsRequirementsSystem + class SystemRepository + attr_reader :id + protected attr_reader :properties + def initialize(id, properties) + @id = id + @properties = properties + end + + def ==(other) + other.is_a?(self.class) and + @id == other.id and + @properties == other.properties + end + + def eql?(other) + self == other + end + + def hash + [@id, @properties].hash + end + + def [](key) + @properties[key] + end + + def each(&block) + @properties.each(&block) + end + end +end diff --git a/test/fixture/dummy-postgresql-17/dummy-postgresql-17.gemspec b/test/fixture/dummy-postgresql-17/dummy-postgresql-17.gemspec new file mode 100644 index 0000000..66b728d --- /dev/null +++ b/test/fixture/dummy-postgresql-17/dummy-postgresql-17.gemspec @@ -0,0 +1,49 @@ +# -*- ruby -*- +# +# Copyright (C) 2025 Ruby-GNOME Project Team +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +require "open-uri" +require "json" + +Gem::Specification.new do |spec| + spec.name = "dummy-postgresql-17" + spec.version = "1.0.0" + spec.authors = ["Sutou Kouhei"] + spec.email = ["kou@cozmixng.org"] + spec.licenses = ["LGPL-3.0-or-later"] + spec.summary = "Dummy gem that uses PostgreSQL 17" + spec.description = "This is for testing rubygems-requirements-system" + spec.extensions = ["ext/dummy-postgresql-17/extconf.rb"] + spec.files = ["ext/dummy-postgresql-17/extconf.rb"] + + spec.add_runtime_dependency("rubygems-requirements-system") + + prefix = "system: libpq >= 17" + spec.requirements << "#{prefix}: debian: repository: id: pgdg" + spec.requirements << "#{prefix}: debian: repository: uris: https://apt.postgresql.org/pub/repos/apt" + spec.requirements << "#{prefix}: debian: repository: signed-by: https://www.postgresql.org/media/keys/ACCC4CF8.asc" + spec.requirements << "#{prefix}: debian: repository: suites: %{code_name}-pgdg" + spec.requirements << "#{prefix}: debian: repository: components: main" + spec.requirements << "#{prefix}: debian: libpq-dev" + spec.requirements << "#{prefix}: rhel: pkgconfig(libssl)" + spec.requirements << "#{prefix}: rhel: repository: id: pgdg17" + spec.requirements << "#{prefix}: rhel: repository: name: PostgreSQL 17 $releasever - $basearch" + spec.requirements << "#{prefix}: rhel: repository: baseurl: https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-$releasever-$basearch" + spec.requirements << "#{prefix}: rhel: repository: gpgcheck: 1" + spec.requirements << "#{prefix}: rhel: repository: gpgkey: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL" + spec.requirements << "#{prefix}: rhel: module: disable: postgresql" + spec.requirements << "#{prefix}: rhel: postgresql17-devel" +end diff --git a/test/fixture/dummy-postgresql-17/ext/dummy-postgresql-17/extconf.rb b/test/fixture/dummy-postgresql-17/ext/dummy-postgresql-17/extconf.rb new file mode 100644 index 0000000..2037716 --- /dev/null +++ b/test/fixture/dummy-postgresql-17/ext/dummy-postgresql-17/extconf.rb @@ -0,0 +1,22 @@ +# Copyright (C) 2025 Ruby-GNOME Project Team +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +require "mkmf" + +require "pkg-config" + +exit(false) unless PKGConfig.have_package("libpq", 17) + +create_makefile("dummy_postgresql_17") diff --git a/test/test-requirements-parser.rb b/test/test-requirements-parser.rb index 09decba..55e7300 100644 --- a/test/test-requirements-parser.rb +++ b/test/test-requirements-parser.rb @@ -20,6 +20,7 @@ class TestRequiremtsParser < Test::Unit::TestCase Platform = RubyGemsRequirementsSystem::Platform Requirement = RubyGemsRequirementsSystem::Requirement RequirementsParser = RubyGemsRequirementsSystem::RequirementsParser + SystemRepository = RubyGemsRequirementsSystem::SystemRepository def setup @platform = Platform::Ubuntu.new @@ -91,4 +92,73 @@ def test_ppa ], parse(gemspec_requirements)) end + + def test_repository + prefix = "system: libpq: debian:" + gemspec_requirements = [ + "#{prefix} repository: id: pgdg", + "#{prefix} repository: components: main", + "#{prefix} repository: signed-by: https://www.postgresql.org/media/keys/ACCC4CF8.asc", + "#{prefix} repository: suites: %{code_name}-pgdg", + "#{prefix} repository: uris: https://apt.postgresql.org/pub/repos/apt", + "#{prefix} libpq-dev", + ] + repository_properties = { + "components" => "main", + "signed-by" => "https://www.postgresql.org/media/keys/ACCC4CF8.asc", + "suites" => "%{code_name}-pgdg", + "uris" => "https://apt.postgresql.org/pub/repos/apt", + } + repository = SystemRepository.new("pgdg", repository_properties) + assert_equal([ + Requirement.new([Package.new("libpq")], + [ + repository, + "libpq-dev", + ]), + ], + parse(gemspec_requirements)) + end + + def test_repositories + prefix = "system: groonga: debian:" + gemspec_requirements = [ + "#{prefix} repository: id: apache-arrow", + "#{prefix} repository: components: main", + "#{prefix} repository: signed-by: https://dist.apache.org/repos/dist/release/arrow/KEYS", + "#{prefix} repository: suites: %{code_name}", + "#{prefix} repository: uris: https://apache.jfrog.io/artifactory/arrow/%{distribution}/", + "#{prefix} repository: id: groonga", + "#{prefix} repository: components: main", + "#{prefix} repository: signed-by: https://packages.groonga.org/%{distribution}/groonga-archive-keyring.asc", + "#{prefix} repository: suites: %{code_name}", + "#{prefix} repository: uris: https://packages.groonga.org/%{distribution}/", + "#{prefix} libgroonga-dev", + ] + arrow_repository_properties = { + "components" => "main", + "signed-by" => "https://dist.apache.org/repos/dist/release/arrow/KEYS", + "suites" => "%{code_name}", + "uris" => "https://apache.jfrog.io/artifactory/arrow/%{distribution}/", + } + arrow_repository = SystemRepository.new("apache-arrow", + arrow_repository_properties) + groonga_repository_properties = { + "components" => "main", + "signed-by" => "https://packages.groonga.org/%{distribution}/groonga-archive-keyring.asc", + "suites" => "%{code_name}", + "uris" => "https://packages.groonga.org/%{distribution}/", + } + groonga_repository = SystemRepository.new("groonga", + groonga_repository_properties) + assert_equal([ + Requirement.new([Package.new("groonga")], + [ + arrow_repository, + groonga_repository, + "libgroonga-dev", + ]), + ], + parse(gemspec_requirements)) + end end