From Fedora Project Wiki

(EPEL6 can now use these guidelines. F17 is EOL)
(Missed two hunks when writing up https://pagure.io/packaging-committee/issue/710. Also fix a typo.)
 
(11 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{admon/warning|Different guidelines for packages in EPEL 5| In RHEL 5 a ruby version that does not have all the features needed to support these guidelines exists.  If you're packaging for EPEL 5 you should follow the [[Packaging:Old_Ruby | old ruby guidelines]] instead.}}
+
{{DISPLAYTITLE:Fedora Packaging Guidelines for Ruby}}
 +
<div style="float: right; margin-left: 0.5em" class="toclimit-2">__TOC__</div>
  
{{admon/warning|Different Guidelines for Fedora 18| In Fedora 18 the ruby guidelines were slightly different.  Some of the macros and practices listed here did not work.  Please see this [[https://fedoraproject.org/w/index.php?title=Packaging:Ruby&oldid=306009 earlier version of the guidelines]] if you want your package to work on those versions of Fedora}}
+
{{admon/note|JRuby Gems| Although Fedora has fully functioning JRuby integrated with system RubyGems, we have decided to not include the JRuby specific packaging guidelines here, as they need some more work. They will appear here as soon as we feel that we've got everything covered properly. You can contact us on [https://lists.fedoraproject.org/mailman/listinfo/ruby-sig Ruby-SIG mailing list] in case of any questions about the prepared JRuby packaging guidelines.}}
 
 
{{admon/note|JRuby Gems| Although Fedora 19 has fully functioning JRuby integrated with system RubyGems, we have decided to not include the JRuby specific packaging guidelines here, as they need some more work. They will appear here as soon as we feel that we've got everything covered properly. You can contact us on [https://lists.fedoraproject.org/mailman/listinfo/ruby-sig Ruby-SIG mailing list] in case of any questions about the prepared JRuby packaging guidelines.}}
 
  
 
There are three basic categories of ruby packages: [[#RubyGems | ruby gems]], [[#Non-Gem Packages | non-gem ruby packages]], and [[#ruby_applications| applications written in ruby]]. These guidelines contain sections common to all of these as well as sections which apply to each one individually. Be sure to read all the guidelines relevant to the type of ruby package you are building.
 
There are three basic categories of ruby packages: [[#RubyGems | ruby gems]], [[#Non-Gem Packages | non-gem ruby packages]], and [[#ruby_applications| applications written in ruby]]. These guidelines contain sections common to all of these as well as sections which apply to each one individually. Be sure to read all the guidelines relevant to the type of ruby package you are building.
Line 9: Line 8:
 
== Ruby Compatibility ==
 
== Ruby Compatibility ==
  
Each Ruby package '''must''' indicate it depends on a Ruby interpreter. Use ruby(release) virtual requirement to achieve that:
+
Each Ruby package MUST indicate it depends on a Ruby interpreter (this does not apply to [[#RubyGems|ruby gem packages]]). Use ruby(release) virtual requirement to achieve that:
 
<pre>
 
<pre>
 
Requires: ruby(release)
 
Requires: ruby(release)
Line 17: Line 16:
 
Requires: ruby(release) >= 1.9.1
 
Requires: ruby(release) >= 1.9.1
 
</pre>
 
</pre>
{{admon/note|Alternate interpreters| Alternate Ruby interpreters (currently JRuby) also <code>Provide: ruby(release)</code>. This implies, that pure RubyGems packages (these are shared among interpreters) '''should not''' have <code>Requires: ruby</code> or <code>Requires: jruby</code> to have their dependencies satisfied by any of these interpreters.}}
+
{{admon/note|Alternate interpreters| Alternate Ruby interpreters (currently JRuby) also <code>Provide: ruby(release)</code>. This implies, that pure RubyGems packages (these are shared among interpreters) SHOULD NOT have <code>Requires: ruby</code> or <code>Requires: jruby</code> to have their dependencies satisfied by any of these interpreters.}}
  
{{admon/warning|Over specified ruby(release) versioning| Please note, that if the  <code>ruby(release)</code> version requirement is too specific, it may cause an unexpected interpreter to be drawn in. E.g. <code>ruby(release) &#61; 1.8</code> will require JRuby package, since it is the only package that provides it.}}
+
{{admon/warning|Over specified ruby(release) versioning| Please note, that if the  <code>ruby(release)</code> version requirement is too specific, it might cause an unexpected interpreter to be drawn in. E.g. <code>ruby(release) &#61; 1.8</code> will require JRuby package, since it is the only package that provides it.}}
  
 
{{Anchor|interpreters_compatibility}}
 
{{Anchor|interpreters_compatibility}}
  
 
=== Different Interpreters Compatibility ===
 
=== Different Interpreters Compatibility ===
Most of the pure Ruby packages will work on all Ruby interpreters. There are however cases, when the packages use interpreter-specific functions (like <code>fork()</code>) and won't run on other interpreters (JRuby). In this case, the package '''should''' require that interpreter. For example, a package that uses <code>fork</code> should explicitly specify <code>Requires: ruby</code>.
+
Most of the pure Ruby packages will work on all Ruby interpreters. There are however cases, when the packages use interpreter-specific functions (like <code>fork()</code>) and won't run on other interpreters (JRuby). In this case, the package SHOULD require that interpreter. For example, a package that uses <code>fork</code> SHOULD explicitly specify <code>Requires: ruby</code>.
In case of such package, packager '''should''' file a bug to ask upstream to provide support for other interpreter(s). This '''should''' be documented in specfile.
+
In case of such package, packager SHOULD file a bug to ask upstream to provide support for other interpreter(s). This SHOULD be documented in specfile.
  
 
=== Shebang lines ===
 
=== Shebang lines ===
Line 45: Line 44:
 
</pre>
 
</pre>
  
Ruby executables that are known to only run on one Ruby implementation '''should''' use that specific implementation in their shebang - <code>#!/usr/bin/ruby-mri</code> or <code>#!/usr/bin/jruby</code> to ensure that they run using that implementation.  All other code '''should''' use <code>#!/usr/bin/ruby</code>.
+
Ruby executables that are known to only run on one Ruby implementation SHOULD use that specific implementation in their shebang - <code>#!/usr/bin/ruby-mri</code> or <code>#!/usr/bin/jruby</code> to ensure that they run using that implementation.  All other code SHOULD use <code>#!/usr/bin/ruby</code>.
  
 
{{Anchor|ruby_naming}}
 
{{Anchor|ruby_naming}}
Line 51: Line 50:
 
== Naming Guidelines ==
 
== Naming Guidelines ==
  
* Packages that contain Ruby Gems '''must''' be called <code>rubygem-%{gem_name}</code>.
+
* Packages that contain Ruby Gems MUST be called <code>rubygem-%{gem_name}</code>.
  
* The name of a ruby extension/library package '''must''' start with the interpreter it is built for (ruby, jruby, etc) and then the <code>UPSTREAM</code> name.  For example: <code>ruby-UPSTREAM</code>. If the upstream name <code>UPSTREAM</code> contains <code>ruby</code>, that '''should''' be dropped from the name. For example, the SQLite database driver for ruby is called <code>sqlite3-ruby</code>. The corresponding Fedora package should be called <code>ruby-sqlite3</code>, and not <code>ruby-sqlite3-ruby</code>.
+
* The name of a ruby extension/library package MUST start with the interpreter it is built for (ruby, jruby, etc) and then the <code>UPSTREAM</code> name.  For example: <code>ruby-UPSTREAM</code>. If the upstream name <code>UPSTREAM</code> contains <code>ruby</code>, that SHOULD be dropped from the name. For example, the SQLite database driver for ruby is called <code>sqlite3-ruby</code>. The corresponding Fedora package SHOULD be called <code>ruby-sqlite3</code>, and not <code>ruby-sqlite3-ruby</code>.
  
* Application packages that mainly provide user-level tools that happen to be written in Ruby '''must''' follow the general [[Packaging/NamingGuidelines|  NamingGuidelines]]  instead.
+
* Application packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the general [[Packaging/NamingGuidelines|  NamingGuidelines]]  instead.
  
 
== Macros ==
 
== Macros ==
Line 117: Line 116:
 
=== Interpreter independence and directory macros ===
 
=== Interpreter independence and directory macros ===
  
You may have noticed that the table above has different directories for non-gem libraries on different ruby interpreters but only a single set of directories for rubygem libraries.  This is because code written for one ruby interpreter will often run on all ruby interpreters that Fedora ships (ruby, jruby, etc).  However, some code uses methods that are not available on all interpreters (see [[#interpreter_compatibility|Different Interpreters Compatibility]]).  Rubygems have a facility to ship different versions of the code in the same gem so that the gem can run on all versions of the interpreter so we only need to have one common directory for rubygems that all the interpreters can use.
+
You might have noticed that the table above has different directories for non-gem libraries on different ruby interpreters but only a single set of directories for rubygem libraries.  This is because code written for one ruby interpreter will often run on all ruby interpreters that Fedora ships (ruby, jruby, etc).  However, some code uses methods that are not available on all interpreters (see [[#interpreter_compatibility|Different Interpreters Compatibility]]).  Rubygems have a facility to ship different versions of the code in the same gem so that the gem can run on all versions of the interpreter so we only need to have one common directory for rubygems that all the interpreters can use.
  
The standard ruby <code>%{vendorlib}</code> directories lack this facility.  For this reason, non-gem libraries need to be placed in per-interpreter directories and must have a separate subpackage (or package depending on upstream) for each interpreter that they support.
+
The standard ruby <code>%{vendorlib}</code> directories lack this facility.  For this reason, non-gem libraries need to be placed in per-interpreter directories and MUST have a separate subpackage (or package depending on upstream) for each interpreter that they support.
  
 
== Libraries ==
 
== Libraries ==
  
These guidelines only apply to Ruby packages whose main purpose is providing a Ruby library; packages that mainly provide user-level tools that happen to be written in Ruby must follow the [[#ruby_applications|ruby applications guidelines]] instead.
+
These guidelines only apply to Ruby packages whose main purpose is providing a Ruby library; packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the [[#ruby_applications|ruby applications guidelines]] instead.
  
 
=== RubyGems ===
 
=== RubyGems ===
  
[http://www.rubygems.org/ RubyGems] are Ruby's own packaging format. Gems contain a lot of the same metadata that RPM's need, making fairly smooth interoperation between RPM and Gems possible. This guideline ensures that Gems are packaged as RPM's in a way that such RPM's fit cleanly with the rest of the distribution and to make it possible for the end user to satisfy dependencies of a Gem by installing the appropriate RPM-packaged Gem.
+
[http://www.rubygems.org/ RubyGems] are Ruby's own packaging format. Gems contain a lot of the same metadata that RPM's need, making fairly smooth interoperation between RPM and Gems possible. This guideline ensures that Gems are packaged as RPM's in a way that such RPM's fit cleanly with the rest of the distribution and to make it possible for the end user to satisfy dependencies of a Gem by installing the appropriate RPM-packaged Gem.
  
 
Both RPM's and Gems use similar terminology --- there are specfiles, package names, dependencies etc. for both. To keep confusion to a minimum, terms relating to Gem concepts will be explicitly refereed to with the word 'Gem' prefixed, eg 'Gem specification' (.gemspec). An unqualified 'package' in the following always means an RPM.
 
Both RPM's and Gems use similar terminology --- there are specfiles, package names, dependencies etc. for both. To keep confusion to a minimum, terms relating to Gem concepts will be explicitly refereed to with the word 'Gem' prefixed, eg 'Gem specification' (.gemspec). An unqualified 'package' in the following always means an RPM.
  
* Spec files '''must''' contain a definition of <code>%{gem_name}</code> which is the name from the Gem's specification.
+
* Spec files MUST contain a definition of <code>%{gem_name}</code> which is the name from the Gem's specification.
* The <code>Source</code> of the package '''must''' be the full URL to the released Gem archive; the version of the package '''must''' be the Gem's version.
+
* The <code>Source</code> of the package MUST be the full URL to the released Gem archive; the version of the package MUST be the Gem's version.
* The package '''must''' <code>BuildRequires: rubygems-devel</code> to pull in the macros needed to build.
+
* The package MUST <code>BuildRequires: rubygems-devel</code> to pull in the macros needed to build.
* The package '''must''' <code>Requires: rubygems</code>, since it owns the RubyGems directory structure.  
+
* There SHOULD NOT be any rubygem <code>Requires</code> nor <code>Provides</code> listed, since those are autogenerated.
 +
* There SHOULD NOT be <code>Requires: ruby(release)</code>, unless you want to explicitly specify Ruby version compatibility. Automatically generated dependency on RubyGems (<code>Requires: ruby(rubygems)</code>) is enough.
 +
 
 +
==== Filtering Requires and Provides ====
 +
Runtime requires and provides are automatically generated by RPM's dependency generator. However, it can sometimes throw in additional dependencies contrary to reality. To fix this, the dependency generator needs to be overridden so that the additional dependencies can be filtered out.  See [[Packaging:AutoProvidesAndRequiresFiltering]] for details.
  
 
==== Building gems ====
 
==== Building gems ====
Line 142: Line 145:
 
<pre>
 
<pre>
 
%prep
 
%prep
gem unpack %{SOURCE0}
+
%setup -q -n  %{gem_name}-%{version}
%setup -q -D -T -n  %{gem_name}-%{version}
 
 
 
gem spec %{SOURCE0} -l --ruby > %{gem_name}.gemspec
 
  
# Modify the gemspec if necessary with a patch or sed
+
# Modify the gemspec if necessary
 
# Also apply patches to code if necessary
 
# Also apply patches to code if necessary
 
%patch0 -p1
 
%patch0 -p1
Line 153: Line 153:
 
%build
 
%build
 
# Create the gem as gem install only works on a gem file
 
# Create the gem as gem install only works on a gem file
gem build %{gem_name}.gemspec
+
gem build ../%{gem_name}-%{version}.gemspec
  
 
# %%gem_install compiles any C extensions and installs the gem into ./%%gem_dir
 
# %%gem_install compiles any C extensions and installs the gem into ./%%gem_dir
Line 167: Line 167:
 
cp -a ./%{_bindir}/* %{buildroot}%{_bindir}
 
cp -a ./%{_bindir}/* %{buildroot}%{_bindir}
  
 +
# If there are C extensions, copy them to the extdir.
 +
mkdir -p %{buildroot}%{gem_extdir_mri}
 +
cp -a .%{gem_extdir_mri}/{gem.build_complete,*.so} %{buildroot}%{gem_extdir_mri}/
 +
</pre>
 +
 +
===== %prep =====
 +
RPM (as of 4.14) can directly unpack gem archives, so we can call <code>gem unpack</code> to extract the source from the gem.  Then we call <code>%setup -n %{gem_name}-%{version}</code> to tell rpm what the directory the gem has unpacked into.  At the same directory level, the %{gem_name}-%{version}.gemspec file is created automatically as well. This <code>.gemspec</code> file will be used to rebuild the gem later. If we need to modify the <code>.gemspec</code> (for instance, if the version of dependencies is wrong for Fedora or the <code>.gemspec</code> is using old, no longer supported fields) we would do it here.  Patches to the code itself can also be done here.
  
# If there are C extensions, mv them to the extdir.
+
{{admon/warning|Different Guidelines for Fedora 26 and older| In Fedora 26 and older releases, the rubygem <code>%prep</code> section has to be slightly different, since the <code>%setup</code> macro cannot expand the gem yet. Please use the following snippet to mimic the RPM 4.14 %setup macro functionality:
# You must replace REQUIRE_PATHS according to your gem specifics.
 
mkdir -p %{buildroot}%{gem_extdir_mri}/REQUIRE_PATHS
 
mv %{buildroot}%{gem_instdir}/REQUIRE_PATHS/shared_object.so %{buildroot}%{gem_extdir_mri}/REQUIRE_PATHS/
 
</pre>
 
  
You should replace REQUIRE_PATHS with the first value of the require_paths field in the gemspec file.  It will typically be either "lib" or "ext".  For instance if you find following line in your gemspec file:
 
 
<pre>
 
<pre>
  s.require_paths = ["lib"]
+
%prep
</pre>
+
gem unpack %{SOURCE0}
 +
gem spec %{SOURCE0} -l --ruby > %{gem_name}-%{version}.gemspec
 +
 
 +
%setup -q -D -T -n  %{gem_name}-%{version}
  
then the example above would look like:
+
# Modify the gemspec if necessary
<pre>
+
# Also apply patches to code if necessary
mkdir -p %{buildroot}%{gem_extdir_mri}/lib
+
%patch0 -p1
mv %{buildroot}%{gem_instdir}/lib/shared_object.so %{buildroot}%{gem_extdir_mri}/lib/
 
 
</pre>
 
</pre>
  
===== %prep =====
+
The rest of the package can stay the same for all Fedora versions.
Since gems aren't an archive format that rpm recognizes, the first thing we have to do is explicitly use <code>gem unpack</code> to extract the source from the gem.  Then we call <code>%setup -n %{gem_name}-%{version}</code> to tell rpm what the directory the gem has unpacked into.  The [http://www.rpm.org/max-rpm/s1-rpm-inside-macros.html#S3-RPM-INSIDE-SETUP-T-OPTION <code>-T</code>] and [http://www.rpm.org/max-rpm/s1-rpm-inside-macros.html#S3-RPM-INSIDE-SETUP-D-OPTION <code>-D</code>] flags tell <code>%setup</code> that we've already unpacked the code
+
}}
 
 
We then run <code>gem spec</code> to output the metadata from the gem into a file.  This <code>.gemspec</code> file will be used to rebuild the gem later.  If we need to modify the <code>.gemspec</code> (for instance, if the version of dependencies is wrong for Fedora or the <code>.gemspec</code> is using old, no longer supported fields) we would do it here.  Patches to the code itself can also be done here.
 
  
 
===== %build =====
 
===== %build =====
Line 199: Line 201:
 
:Might override the gem installation destination. However we do not suggest to use this option.
 
:Might override the gem installation destination. However we do not suggest to use this option.
  
{{admon/note||The %gem_install macro '''must not''' be used to install into the <code>%{buildroot}</code>}}
+
{{admon/note||The %gem_install macro MUST NOT be used to install into the <code>%{buildroot}</code>}}
  
  
Line 206: Line 208:
  
 
==== Patching required gem versions ====
 
==== Patching required gem versions ====
One common patching need is to change overly strict version requirements in the upstream gemspec.  This may be because upstream's gemspec only mentions versions that they've explicitly tested against but we know that a different version will also work or because we know that the packages we ship have applied fixes for problematic behaviour without bumping the version number (for instance, backported fixes).  To fix these issues, find the <code>add_runtime_dependency</code> call in the gemspec and patch it with the corrected version similar to this:
+
One common patching need is to change overly strict version requirements in the upstream <code>.gemspec</code>.  This could be because upstream's <code>.gemspec</code> only mentions versions that they've explicitly tested against but we know that a different version will also work or because we know that the packages we ship have applied fixes for problematic behavior without bumping the version number (for instance, backported fixes).  To adjust such dependencies, you can use the <code>%gemspec_add_dep</code> and <code>%gemspec_remove_dep</code> macros.
 +
 
 +
For example, if you wanted to use any version of Aruba instead of the overly specific version requested by upstream, you could use in <code>%prep</code> section following two lines:
  
 
<pre>
 
<pre>
Gem::Specification.new do |s|
+
%gemspec_remove_dep -g aruba "~> 0.14.2"
  # [...]
+
%gemspec_add_dep -g aruba
- s.add_runtime_dependency(%q<mail>, ["~> 2.2.19"])
 
+  s.add_runtime_dependency(%q<mail>, [">= 2.3.0"])
 
  # [...]
 
end
 
 
</pre>
 
</pre>
 +
 +
{{admon/warning|Use macros only on top of generated <code>.gemspec</code>|The <code>%gemspec_add_dep</code> and <code>%gemspec_remove_dep</code> macros work reliably only on <code>.gemspec</code> generated using the <code>ruby spec</code> command. Please don't use the macros on upstream <code>.gemspec</code> files.}}
  
 
{{admon/warning|Be sure to test|Do not simply change versions without testing that the new version works.  There are times the upstream is overly strict but there are also times when the version requirement was specified because a specific bug was encountered or the API changed in a minor release.}}
 
{{admon/warning|Be sure to test|Do not simply change versions without testing that the new version works.  There are times the upstream is overly strict but there are also times when the version requirement was specified because a specific bug was encountered or the API changed in a minor release.}}
Line 221: Line 223:
 
=== Packaging for Gem and non-Gem use ===
 
=== Packaging for Gem and non-Gem use ===
  
{{admon/caution|Packaging for non-Gem use is no longer needed| Originally, rubygem modules were not placed in ruby's library path, so we packaged rubygems for use with both gems and non-gems.  This allowed code that used <code>require('ARubyModulePackagedAsAGem');</code> to function.  The current <code>rubygem</code> module adds all gems to the ruby library path when it is <code>require</code>'d. So, packagers '''must not''' create non-Gem subpackages of rubygems for new packages. Since the majority of Ruby packages in Fedora are now packaged as installed gems, you may need to patch the code to use <code>require('rubygem')</code> as early in the program as possible to ensure that these ruby components are properly found.  Packages for ruby gems that currently create a non-gem subpackage should be adapted to stop shipping the non-gem subpackage (with a [[Packaging:Guidelines#Renaming.2FReplacing_Existing_Packages | proper Obsoletes and Provides]] in the main rubygem package).}}
+
{{admon/caution|Packaging for non-Gem use is no longer needed| Originally, rubygem modules were not placed in ruby's library path, so we packaged rubygems for use with both gems and non-gems.  This allowed code that used <code>require('ARubyModulePackagedAsAGem');</code> to function.  The current <code>rubygem</code> module adds all gems to the ruby library path when it is <code>require</code>'d. So, packagers MUST NOT create non-Gem subpackages of rubygems for new packages. Since the majority of Ruby packages in Fedora are now packaged as installed gems, you might need to patch the code to use <code>require('rubygem')</code> as early in the program as possible to ensure that these ruby components are properly found.  Packages for ruby gems that currently create a non-gem subpackage SHOULD be adapted to stop shipping the non-gem subpackage (with a [[Packaging:Guidelines#Renaming.2FReplacing_Existing_Packages | proper Obsoletes and Provides]] in the main rubygem package).}}
  
 
=== Non-Gem Packages ===
 
=== Non-Gem Packages ===
  
Non-Gem Ruby packages '''must''' require ruby-devel package at build time with a <code>BuildRequires: ruby-devel</code>, and '''may''' indicate the minimal ruby version they need for building.
+
Non-Gem Ruby packages MUST require ruby-devel package at build time with a <code>BuildRequires: ruby-devel</code>, and MAY indicate the minimal ruby version they need for building.
  
  
 
==== Build Architecture and File Placement ====
 
==== Build Architecture and File Placement ====
The following only affects the files that the package installs into <code>%{ruby_vendorarchdir} </code> and <code>%{ruby_vendorlibdir}</code> (the actual Ruby library files). All other files in a Ruby package must adhere to the general Fedora packaging conventions.
+
The following only affects the files that the package installs into <code>%{ruby_vendorarchdir} </code> and <code>%{ruby_vendorlibdir}</code> (the actual Ruby library files). All other files in a Ruby package MUST adhere to the general Fedora packaging conventions.
  
  
Line 235: Line 237:
  
 
==== Pure Ruby packages ====
 
==== Pure Ruby packages ====
Pure Ruby packages '''must''' be built as noarch packages.
+
Pure Ruby packages MUST be built as noarch packages.
  
The Ruby library files in a pure Ruby package '''must''' be placed into <code>%{ruby_vendorlibdir}</code> (or its proper subdirectory). The specfile '''must''' use this macro.
+
The Ruby library files in a pure Ruby package MUST be placed into <code>%{ruby_vendorlibdir}</code> (or its proper subdirectory). The specfile MUST use this macro.
  
 
{{Anchor|ruby_vendorarchdir}}
 
{{Anchor|ruby_vendorarchdir}}
Line 243: Line 245:
 
==== Ruby packages with binary content/shared libraries ====
 
==== Ruby packages with binary content/shared libraries ====
  
For packages with binary content, e.g., database drivers or any other Ruby bindings to C libraries, the package '''must''' be architecture specific.
+
For packages with binary content, e.g., database drivers or any other Ruby bindings to C libraries, the package MUST be architecture specific.
  
The binary files in a Ruby package with binary content '''must''' be placed into <code>%{ruby_vendorarchdir}</code> (or its proper subdirectory). The Ruby files in such a package '''should''' be placed into %{ruby_vendorlibdir}. The specfile '''must''' use these macros.
+
The binary files in a Ruby package with binary content MUST be placed into <code>%{ruby_vendorarchdir}</code> (or its proper subdirectory). The Ruby files in such a package SHOULD be placed into %{ruby_vendorlibdir}. The specfile MUST use these macros.
  
 
For packages which create C shared libraries using <code>extconf.rb</code>
 
For packages which create C shared libraries using <code>extconf.rb</code>
Line 252: Line 254:
 
export CONFIGURE_ARGS="--with-cflags='%{optflags}'"
 
export CONFIGURE_ARGS="--with-cflags='%{optflags}'"
 
</pre>
 
</pre>
'''should''' be used to pass <code>CFLAGS</code> to <code>Makefile</code> correctly. Also, to place the files into the correct folders during build, pass <code>--vendor</code> to <code>extconf.rb</code> like this:
+
SHOULD be used to pass <code>CFLAGS</code> to <code>Makefile</code> correctly. Also, to place the files into the correct folders during build, pass <code>--vendor</code> to <code>extconf.rb</code> like this:
 
<pre>
 
<pre>
 
extconf.rb --vendor
 
extconf.rb --vendor
Line 266: Line 268:
 
* web applications, typically built using Rails, Sinatra or similar frameworks.  
 
* web applications, typically built using Rails, Sinatra or similar frameworks.  
  
The RPM packages '''must''' obey FHS rules.  They should be installed into <code>%{_datadir}</code>. The following macro can help you:
+
The RPM packages MUST obey FHS rules.  They SHOULD be installed into <code>%{_datadir}</code>. The following macro can help you:
 
<pre>
 
<pre>
 
%global app_root %{_datadir}/%{name}
 
%global app_root %{_datadir}/%{name}
 
</pre>
 
</pre>
  
These packages typically have no "Provides" section, since no other libraries or applications depend on them.
+
These packages typically have no <code>Provides</code> section, since no other libraries or applications depend on them.
  
 
Here's an abbreviated example:
 
Here's an abbreviated example:
Line 309: Line 311:
  
 
%prep
 
%prep
gem unpack -V %{SOURCE0}
+
%setup -q -n %{name}-%{version}
%setup -q -D -T -n %{name}-%{version}
 
  
 
%build
 
%build
Line 347: Line 348:
 
</pre>
 
</pre>
  
Note, that although the source is a RubyGem, we have to install the files manually under %{_datadir}/%{name}, %{_bindir}, etc. to follow FHS and general packaging guidelines.  If additional Fedora specific files (systemd <code>.service</code> files, configurations) are required, they '''should''' be
+
Note, that although the source is a RubyGem, we have to install the files manually under %{_datadir}/%{name}, %{_bindir}, etc. to follow FHS and general packaging guidelines.  If additional Fedora specific files (systemd <code>.service</code> files, configurations) are required, they SHOULD be
 
* added via another <code>%SOURCE</code> tags
 
* added via another <code>%SOURCE</code> tags
 
<pre>
 
<pre>
Line 359: Line 360:
 
== Running test suites ==
 
== Running test suites ==
  
If there is test suite available for the package (even separately, for example not included in the gem but available in the upstream repository), it '''should''' be run in <code>%check</code>. The test suite is the only automated tool which can assure basic functionality of the package.  Running it is especially helpful when mass rebuilds are required. You may skip test suite execution when not all build dependencies are met but this '''must''' be documented in the specfile. The missing build dependencies to enable the test suite should be packaged for Fedora as soon as possible and the test suite re-enabled.
+
If there is test suite available for the package (even separately, for example not included in the gem but available in the upstream repository), it SHOULD be run in <code>%check</code>. The test suite is the only automated tool which can assure basic functionality of the package.  Running it is especially helpful when mass rebuilds are required. You MAY skip test suite execution when not all build dependencies are met but this MUST be documented in the specfile. The missing build dependencies to enable the test suite SHOULD be packaged for Fedora as soon as possible and the test suite re-enabled.
 
 
The tests '''should not''' be run using Rake - Rake almost always draws in some unnecessary dependencies like hoe or gemcutter.
 
  
{{admon/note|Do not ship tests|Normally tests are only run at package buildtimeThey should not be included in the binary rpms that users install on their systems. You may make an exception for this if the package makes public use of the test suite at runtime (for instance, an application package that has a <code>--selftest</code> command line switch that runs its testsuite.)}}
+
The tests SHOULD NOT be run using Rake, as Rake almost always draws in some unnecessary dependencies like hoe or gemcutterFor similar reasons, a dependency on Bundler SHOULD be avoided. Also, code coverage frameworks such as SimpleCov and Coveralls SHOULD be avoided.
  
 
=== Testing With Different Ruby Implementations ===
 
=== Testing With Different Ruby Implementations ===
To run tests with different Ruby implementation such as JRuby, you need to <code>BuildRequires: jruby</code>. Then use Rubypick's interpreter switching:
+
To run tests with different Ruby implementation such as JRuby, add <code>BuildRequires: jruby</code>. Then use Rubypick's interpreter switching:
  
 
<pre>
 
<pre>
testrb _jruby_ -Ilib test
+
ruby _jruby_ -Ilib -e 'Dir.glob "./test/test_*.rb", &method(:require)'
 
</pre>
 
</pre>
  
If your package is running unittests for ruby-mri and it is intended to run under alternate interpreters then it needs to run the unittests under all alternate interpreters as well.  This is the only method we have to check compatibility of the code under each interpreter.  The same rules apply that you can omit this if libraries you need are unavailable for a specific alternate interpreter but you must have a comment to explain.
+
If your package is running unittests for ruby-mri and it is intended to run under alternate interpreters then it needs to run the unittests under all alternate interpreters as well.  This is the only method we have to check compatibility of the code under each interpreter.  The same rules apply that you can omit this if libraries you need are unavailable for a specific alternate interpreter but you MUST have a comment to explain.
  
 
=== Testing frameworks usage ===
 
=== Testing frameworks usage ===
  
The Ruby community supports many testing frameworks. The following sections demonstrate how several to execute several of the more common test suites.
+
The Ruby community supports many testing frameworks. The following sections demonstrate how several to execute test suites using the more common of them.
  
==== MiniTest ====
+
==== MiniTest / Test::UNIT ====
  
MiniTest is the default testing framework shipped with Ruby.  It is unbundled in Fedora (you must use <code>BuildRequires: rubygem-minitest</code>). To run tests using MiniTest you can use something like:
+
MiniTest as well as Test::UNIT are shipped with Ruby.  To use them, you need to use <code>BuildRequires: rubygem-minitest</code> or <code>BuildRequires: rubygem-testunit</code> respectively. To execute the test suite you can use something like:
  
 
<pre>
 
<pre>
 
%check
 
%check
testrb -Ilib test
+
ruby -Ilib -e 'Dir.glob "./test/**/*_test.rb", &method(:require)'
 
</pre>
 
</pre>
  
==== Test::UNIT ====
+
You might need to adjust <code>-Ilib</code> to be <code>-Ilib:test</code>, or you could need to use a slightly different matching pattern for <code>test_*.rb</code>, etc. Packagers are expected to use the right pattern for each gem.
 
 
To run tests using Test::Unit you must <code>BuildRequires: rubygem-test-unit-</code> and use something like this:
 
 
 
<pre>
 
%check
 
testrb2 -Ilib test
 
</pre>
 
 
 
{{admon/note|| Test suites which run using Test::UNIT can typically also be executed by MiniTest. In that case, please prefer MiniTest.}}
 
  
 
==== RSpec ====
 
==== RSpec ====
  
To run tests using RSpec >= 2 you must <code>BuildRequires: rubygem-rspec</code> and use something like:
+
To run tests using RSpec >= 3 you add <code>BuildRequires: rubygem-rspec</code> and use something like:
  
 
<pre>
 
<pre>
Line 406: Line 396:
 
rspec -Ilib spec
 
rspec -Ilib spec
 
</pre>
 
</pre>
 
  
 
=== Test suites not included in the package ===
 
=== Test suites not included in the package ===
Line 420: Line 409:
 
# ...
 
# ...
 
%prep
 
%prep
%setup -q -D -T -n %{gem_name}-%{version}
+
%setup -q -n %{gem_name}-%{version} -b 1
 
 
tar -xzf %{SOURCE1}
 
  
 
# ...
 
# ...
  
 
%check
 
%check
cp -pr spec/ ./%{gem_instdir}
 
 
pushd ./%{gem_instdir}
 
pushd ./%{gem_instdir}
 +
# Link the test suite into the right place in source tree.
 +
ln -s %{_builddir}/spec .
 +
 
# Run tests
 
# Run tests
rm -rf spec
+
rspec spec
 
popd
 
popd
  

Latest revision as of 16:47, 9 May 2018

Note.png
JRuby Gems
Although Fedora has fully functioning JRuby integrated with system RubyGems, we have decided to not include the JRuby specific packaging guidelines here, as they need some more work. They will appear here as soon as we feel that we've got everything covered properly. You can contact us on Ruby-SIG mailing list in case of any questions about the prepared JRuby packaging guidelines.

There are three basic categories of ruby packages: ruby gems, non-gem ruby packages, and applications written in ruby. These guidelines contain sections common to all of these as well as sections which apply to each one individually. Be sure to read all the guidelines relevant to the type of ruby package you are building.

Ruby Compatibility

Each Ruby package MUST indicate it depends on a Ruby interpreter (this does not apply to ruby gem packages). Use ruby(release) virtual requirement to achieve that:

Requires: ruby(release)

If the package requires Ruby of certain version(s), make the requirement versioned like this:

Requires: ruby(release) >= 1.9.1
Note.png
Alternate interpreters
Alternate Ruby interpreters (currently JRuby) also Provide: ruby(release). This implies, that pure RubyGems packages (these are shared among interpreters) SHOULD NOT have Requires: ruby or Requires: jruby to have their dependencies satisfied by any of these interpreters.
Warning.png
Over specified ruby(release) versioning
Please note, that if the ruby(release) version requirement is too specific, it might cause an unexpected interpreter to be drawn in. E.g. ruby(release) = 1.8 will require JRuby package, since it is the only package that provides it.

Different Interpreters Compatibility

Most of the pure Ruby packages will work on all Ruby interpreters. There are however cases, when the packages use interpreter-specific functions (like fork()) and won't run on other interpreters (JRuby). In this case, the package SHOULD require that interpreter. For example, a package that uses fork SHOULD explicitly specify Requires: ruby. In case of such package, packager SHOULD file a bug to ask upstream to provide support for other interpreter(s). This SHOULD be documented in specfile.

Shebang lines

On Fedora, /usr/bin/ruby is implemented via Rubypick. Rubypick is a tool similar to RVM or rbenv. It allows choosing interpreter to execute Ruby script. Rubypick routes anything executed via /usr/bin/ruby to /usr/bin/ruby-mri or /usr/bin/jruby. By default, it runs MRI (Matz's Ruby Implementation), but user can explicitly specify the interpreter by using _mri_ or _jruby_ as a first parameter. For example:

ruby _jruby_ jruby_script.rb
gem _mri_ install foo
rails _jruby_ s

Using the RUBYPICK environment variable can achieve the same results. The environment variable can be used to set one interpreter as the global default:

export RUBYPICK=_jruby_
ruby jruby_script.rb  # Will use jruby
gem install foo        # Will also use jruby

Ruby executables that are known to only run on one Ruby implementation SHOULD use that specific implementation in their shebang - #!/usr/bin/ruby-mri or #!/usr/bin/jruby to ensure that they run using that implementation. All other code SHOULD use #!/usr/bin/ruby.

Naming Guidelines

  • Packages that contain Ruby Gems MUST be called rubygem-%{gem_name}.
  • The name of a ruby extension/library package MUST start with the interpreter it is built for (ruby, jruby, etc) and then the UPSTREAM name. For example: ruby-UPSTREAM. If the upstream name UPSTREAM contains ruby, that SHOULD be dropped from the name. For example, the SQLite database driver for ruby is called sqlite3-ruby. The corresponding Fedora package SHOULD be called ruby-sqlite3, and not ruby-sqlite3-ruby.
  • Application packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the general NamingGuidelines instead.

Macros

Non-gem ruby packages and ruby gem packages install to certain standard locations. The ruby-devel and rubygems-devel packages contain macros useful for the respective package types. Alternate ruby interpreters will have equivalent locations (To be added to this table later)

Macro Expanded path Usage
From ruby-devel; intended for non-gem packages.
%{ruby_vendorarchdir} /usr/lib{64}/ruby/vendor_ruby Place for architecture specific (e.g. *.so) files.
%{ruby_vendorlibdir} /usr/share/ruby/vendor_ruby Place for architecture independent (e.g. *.rb) files.
%{ruby_sitearchdir} /usr/local/lib{64}/ruby/site_ruby Place for local architecture specific (e.g. *.so) files.
%{ruby_sitelibdir} /usr/local/share/ruby/site_ruby Place for local architecture independent (e.g. *.rb) files.
From rubygems-devel; intended for gem packages
%{gem_dir} /usr/share/gems Top directory for the Gem structure.
%{gem_instdir} %{gem_dir}/gems/%{gem_name}-%{version} Directory with the actual content of the Gem.
%{gem_libdir} %{gem_instdir}/lib The lib folder of the Gem.
%{gem_cache} %{gem_dir}/cache/%{gem_name}-%{version}.gem The cached Gem.
%{gem_spec} %{gem_dir}/specifications/%{gem_name}-%{version}.gemspec The Gem specification file.
%{gem_docdir} %{gem_dir}/doc/%{gem_name}-%{version} The rdoc documentation of the Gem.
%{gem_extdir_mri} %{_libdir}/gems/ruby/%{gem_name}-%{version} The directory for MRI Ruby Gem extensions.

Interpreter independence and directory macros

You might have noticed that the table above has different directories for non-gem libraries on different ruby interpreters but only a single set of directories for rubygem libraries. This is because code written for one ruby interpreter will often run on all ruby interpreters that Fedora ships (ruby, jruby, etc). However, some code uses methods that are not available on all interpreters (see Different Interpreters Compatibility). Rubygems have a facility to ship different versions of the code in the same gem so that the gem can run on all versions of the interpreter so we only need to have one common directory for rubygems that all the interpreters can use.

The standard ruby %{vendorlib} directories lack this facility. For this reason, non-gem libraries need to be placed in per-interpreter directories and MUST have a separate subpackage (or package depending on upstream) for each interpreter that they support.

Libraries

These guidelines only apply to Ruby packages whose main purpose is providing a Ruby library; packages that mainly provide user-level tools that happen to be written in Ruby MUST follow the ruby applications guidelines instead.

RubyGems

RubyGems are Ruby's own packaging format. Gems contain a lot of the same metadata that RPM's need, making fairly smooth interoperation between RPM and Gems possible. This guideline ensures that Gems are packaged as RPM's in a way that such RPM's fit cleanly with the rest of the distribution and to make it possible for the end user to satisfy dependencies of a Gem by installing the appropriate RPM-packaged Gem.

Both RPM's and Gems use similar terminology --- there are specfiles, package names, dependencies etc. for both. To keep confusion to a minimum, terms relating to Gem concepts will be explicitly refereed to with the word 'Gem' prefixed, eg 'Gem specification' (.gemspec). An unqualified 'package' in the following always means an RPM.

  • Spec files MUST contain a definition of %{gem_name} which is the name from the Gem's specification.
  • The Source of the package MUST be the full URL to the released Gem archive; the version of the package MUST be the Gem's version.
  • The package MUST BuildRequires: rubygems-devel to pull in the macros needed to build.
  • There SHOULD NOT be any rubygem Requires nor Provides listed, since those are autogenerated.
  • There SHOULD NOT be Requires: ruby(release), unless you want to explicitly specify Ruby version compatibility. Automatically generated dependency on RubyGems (Requires: ruby(rubygems)) is enough.

Filtering Requires and Provides

Runtime requires and provides are automatically generated by RPM's dependency generator. However, it can sometimes throw in additional dependencies contrary to reality. To fix this, the dependency generator needs to be overridden so that the additional dependencies can be filtered out. See Packaging:AutoProvidesAndRequiresFiltering for details.

Building gems

Since gems aren't a standard archive format that rpm knows about and they encapsulate both an archive format and information to build the ruby library building an rpm from a gem looks a little different from other rpms.

A sample spec for building gems would look like this:

%prep
%setup -q -n  %{gem_name}-%{version}

# Modify the gemspec if necessary
# Also apply patches to code if necessary
%patch0 -p1

%build
# Create the gem as gem install only works on a gem file
gem build ../%{gem_name}-%{version}.gemspec

# %%gem_install compiles any C extensions and installs the gem into ./%%gem_dir
# by default, so that we can move it into the buildroot in %%install
%gem_install

%install
mkdir -p %{buildroot}%{gem_dir}
cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/

# If there were programs installed:
mkdir -p %{buildroot}%{_bindir}
cp -a ./%{_bindir}/* %{buildroot}%{_bindir}

# If there are C extensions, copy them to the extdir.
mkdir -p %{buildroot}%{gem_extdir_mri}
cp -a .%{gem_extdir_mri}/{gem.build_complete,*.so} %{buildroot}%{gem_extdir_mri}/
%prep

RPM (as of 4.14) can directly unpack gem archives, so we can call gem unpack to extract the source from the gem. Then we call %setup -n %{gem_name}-%{version} to tell rpm what the directory the gem has unpacked into. At the same directory level, the %{gem_name}-%{version}.gemspec file is created automatically as well. This .gemspec file will be used to rebuild the gem later. If we need to modify the .gemspec (for instance, if the version of dependencies is wrong for Fedora or the .gemspec is using old, no longer supported fields) we would do it here. Patches to the code itself can also be done here.

Warning.png
Different Guidelines for Fedora 26 and older
In Fedora 26 and older releases, the rubygem %prep section has to be slightly different, since the %setup macro cannot expand the gem yet. Please use the following snippet to mimic the RPM 4.14 %setup macro functionality:
%prep
gem unpack %{SOURCE0}
gem spec %{SOURCE0} -l --ruby > %{gem_name}-%{version}.gemspec

%setup -q -D -T -n  %{gem_name}-%{version}

# Modify the gemspec if necessary
# Also apply patches to code if necessary
%patch0 -p1
The rest of the package can stay the same for all Fedora versions.
%build

Next we build the gem. Because %gem_install only operates on gem archives, we next recreate the gem with gem build. The gem file that is created is then used by %gem_install to build and install the code into the temporary directory, which is ./%{gem_dir} by default. We do this because the %gem_install command both builds and installs the code in one step so we need to have a temporary directory to place the built sources before installing them in %install section.

%gem_install macro accepts two additional options:

-n <gem_file>
Allows to override gem used for installation. This might get useful for converting legacy spec, so you might specify %{SOURCE0} as a gem for installation.
-d <install_dir>
Might override the gem installation destination. However we do not suggest to use this option.
Note.png
The %gem_install macro MUST NOT be used to install into the %{buildroot}


%install

Here we actually install into the %{buildroot}. We create the directories that we need and then copy what was installed into the temporary directories into the %{buildroot} hierarchy. Finally, if this ruby gem creates shared objects the shared objects are moved into the arch specific %{gem_extdir_mri} path.

Patching required gem versions

One common patching need is to change overly strict version requirements in the upstream .gemspec. This could be because upstream's .gemspec only mentions versions that they've explicitly tested against but we know that a different version will also work or because we know that the packages we ship have applied fixes for problematic behavior without bumping the version number (for instance, backported fixes). To adjust such dependencies, you can use the %gemspec_add_dep and %gemspec_remove_dep macros.

For example, if you wanted to use any version of Aruba instead of the overly specific version requested by upstream, you could use in %prep section following two lines:

%gemspec_remove_dep -g aruba "~> 0.14.2"
%gemspec_add_dep -g aruba
Warning.png
Use macros only on top of generated .gemspec
The %gemspec_add_dep and %gemspec_remove_dep macros work reliably only on .gemspec generated using the ruby spec command. Please don't use the macros on upstream .gemspec files.
Warning.png
Be sure to test
Do not simply change versions without testing that the new version works. There are times the upstream is overly strict but there are also times when the version requirement was specified because a specific bug was encountered or the API changed in a minor release.

Packaging for Gem and non-Gem use

Stop (medium size).png
Packaging for non-Gem use is no longer needed
Originally, rubygem modules were not placed in ruby's library path, so we packaged rubygems for use with both gems and non-gems. This allowed code that used require('ARubyModulePackagedAsAGem'); to function. The current rubygem module adds all gems to the ruby library path when it is require'd. So, packagers MUST NOT create non-Gem subpackages of rubygems for new packages. Since the majority of Ruby packages in Fedora are now packaged as installed gems, you might need to patch the code to use require('rubygem') as early in the program as possible to ensure that these ruby components are properly found. Packages for ruby gems that currently create a non-gem subpackage SHOULD be adapted to stop shipping the non-gem subpackage (with a proper Obsoletes and Provides in the main rubygem package).

Non-Gem Packages

Non-Gem Ruby packages MUST require ruby-devel package at build time with a BuildRequires: ruby-devel, and MAY indicate the minimal ruby version they need for building.


Build Architecture and File Placement

The following only affects the files that the package installs into %{ruby_vendorarchdir} and %{ruby_vendorlibdir} (the actual Ruby library files). All other files in a Ruby package MUST adhere to the general Fedora packaging conventions.


Warning.png
Site versus Vendor
Previously, %{ruby_sitelibdir} and %{ruby_sitearchdir} were used. However, as they are meant only for local installations, please use %{ruby_vendorlibdir} and %{ruby_vendorarchdir} instead.

Pure Ruby packages

Pure Ruby packages MUST be built as noarch packages.

The Ruby library files in a pure Ruby package MUST be placed into %{ruby_vendorlibdir} (or its proper subdirectory). The specfile MUST use this macro.

Ruby packages with binary content/shared libraries

For packages with binary content, e.g., database drivers or any other Ruby bindings to C libraries, the package MUST be architecture specific.

The binary files in a Ruby package with binary content MUST be placed into %{ruby_vendorarchdir} (or its proper subdirectory). The Ruby files in such a package SHOULD be placed into %{ruby_vendorlibdir}. The specfile MUST use these macros.

For packages which create C shared libraries using extconf.rb

export CONFIGURE_ARGS="--with-cflags='%{optflags}'"

SHOULD be used to pass CFLAGS to Makefile correctly. Also, to place the files into the correct folders during build, pass --vendor to extconf.rb like this:

extconf.rb --vendor

Applications

Applications are

  • programs that provide user-level tools or
  • web applications, typically built using Rails, Sinatra or similar frameworks.

The RPM packages MUST obey FHS rules. They SHOULD be installed into %{_datadir}. The following macro can help you:

%global app_root %{_datadir}/%{name}

These packages typically have no Provides section, since no other libraries or applications depend on them.

Here's an abbreviated example:

%global app_root %{_datadir}/%{name}

Summary: Deltacloud REST API
Name: deltacloud-core
Version: 0.3.0
Release: 3%{?dist}
Group: Development/Languages
License: ASL 2.0 and MIT
URL: http://incubator.apache.org/deltacloud
Source0: http://gems.rubyforge.org/gems/%{name}-%{version}.gem
Requires: rubygem-haml
#...
Requires(post):   chkconfig
#...
BuildRequires: rubygem-haml
#...
BuildArch: noarch

%description
The Deltacloud API is built as a service-based REST API.
You do not directly link a Deltacloud library into your program to use it.
Instead, a client speaks the Deltacloud API over HTTP to a server
which implements the REST interface.

%package doc
Summary: Documentation for %{name}
Group: Documentation
Requires:%{name} = %{version}-%{release}

%description doc
Documentation for %{name}

%prep
%setup -q -n %{name}-%{version}

%build

%install
mkdir -p %{buildroot}%{app_root}
mkdir -p %{buildroot}%{_initddir}
mkdir -p %{buildroot}%{_bindir}
cp -r * %{buildroot}%{app_root}
mv %{buildroot}%{app_root}/support/fedora/%{name} %{buildroot}%{_initddir}
find %{buildroot}%{app_root}/lib -type f | xargs chmod -x
chmod 0755 %{buildroot}%{_initddir}/%{name}
chmod 0755 %{buildroot}%{app_root}/bin/deltacloudd
rm -rf %{buildroot}%{app_root}/support
rdoc --op %{buildroot}%{_defaultdocdir}/%{name}

%post
# This adds the proper /etc/rc*.d links for the script
/sbin/chkconfig --add %{name}

%files
%{_initddir}/%{name}
%{_bindir}/deltacloudd
%dir %{app_root}/
%{app_root}/bin
#...

%files doc
%{_defaultdocdir}/%{name}
%{app_root}/tests
%{app_root}/%{name}.gemspec
%{app_root}/Rakefile

%changelog
#...

Note, that although the source is a RubyGem, we have to install the files manually under %{_datadir}/%{name}, %{_bindir}, etc. to follow FHS and general packaging guidelines. If additional Fedora specific files (systemd .service files, configurations) are required, they SHOULD be

  • added via another %SOURCE tags
Source1: deltacloudd-fedora
  • placed into appropriate locations during %install stage
install -m 0755 %{SOURCE1} %{buildroot}%{_bindir}/deltacloudd

Running test suites

If there is test suite available for the package (even separately, for example not included in the gem but available in the upstream repository), it SHOULD be run in %check. The test suite is the only automated tool which can assure basic functionality of the package. Running it is especially helpful when mass rebuilds are required. You MAY skip test suite execution when not all build dependencies are met but this MUST be documented in the specfile. The missing build dependencies to enable the test suite SHOULD be packaged for Fedora as soon as possible and the test suite re-enabled.

The tests SHOULD NOT be run using Rake, as Rake almost always draws in some unnecessary dependencies like hoe or gemcutter. For similar reasons, a dependency on Bundler SHOULD be avoided. Also, code coverage frameworks such as SimpleCov and Coveralls SHOULD be avoided.

Testing With Different Ruby Implementations

To run tests with different Ruby implementation such as JRuby, add BuildRequires: jruby. Then use Rubypick's interpreter switching:

ruby _jruby_ -Ilib -e 'Dir.glob "./test/test_*.rb", &method(:require)'

If your package is running unittests for ruby-mri and it is intended to run under alternate interpreters then it needs to run the unittests under all alternate interpreters as well. This is the only method we have to check compatibility of the code under each interpreter. The same rules apply that you can omit this if libraries you need are unavailable for a specific alternate interpreter but you MUST have a comment to explain.

Testing frameworks usage

The Ruby community supports many testing frameworks. The following sections demonstrate how several to execute test suites using the more common of them.

MiniTest / Test::UNIT

MiniTest as well as Test::UNIT are shipped with Ruby. To use them, you need to use BuildRequires: rubygem-minitest or BuildRequires: rubygem-testunit respectively. To execute the test suite you can use something like:

%check
ruby -Ilib -e 'Dir.glob "./test/**/*_test.rb", &method(:require)'

You might need to adjust -Ilib to be -Ilib:test, or you could need to use a slightly different matching pattern for test_*.rb, etc. Packagers are expected to use the right pattern for each gem.

RSpec

To run tests using RSpec >= 3 you add BuildRequires: rubygem-rspec and use something like:

%check
rspec -Ilib spec

Test suites not included in the package

Sometimes you have to get the tests separately from upstream's gem package. As an example lets suppose you're packaging rubygem-delorean, version 1.2.0, which is hosted on Github. Tests are not included in the Gem itself, so you need to get them and adjust the specfile accordingly:

# git clone https://github.com/bebanjo/delorean.git && cd delorean
# git checkout v1.2.0
# tar -czf rubygem-delorean-1.2.0-specs.tgz spec/
Source1: %{name}-%{version}-specs.tgz

# ...
%prep
%setup -q -n %{gem_name}-%{version} -b 1

# ...

%check
pushd ./%{gem_instdir}
# Link the test suite into the right place in source tree.
ln -s %{_builddir}/spec .

# Run tests
rspec spec
popd

# ...

  • Make sure to include the version of the tests in the source name, so that when updating to new version, rpmbuild will fail because it won't find the proper %{SOURCE1} (and this will remind you to update the tests, too).
  • Add the commands you used to get the tests into the specfile as comments. This will make it a lot easier the next time you will need to get them.
  • Run the tests as you normally would.