rubygems-requirements-system 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 598b91ef023d6606d97e7bfc74168cef0316de0897c63266c7371ef844322d6a
4
- data.tar.gz: 99fe81ea125044711118147950372853e17377a25306b09e20f9ca0f3369e43e
3
+ metadata.gz: 82e9618b8faccc8a97c5bed8acff02ae39a405e94bb02d9353ad5dc264dda420
4
+ data.tar.gz: f20b793d2dc732c13d2f6c581f103697de2f489afca060eef6e386576f814e3c
5
5
  SHA512:
6
- metadata.gz: b0dbff35b8e7ccfd004b78bfcf26efe011afd87106c646ba02093c4c7e076f568ebc8d65b96dc3ce15d2ed739780de8504557a412f5e4a1e21a2a38c15e6f735
7
- data.tar.gz: 89933a20c0aa2129b871df4a70d445bb856258959889a225849f25030d55de60252470db5e540e3a3fd12cedc4773ca5c78712d170183aa393948267419ea2b2
6
+ metadata.gz: a9d9abee1cf5832aa8b0d883218addaa3c59cfed9128f3abd836787a2b8e018eb68b04794ded74231691effd09d06dce8809eb01eceae33e8158bce1f7c84558
7
+ data.tar.gz: e420c6d08d4f28c7407622f1417dfbd5b90bf7444146812a0ed6795fd6c829bcd1945a8a24ce22d0660a242cf247861328bda7a983442346d1608c76fbc22cea
data/README.md CHANGED
@@ -173,7 +173,7 @@ Gem::Specification.new do |spec|
173
173
  #
174
174
  # On Ubuntu 24.04:
175
175
  # https://packages.groonga.org/%{distribution}/groonga-apt-source-latest-%{code_name}.deb ->
176
- # https://packages.groonga.org/ubuntu/groonga-apt-source-latest-nobole.deb
176
+ # https://packages.groonga.org/ubuntu/groonga-apt-source-latest-noble.deb
177
177
  spec.requirements << "system: groonga: debian: https://packages.groonga.org/%{distribution}/groonga-apt-source-latest-%{code_name}.deb"
178
178
  # Install libgroonga-dev from the registered repository.
179
179
  spec.requirements << "system: groonga: debian: libgroonga-dev"
@@ -207,10 +207,145 @@ Gem::Specification.new do |spec|
207
207
  end
208
208
  ```
209
209
 
210
+ ### Install repositories
211
+
212
+ You can install APT/Yum repositories by specifying metadata.
213
+
214
+ You need to specify multiple metadata for one repository. So you need
215
+ to use multiple `spec.requirements` for one repository. Here is the
216
+ syntax for one repository:
217
+
218
+ ```ruby
219
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key1}: #{value1}"
220
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key2}: #{value2}"
221
+ # ...
222
+ ```
223
+
224
+ You must specify at least `id` as `key`. For example:
225
+
226
+ ```
227
+ spec.requirements << "system: libpq: debian: repository: id: pgdg"
228
+ ```
229
+
230
+ You can start another repository metadata by starting `id` metadata
231
+ for another repository:
232
+
233
+ ```ruby
234
+ spec.requirements << "system: #{package}: #{platform}: repository: id: repository1
235
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key1_1}: #{value1_1}"
236
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key1_2}: #{value1_2}"
237
+ # ...
238
+ spec.requirements << "system: #{package}: #{platform}: repository: id: repository2
239
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key2_1}: #{value2_1}"
240
+ spec.requirements << "system: #{package}: #{platform}: repository: #{key2_2}: #{value2_2}"
241
+ # ...
242
+ ```
243
+
244
+ Here are metadata for a APT repository:
245
+
246
+ * `compoennts`: Optional. The default is `main`.
247
+ * `signed-by`: Optional. The URL of armored keyring that is used for
248
+ signing this repository.
249
+ * `suites`: Optional. The default is `%{code_name}`.
250
+ * `types`: Optional. The default is `deb`.
251
+ * `uris`: Required. The URLs that provide this repository.
252
+
253
+ See also: https://wiki.debian.org/SourcesList
254
+
255
+ Here are metadata for a Yum repository:
256
+
257
+ * `baseurl`: Required. The base URL that provides this repository.
258
+ * `gpgkey`: Optional. The URL of GPG key that is used for signing this
259
+ repository.
260
+ * `name`: Optional. The name of this repository.
261
+
262
+ See also: TODO: Is there any URL that describes the specification of
263
+ `.repo` file?
264
+
265
+ You can use placeholder for metadata values with `%{KEY}` format.
266
+
267
+ Here are available placeholders:
268
+
269
+ `debian` family platforms:
270
+
271
+ * `distribution`: The `ID` value in `/etc/os-release`. It's `debian`,
272
+ `ubuntu` and so on.
273
+ * `code_name`: The `VERSION_CODENAME` value in `/etc/os-release`. It's
274
+ `bookworm`, `noble` and so on.
275
+ * `version`: The `VERSION_ID` value in `/etc/os-release`. It's `12`,
276
+ `24.04` and so on.
277
+
278
+ `fedora` family platforms:
279
+
280
+ * `distribution`: The `ID` value in `/etc/os-release`. It's `fedora`,
281
+ `rhel`, `almalinux` and so on.
282
+ * `major_version`: The major part of `VERSION_ID` value in
283
+ `/etc/os-release`. It's `41`, `9` and so on.
284
+ * `version`: The `VERSION_ID` value in `/etc/os-release`. It's `41`,
285
+ `9.5` and so on.
286
+
287
+ Here is an example that uses this feature for adding a new repository:
288
+
289
+ ```ruby
290
+ Gem::Specification.new do |spec|
291
+ # ...
292
+
293
+ # Install PostgreSQL's APT repository on Debian family platforms.
294
+ #
295
+ # %{code_name} is placeholders.
296
+ #
297
+ # On Debian GNU/Linux bookworm:
298
+ # %{code_name}-pgdg ->
299
+ # bookworm-pgdg
300
+ #
301
+ # On Ubuntu 24.04:
302
+ # %{code_name}-pgdg ->
303
+ # noble-pgdg
304
+ spec.requirements << "system: libpq: debian: repository: id: pgdg"
305
+ spec.requirements << "system: libpq: debian: repository: uris: https://apt.postgresql.org/pub/repos/apt"
306
+ spec.requirements << "system: libpq: debian: repository: signed-by: https://www.postgresql.org/media/keys/ACCC4CF8.asc"
307
+ spec.requirements << "system: libpq: debian: repository: suites: %{code_name}-pgdg"
308
+ spec.requirements << "system: libpq: debian: repository: components: main"
309
+ # Install libpq-dev from the registered repository.
310
+ spec.requirements << "system: libpq: debian: libpq-dev"
311
+
312
+ # Install PostgreSQL's Yum repository on RHEL family platforms:
313
+ spec.requirements << "system: libpq: rhel: repository: id: pgdg17"
314
+ spec.requirements << "system: libpq: rhel: repository: name: PostgreSQL 17 $releasever - $basearch"
315
+ spec.requirements << "system: libpq: rhel: repository: baseurl: https://download.postgresql.org/pub/repos/yum/17/redhat/rhel-$releasever-$basearch"
316
+ spec.requirements << "system: libpq: rhel: repository: gpgkey: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL"
317
+ # You can disable built-in "postgresql" module by "module: disable:
318
+ # postgresql".
319
+ spec.requirements << "system: libpq: rhel: module: disable: postgresql"
320
+ # Install postgresql17-devel from the registered repository. But
321
+ # users can't find "libpq.pc" provided by postgresql17-devel without
322
+ # PKG_CONFIG_PATH=/usr/pgsql-17/lib/pkgconfig ...
323
+ spec.requirements << "system: libpq: rhel: postgresql17-devel"
324
+
325
+ # ...
326
+ end
327
+ ```
328
+
329
+ ## Configurations
330
+
331
+ ### Opt-out
332
+
333
+ If you don't like that gems may install system packages automatically,
334
+ you can disable this feature by the followings:
335
+
336
+ 1. Set `RUBYGEMS_REQUIREMENTS_SYSTEM=false`
337
+ 2. Add the following configuration to `~/.gemrc`:
338
+
339
+ ```yaml
340
+ requirements_system:
341
+ enabled: true
342
+ ```
343
+
210
344
  ## Requirements
211
345
 
212
- RubyGems 3.5.0 or later is required. RubyGems can load installed
213
- plugin immediately since 3.5.0.
346
+ RubyGems 3.4.14 or later is required. RubyGems can load installed
347
+ plugin immediately since 3.4.14. Ruby 3.2.3 or later ships RubyGems
348
+ 3.4.14 or later.
214
349
 
215
350
  If `gem install glib2` installs rubygems-requirements-system gem as a
216
351
  dependency, old RubyGems doesn't use a RubyGems plugin in
data/doc/text/news.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # News
2
2
 
3
+ ## 0.0.3 - 2025-01-13
4
+
5
+ ### Improvements
6
+
7
+ * Added support for opt-out.
8
+
9
+ * debian: Added support for adding APT repository by raw metadata.
10
+
11
+ * fedora: Added support for adding Yum repository by raw metadata.
12
+
13
+ * fedora: Added support for enabling/disabling module.
14
+
3
15
  ## 0.0.2 - 2025-01-08
4
16
 
5
17
  ### Improvements
@@ -20,40 +20,9 @@ require "pkg-config"
20
20
  require_relative "version"
21
21
 
22
22
  require_relative "platform"
23
+ require_relative "requirements-parser"
23
24
 
24
25
  module RubyGemsRequirementsSystem
25
- Package = Struct.new(:id, :operator, :version) do
26
- def valid?
27
- return false if id.empty?
28
- return false if operator and version.nil?
29
- true
30
- end
31
- end
32
-
33
- Requirement = Struct.new(:packages, :system_packages) do
34
- def satisfied?
35
- packages.any? do |package|
36
- installed?(package)
37
- end
38
- end
39
-
40
- private
41
- def installed?(package)
42
- package_config = PKGConfig.package_config(package.id)
43
- begin
44
- package_config.cflags
45
- rescue PackageConfig::NotFoundError
46
- return false
47
- end
48
-
49
- return true if package.version.nil?
50
-
51
- current_version = Gem::Version.new(package_config.version)
52
- required_version = Gem::Version.new(package.version)
53
- current_version.__send__(package.operator, required_version)
54
- end
55
- end
56
-
57
26
  class Installer
58
27
  include Gem::UserInteraction
59
28
 
@@ -63,7 +32,10 @@ module RubyGemsRequirementsSystem
63
32
  end
64
33
 
65
34
  def install
66
- requirements = parse_requirements(@gemspec.requirements)
35
+ return true unless enabled?
36
+
37
+ parser = RequirementsParser.new(@gemspec.requirements, @platform)
38
+ requirements = parser.parse
67
39
  requirements.all? do |requirement|
68
40
  next true if requirement.satisfied?
69
41
  @platform.install(requirement)
@@ -71,52 +43,21 @@ module RubyGemsRequirementsSystem
71
43
  end
72
44
 
73
45
  private
74
- def parse_requirements(gemspec_requirements)
75
- all_packages_set = {}
76
- requirements = {}
77
- gemspec_requirements.each do |gemspec_requirement|
78
- components = gemspec_requirement.split(/: +/, 4)
79
- next unless components.size == 4
80
-
81
- id, raw_packages, platform, system_package = components
82
- next unless id == "system"
83
-
84
- packages = parse_packages(raw_packages)
85
- next if packages.empty?
86
-
87
- all_packages_set[packages] = true
88
-
89
- next unless @platform.target?(platform)
90
- requirements[packages] ||= []
91
- requirements[packages] << system_package
92
- end
93
- (all_packages_set.keys - requirements.keys).each do |not_used_packages|
94
- system_packages = @platform.default_system_packages(not_used_packages)
95
- next if system_packages.nil?
96
- requirements[not_used_packages] = system_packages
97
- end
98
- requirements.collect do |packages, system_packages|
99
- Requirement.new(packages, system_packages)
46
+ def enabled?
47
+ case ENV["RUBYGEMS_REQUIREMENTS_SYSTEM"]
48
+ when "0", "no", "NO", "false", "FALSE"
49
+ return false
100
50
  end
101
- end
102
51
 
103
- def parse_packages(raw_packages)
104
- packages = raw_packages.split(/\s*\|\s*/).collect do |raw_package|
105
- Package.new(*raw_package.split(/\s*(==|!=|>=|>|<=|<)\s*/, 3))
106
- end
107
- # Ignore this requirement if any invalid package is included.
108
- # We must not report an error for this because
109
- # Gem::Specification#requirements is a free form
110
- # configuration. So there are configuration values that use
111
- # "system: ..." but not for this plugin. We can report a
112
- # warning instead.
113
- packages.each do |package|
114
- unless package.valid?
115
- # TODO: Report a warning
116
- return []
117
- end
52
+ requirements_system = Gem.configuration["requirements_system"] || {}
53
+ case requirements_system["enabled"]
54
+ when false
55
+ return false
56
+ when "false" # "true"/"false" isn't converted to boolean with old RubyGems
57
+ return false
118
58
  end
119
- packages
59
+
60
+ true
120
61
  end
121
62
  end
122
63
  end
@@ -34,7 +34,7 @@ module RubyGemsRequirementsSystem
34
34
  end
35
35
 
36
36
  def id_like
37
- (self["ID_LIKE"] || "").split(/ /)
37
+ (self["ID_LIKE"] || "").split(/\s+/)
38
38
  end
39
39
 
40
40
  def version
@@ -14,4 +14,25 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  module RubyGemsRequirementsSystem
17
+ Package = Struct.new(:id, :operator, :required_version) do
18
+ class << self
19
+ def parse(input)
20
+ new(*input.split(/\s*(==|>=|>|<=|<)\s*/, 3))
21
+ end
22
+ end
23
+
24
+ def valid?
25
+ return false if id.empty?
26
+ return false if operator and required_version.nil?
27
+ true
28
+ end
29
+
30
+ def satisfied?(target_version)
31
+ return true if required_version.nil?
32
+
33
+ target = Gem::Version.new(target_version)
34
+ required = Gem::Version.new(required_version)
35
+ target.__send__(operator, required)
36
+ end
37
+ end
17
38
  end
@@ -15,6 +15,7 @@
15
15
 
16
16
  require "fileutils"
17
17
  require "open-uri"
18
+ require "rubygems/user_interaction"
18
19
  require "tempfile"
19
20
 
20
21
  require_relative "../executable-finder"
@@ -33,6 +34,14 @@ module RubyGemsRequirementsSystem
33
34
  nil
34
35
  end
35
36
 
37
+ def valid_system_package?(package)
38
+ true
39
+ end
40
+
41
+ def valid_system_repository?(repository)
42
+ false
43
+ end
44
+
36
45
  def install(requirement)
37
46
  synchronize do
38
47
  requirement.system_packages.any? do |package|
@@ -108,12 +117,19 @@ module RubyGemsRequirementsSystem
108
117
  end
109
118
 
110
119
  def run_command_line(package, action, command_line)
120
+ if package.is_a?(SystemRepository)
121
+ package_label = "repository(#{package.id})"
122
+ else
123
+ package_label = package
124
+ end
125
+ prefix = "requirements: system: #{package_label}: #{action}"
126
+ say("#{prefix}: Start")
111
127
  failed_to_get_super_user_priviledge = false
112
128
  if have_priviledge?
113
129
  succeeded = system(*command_line)
114
130
  else
115
131
  if sudo
116
- prompt = "[sudo] password for %u to #{action} <#{package}>: "
132
+ prompt = "[sudo] password for %u to #{action} <#{package_label}>: "
117
133
  command_line = [sudo, "-p", prompt, *command_line]
118
134
  succeeded = system(*command_line)
119
135
  else
@@ -126,13 +142,13 @@ module RubyGemsRequirementsSystem
126
142
  else
127
143
  result_message = succeeded ? "succeeded" : "failed"
128
144
  end
129
- say("#{action.capitalize} '#{package}' system package: #{result_message}")
145
+ say("#{prefix}: #{result_message}")
130
146
 
131
147
  unless succeeded
132
148
  escaped_command_line = command_line.collect do |part|
133
149
  Shellwords.escape(part)
134
150
  end
135
- alert_warning("'#{package}' system package is required.")
151
+ alert_warning("#{prefix}: Failed.")
136
152
  alert_warning("Run the following command " +
137
153
  "to #{action} required system package: " +
138
154
  escaped_command_line.join(" "))
@@ -42,6 +42,14 @@ module RubyGemsRequirementsSystem
42
42
  end
43
43
  end
44
44
 
45
+ def valid_system_repository?(repository)
46
+ uris = repository["uris"]
47
+ return false if uris.nil?
48
+ return false if uris.empty?
49
+
50
+ true
51
+ end
52
+
45
53
  private
46
54
  def prepare_command_lines(package)
47
55
  [
@@ -50,8 +58,62 @@ module RubyGemsRequirementsSystem
50
58
  end
51
59
 
52
60
  def install_command_line(package)
61
+ if package.is_a?(SystemRepository)
62
+ install_command_line_repository(package)
63
+ else
64
+ install_command_line_package(package)
65
+ end
66
+ end
67
+
68
+ def install_command_line_repository(repository)
69
+ temporary_file_base_name = [
70
+ "rubygems-requirements-system-debian-#{repository.id}",
71
+ ".sources",
72
+ ]
73
+ sources = create_temporary_file(temporary_file_base_name)
74
+ signed_by_url = resolve_value_template(repository["signed-by"])
75
+ if signed_by_url
76
+ signed_by = URI.open(signed_by_url) do |response|
77
+ response.read
78
+ end
79
+ else
80
+ signed_by = nil
81
+ end
82
+ suites = repository["suites"] || "%{code_name}"
83
+ components = repository["components"] || "main"
84
+ {
85
+ "Types" => repository["types"] || "deb",
86
+ "URIs" => resolve_value_template(repository["uris"]),
87
+ "Suites" => resolve_value_template(suites),
88
+ "Components" => resolve_value_template(components),
89
+ "Signed-By" => signed_by,
90
+ }.each do |key, value|
91
+ next if value.nil?
92
+ if value.include?("\n")
93
+ sources.puts("#{key}:")
94
+ value.each_line(chomp: true) do |line|
95
+ if line.empty?
96
+ sources.puts(" .")
97
+ else
98
+ sources.puts(" #{line}")
99
+ end
100
+ end
101
+ else
102
+ sources.puts("#{key}: #{value}")
103
+ end
104
+ end
105
+ sources.close
106
+ FileUtils.chmod("go+r", sources.path)
107
+ [
108
+ "cp",
109
+ sources.path,
110
+ "/etc/apt/sources.list.d/#{repository.id}.sources",
111
+ ]
112
+ end
113
+
114
+ def install_command_line_package(package)
53
115
  if package.start_with?("https://")
54
- package_url = resolve_package_url_template(package)
116
+ package_url = resolve_value_template(package)
55
117
  temporary_file_base_name = [
56
118
  "rubygems-requirements-system-debian",
57
119
  File.extname(package),
@@ -61,7 +123,6 @@ module RubyGemsRequirementsSystem
61
123
  IO.copy_stream(response, local_package)
62
124
  end
63
125
  local_package.close
64
- @temporary_files << local_package
65
126
  FileUtils.chmod("go+r", local_package.path)
66
127
  package = local_package.path
67
128
  end
@@ -80,9 +141,11 @@ module RubyGemsRequirementsSystem
80
141
  true
81
142
  end
82
143
 
83
- def resolve_package_url_template(package_url_template)
144
+ def resolve_value_template(template)
145
+ return nil if template.nil?
146
+
84
147
  os_release = OSRelease.new
85
- package_url_template % {
148
+ template % {
86
149
  distribution: os_release.id,
87
150
  code_name: os_release.version_codename,
88
151
  version: os_release.version_id,
@@ -31,25 +31,97 @@ module RubyGemsRequirementsSystem
31
31
  platform == "fedora"
32
32
  end
33
33
 
34
+ def valid_system_package?(package)
35
+ return true unless package.start_with?("module:")
36
+
37
+ action, target = parse_module_system_package(package)
38
+ case action
39
+ when "enable", "disable"
40
+ else
41
+ return false
42
+ end
43
+ return false if target.nil?
44
+
45
+ true
46
+ end
47
+
48
+ def valid_system_repository?(repository)
49
+ baseurl = repository["baseurl"]
50
+ return false if baseurl.nil?
51
+ return false if baseurl.empty?
52
+
53
+ true
54
+ end
55
+
34
56
  def default_system_packages(packages)
35
57
  packages.collect {|package| "pkgconfig(#{package.id})"}
36
58
  end
37
59
 
38
60
  private
61
+ def parse_module_system_package(package)
62
+ # "module: disable: postgresql" ->
63
+ # ["module", "disable", "postgresql"]
64
+ _, action, target = package.split(/:\s*/, 3)
65
+ [action, target]
66
+ end
67
+
39
68
  def install_command_line(package)
69
+ if package.is_a?(SystemRepository)
70
+ install_command_line_repository(package)
71
+ elsif package.start_with?("module:")
72
+ install_command_line_module(package)
73
+ else
74
+ install_command_line_package(package)
75
+ end
76
+ end
77
+
78
+ def install_command_line_repository(repository)
79
+ temporary_file_base_name = [
80
+ "rubygems-requirements-system-fedora-#{repository.id}",
81
+ ".repo",
82
+ ]
83
+ repo = create_temporary_file(temporary_file_base_name)
84
+ repo.puts("[#{repository.id}]")
85
+ {
86
+ "name" => resolve_value_template(repository["name"]),
87
+ "baseurl" => resolve_value_template(repository["baseurl"]),
88
+ "enabled" => "1",
89
+ "gpgcheck" => repository["gpgkey"] ? "1" : "0",
90
+ "gpgkey" => resolve_value_template(repository["gpgkey"]),
91
+ }.each do |key, value|
92
+ next if value.nil?
93
+ repo.puts("#{key}=#{value}")
94
+ end
95
+ repo.close
96
+ FileUtils.chmod("go+r", repo.path)
97
+ [
98
+ "cp",
99
+ repo.path,
100
+ "/etc/yum.repos.d/#{repository.id}.repo",
101
+ ]
102
+ end
103
+
104
+ def install_command_line_module(package)
105
+ action, target = parse_module_system_package(package)
106
+ ["dnf", "-y", "module", action, target]
107
+ end
108
+
109
+ def install_command_line_package(package)
40
110
  if package.start_with?("https://")
41
- package = resolve_package_url_template(package)
111
+ package = resolve_value_template(package)
42
112
  end
43
- ["dnf", "install", "-y", package]
113
+ ["dnf", "-y", "install", package]
44
114
  end
45
115
 
46
116
  def need_super_user_priviledge?
47
117
  true
48
118
  end
49
119
 
50
- def resolve_package_url_template(package_url_template)
120
+ def resolve_value_template(template)
121
+ return nil if template.nil?
122
+
51
123
  os_release = OSRelease.new
52
- package_url_template % {
124
+ template % {
53
125
  distribution: os_release.id,
54
126
  major_version: major_version.to_s,
55
127
  version: os_release.version_id,
@@ -39,16 +39,16 @@ module RubyGemsRequirementsSystem
39
39
  end
40
40
 
41
41
  private
42
- def install_command_line(package)
42
+ def install_command_line_package(package)
43
43
  if package.start_with?("https://")
44
- package = resolve_package_url_template(package)
44
+ package = resolve_value_template(package)
45
45
  end
46
46
  if major_version >= 9
47
- ["dnf", "install", "--enablerepo=crb", "-y", package]
47
+ ["dnf", "-y", "install", "--enablerepo=crb", package]
48
48
  elsif major_version >= 8
49
- ["dnf", "install", "--enablerepo=powertools", "-y", package]
49
+ ["dnf", "-y", "install", "--enablerepo=powertools", package]
50
50
  else
51
- ["yum", "install", "-y", package]
51
+ ["yum", "-y", "install", package]
52
52
  end
53
53
  end
54
54
 
@@ -39,7 +39,7 @@ module RubyGemsRequirementsSystem
39
39
 
40
40
  private
41
41
  def prepare_command_lines(package)
42
- if package.start_with?("ppa:")
42
+ if package.is_a?(String) and package.start_with?("ppa:")
43
43
  [
44
44
  ["apt-get", "update"],
45
45
  install_command_line("software-properties-common"),
@@ -49,7 +49,7 @@ module RubyGemsRequirementsSystem
49
49
  end
50
50
  end
51
51
 
52
- def install_command_line(package)
52
+ def install_command_line_package(package)
53
53
  if package.start_with?("ppa:")
54
54
  [
55
55
  "add-apt-repository",
@@ -0,0 +1,36 @@
1
+ # Copyright (C) 2025 Ruby-GNOME Project Team
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module RubyGemsRequirementsSystem
17
+ Requirement = Struct.new(:packages, :system_packages) do
18
+ def satisfied?
19
+ packages.any? do |package|
20
+ installed?(package)
21
+ end
22
+ end
23
+
24
+ private
25
+ def installed?(package)
26
+ package_config = PKGConfig.package_config(package.id)
27
+ begin
28
+ package_config.cflags
29
+ rescue PackageConfig::NotFoundError
30
+ return false
31
+ end
32
+
33
+ package.satisfied?(package_config.version)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright (C) 2025 Ruby-GNOME Project Team
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require_relative "package"
17
+ require_relative "requirement"
18
+ require_relative "system-repository"
19
+
20
+ module RubyGemsRequirementsSystem
21
+ class RequirementsParser
22
+ def initialize(gemspec_requirements, platform)
23
+ @gemspec_requirements = gemspec_requirements
24
+ @platform = platform
25
+ end
26
+
27
+ def parse
28
+ all_packages_set = {}
29
+ requirements = {}
30
+ @gemspec_requirements.each do |gemspec_requirement|
31
+ components = gemspec_requirement.split(/:\s*/, 4)
32
+ next unless components.size == 4
33
+
34
+ id, raw_packages, platform, system_package = components
35
+ next unless id == "system"
36
+
37
+ packages = parse_packages(raw_packages)
38
+ next if packages.empty?
39
+
40
+ all_packages_set[packages] = true
41
+
42
+ next unless @platform.target?(platform)
43
+ requirements[packages] ||= []
44
+ requirements[packages] << system_package
45
+ end
46
+ (all_packages_set.keys - requirements.keys).each do |not_used_packages|
47
+ system_packages = @platform.default_system_packages(not_used_packages)
48
+ next if system_packages.nil?
49
+ requirements[not_used_packages] = system_packages
50
+ end
51
+ requirements.collect do |packages, system_packages|
52
+ system_packages = detect_system_repositories(system_packages)
53
+ Requirement.new(packages, system_packages)
54
+ end
55
+ end
56
+
57
+ private
58
+ def parse_packages(raw_packages)
59
+ packages = raw_packages.split(/\s*\|\s*/).collect do |raw_package|
60
+ Package.parse(raw_package)
61
+ end
62
+ # Ignore this requirement if any invalid package is included.
63
+ # We must not report an error for this because
64
+ # Gem::Specification#requirements is a free form
65
+ # configuration. So there are configuration values that use
66
+ # "system: ..." but not for this plugin. We can report a
67
+ # warning instead.
68
+ packages.each do |package|
69
+ unless package.valid?
70
+ # TODO: Report a warning
71
+ return []
72
+ end
73
+ end
74
+ packages
75
+ end
76
+
77
+ def detect_system_repositories(original_system_packages)
78
+ system_packages = []
79
+ repository_properties = {}
80
+
81
+ flush_repository = lambda do
82
+ return if repository_properties.empty?
83
+ properties = repository_properties
84
+ repository_properties = {}
85
+
86
+ id = properties.delete("id")
87
+ return if id.nil?
88
+ repository = SystemRepository.new(id, properties)
89
+ return unless @platform.valid_system_repository?(repository)
90
+ system_packages << repository
91
+ end
92
+
93
+ original_system_packages.each do |system_package|
94
+ unless system_package.start_with?("repository:")
95
+ flush_repository.call
96
+ next unless @platform.valid_system_package?(system_package)
97
+ system_packages << system_package
98
+ next
99
+ end
100
+ _, key, value = system_package.strip.split(/:\s*/, 3)
101
+ next if value.empty?
102
+ if key == "id" and repository_properties.key?("id")
103
+ flush_repository.call
104
+ end
105
+ repository_properties[key] = value
106
+ end
107
+ flush_repository.call
108
+
109
+ system_packages
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright (C) 2025 Ruby-GNOME Project Team
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module RubyGemsRequirementsSystem
17
+ class SystemRepository
18
+ attr_reader :id
19
+ attr_reader :properties
20
+ protected :properties
21
+ def initialize(id, properties)
22
+ @id = id
23
+ @properties = properties
24
+ end
25
+
26
+ def ==(other)
27
+ other.is_a?(self.class) and
28
+ @id == other.id and
29
+ @properties == other.properties
30
+ end
31
+
32
+ def eql?(other)
33
+ self == other
34
+ end
35
+
36
+ def hash
37
+ [@id, @properties].hash
38
+ end
39
+
40
+ def [](key)
41
+ @properties[key]
42
+ end
43
+
44
+ def each(&block)
45
+ @properties.each(&block)
46
+ end
47
+ end
48
+ end
@@ -14,5 +14,5 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  module RubyGemsRequirementsSystem
17
- VERSION = "0.0.2"
17
+ VERSION = "0.0.3"
18
18
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubygems-requirements-system
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sutou Kouhei
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-08 00:00:00.000000000 Z
10
+ date: 2025-01-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: pkg-config
@@ -65,6 +65,9 @@ files:
65
65
  - lib/rubygems-requirements-system/platform/suse.rb
66
66
  - lib/rubygems-requirements-system/platform/ubuntu.rb
67
67
  - lib/rubygems-requirements-system/platform/unknown.rb
68
+ - lib/rubygems-requirements-system/requirement.rb
69
+ - lib/rubygems-requirements-system/requirements-parser.rb
70
+ - lib/rubygems-requirements-system/system-repository.rb
68
71
  - lib/rubygems-requirements-system/version.rb
69
72
  - lib/rubygems_plugin.rb
70
73
  homepage: https://github.com/ruby-gnome/rubygems-requirements-system