Ruby/Policy 2.0: Difference between revisions

From ALT Linux Wiki
No edit summary
 
(12 intermediate revisions by the same user not shown)
Line 5: Line 5:
}}
}}


== Ruby Policy ==
== Ruby Policy 2.0 ==


__TOC__
__TOC__
Line 11: Line 11:
'''''Rules for ruby module and application packages'''''
'''''Rules for ruby module and application packages'''''


=== Introduction ===
[[Category:Ruby]]
 
==== Ruby and modularity ====
 
NOTE: To simplify link usage, they will be treated as for 2.6.1 ruby version.
 
The ruby language have the modularity feature begginging with the day-spring of its invention in 1993th, so if we have [http://ruby-doc.org/core-2.6.1/all the core functions of ruby] are placed into the ''libruby.so'', are built-in part of the language, then [http://ruby-doc.org/stdlib-2.6.1/ ruby's standard library] is modularized, and its modules are resided in the <tt>/usr/lib/ruby/</tt> folder that has the interpreted modules inside the consequently named folders. For example: '''parser''' module is placed in the ''racc'' module:
 
    <tt>/usr/lib/ruby/racc/parser.rb</tt>
 
Inside the folder named as <arch>-<os>, there are some compiled parts for the respective modules, so ''''cparse''' modules is placed in ''racc'' module:
 
    <tt>/usr/lib/ruby/x86_64-linux/racc/cparse.so</tt>
 
Therefore the when a side library (module) ought to be be used in ordinal ruby applications, then its library modules must be placed into the respective folders in ''/usr/lib/ruby/site_ruby/'', and the last is the only place to reside libraries not from the ruby standard set.
 
The the modern context, which is valid for 2019, in order to deploy additional modules, the '''rubygems''' and '''bundler''' modules are used, which kind of the modules is called as ''gem'', and the first of them previously treated as usual ruby side module. The  '''rubygems''' has begun in 2009 Apr., '''bundler''' in four months last than '''rubygems''' in Aug the same year. In present, the pair of the gems is de-facto an de-jure the only dynamically working system of dependencies of ruby modules system.
 
In modern projects the ruby gem dependencies can be provided with two ways:
* via ''<gem>.gemspec'';
* via ''Gemfile''.
 
The ''.gemspec'' is the only file to declare dependencies for '''rubygems''' between the gems themselves. The dependencies in the file can be joined into two groups separately: runtime, and development, and the least can be used also to test the gem project.
 
The ''Gemfile'' is the file to define local dependencies (additionally to ''.gemspec'') from the other gems. The project containing the ''Gemfile'' counkl not be exactly the ''gem'', but it can be one of the modern ruby application type as: rails or sinatra application, ruby script application. The dependencies can be joined into various kinds of groups along the ''default'' one like ''production'', ''development'', ''test'', ''assets'' etc. Based on the current environment and written in the '''Gemfile''' scheme, the '''bundler''' makes a decision itself, which the gems shall be loaded, and which must be hidden that leads to situation when explicit declaration to call a module from a gem will be avoided. For example it can be observed in rails application like '''chef''' or '''foreman''', or script application '''opennebula'''.
 
Typical ''Gemfile'' can be found [https://raw.githubusercontent.com/theforeman/foreman/develop/Gemfile here].
 
Dependencies to ruby also can be defined in '''Gemfile''' or '''.gemspec'''.
 
==== Versioning ====
 
In the '''ruby''' and its packaging subsystem the [https://semver.org/lang/en/ semantic versioning] approach is applied, in which the first number responds to the major modification in the module, saying when the API is changed. The second number - minor modification, for example when a new feature is added to the module, and the third number means the patch change.
 
=== Comparison to the [[Ruby Policy|previous policy]] ===
 
Strictly speaking the approach with dependencies search over the only '''require''' calls to the modern time is significantly restricted by the following points:
* this method calls are not always exactly detecting the real dependencies list for gems, and increasingly for ''Gemfile''-based projects, insofar as there may be non-mandatory calls to ''require'' in the code, which are framed in the ''begin-end'' code that makes the call optional;
* for ''Gemfile''-based projects the ''require'' calls are implicit and executed by '''bundler''' indirectly based on the ''Gemfile'' and ''.gemspec'' files analysis, if any, then '''bundler''' monitors for integrity of the current module development environment, this leads to that the ''require'' call to external module, in case the dependency to the module isn't declared in the ''Gemfile'', will raises the module not found error that will block the project development (in case of rpm building to build error);
* a name of external module is not always can be inferred from analysis of ''require'' call lines, since some modules have non-standard naming in a relation to their gem, for example a gem can have name 'foo-bar', its modules can have '''FooBar''', '''FooBar''', или '''Foo/Bar''', but correspondence to those files will have ''foo_bar'', ''foo/bar'', and the last is falsely triggered on '''foo''' module name;
* implicit loading of the external project modules, because '''bundler''' make the calls to that modules itself;
* technique for dependency detection from ''require'' isn't capable to control API-version of the dependent package, when it is changed, especially in case when the package requires old and non-updated API.
 
Thus, in modern times is it more safely to use ''Gemfile''/''.gemspec'' file pair analysis to determine the dependencies.
 
=== The 'ruby' package ===
 
In the system there is the only ''libruby'' library, with a single set of the ''stdlibs'' modules.
 
When a new version of the ''libruby'' package has been built, all the ''stdlibs'' modules is rebuilt also, along with all gems that contain dynamically loaded ''.so'' libraries that conforms to the new version of ''libruby'', or where the ''Gemfile''/''.gemspec'' file pair contains explicit requirement to the ruby version in.
 
=== External view of package ===
 
# packages that have been built from a gem, must have '''gem-''' prefix;
# source packages containing the rails, sinatra or similar applications can be without a prefix or have '''rubyapp-''' prefix;
# ''stdlibs'' modules along with other projects that don't belong to previous two points, and non-gem libraries must have '''ruby-''' prefix, and their files be placed into ''site_ruby'' folder;
# underscore chars "_" are replaced to minus '-' char in the names of packages.
 
=== Dependencies ===
 
# From now dependencies is being supervised based on ''Gemfile''/''.gemspec'' file pair analysis.
# Export of automatically calculated gem dependencies has view of '''gem(gemname) = version''' and '''rubygem(gemname) = version''', the last form of the dependency has been kept to make them compatible to ''redhat'' ones.
 
==== Dependency conflict ====
 
In case of the dependency conflict is risen, one of the following ways to resolve them is applied:


# when and increase of a dependent gem version doesn't lead to degrade of workability of the parent gem, it is useful to apply explicit inline gem replacement by using '''ruby_use_gem_dependency''', for example from '''>= 1.1.7''' to '''>= 1.7''' for gem named "gem_name", you shall to specify the only target version of the depending gem: <pre>%ruby_use_gem_dependency gem_name >= 1.7</pre>
# The name of package depends on the repo containments and there is of three main types, so list below shows types and their according namings:
# since '''bundler''' allows keeping in the same tree of gems more than one version of each, but ALT packaging system doesn't allow making that thing, it is necessary to create an additional compatible package  with the required version of the same gem. Name of the package must contain the same package name (as original non-compat), but also have full version including patch number fulled with "minus" chars. For example gem ''compat-gem'' will have full name <tt>gem-compat-gem-1-2-1.2.0.src.rpm</tt>;
## '''gem''': project contains one of several gems depending of one of the contained gems, like: [https://packages.altlinux.org/ru/sisyphus/srpms/gem-concurrent-ruby/specfiles concurrent-ruby], [https://packages.altlinux.org/ru/sisyphus/srpms/gem-train/specfiles train], have to be renamed with one, which begins with ''gem-'' prefix of shorest gem included in repo, with replacement '_', and '.' chars into '-', like name '''smart_proxy.one''' has to become '''gem-smart-proxy-one''' when packaged.
# Gem dependencies, which there is building ''.so'' libraries from scources in, the dependency to current version of ruby is setup automatically, in order to rebuild of the ruby with a new version have forces to rebuild such kind of packages.
## '''gemfile''': projects based on the ''Gemfile'' or ''Rakefile'', for example rails or sinatra based projects like: [https://packages.altlinux.org/ru/sisyphus/srpms/foreman/specfiles foreman], keep their names as is; 
 
## '''complex''': gem grouped projects, name of which can't be unambiguously inferred, like: [https://packages.altlinux.org/ru/sisyphus/srpms/ruby-gnome2/specfiles gnome2], keep their names as is;
=== RPM Macros ===
# Placement of the ruby site, vendor or gem modules is defined by [https://packages.altlinux.org/ru/sisyphus/binary/rpm-macros-ruby/ rpm-ruby-macros] package, which is generated by [https://packages.altlinux.org/ru/sisyphus/srpms/ruby ruby];
 
# Conflicts
Macros, which are required to gem and ruby-application packetizing, are defined in the ''rpm-build-ruby''. And this file must be defined in spec with keyword '''BuilderRequires(pre)''', so we'll get <tt>BuildRequires(pre): rpm-build-ruby</tt>. This is require to build a package with any of kind ruby application gem or pure ruby.
## To solve name conflict, for example if there are gem name like '''gem.name''' and '''gem_name''' that gives the same package name, alternate secondary packages must have '''-alt''' suffix in their names;
 
## Frequent version conflicts can be simply solved with specific ruby macros like of [https://packages.altlinux.org/ru/sisyphus/srpms/gem-nokogiri/specfiles/ gem-nokogiri];
According to the module folder separation in ruby documentation, vendor (in our case is the ALT) dependent modules have to be put into vendor folders, and end user modules, for example when a user is building ruby app in its computer, have to be put into site folders. ".so" libraries are to be put in arch dependent folders, for example '''%ruby_vendorarchdir''', and ".rb" libraries are into just non-arch folders like '''%ruby_vendorlibdir'''.
## In some cases maintainer is requiring to use lesser version of the ruby module, so there two approaches:
 
### downgrade package version with increment of epoch number ('''not recommended''');
Ruby folder and internal variable macros are the following:
### build compatibility version package appending '''-compat''' suffix it package's name, like [https://packages.altlinux.org/ru/sisyphus/srpms/gem-smart-proxy-compat/specfiles gem-smart-proxy-compat];
 
# Versioning
<pre>
## Versioning of the ruby modules is conforming original package's semantic versioning approach.
# ruby file and folder macros
## In case if a maintainer requires to apply changes to a module, for example, either with a patch, or/and from a specific commit from the git tree, it should add a patch version counting from the nearest back release to the current commit, to affect gemspec version change, and the add "zero" right after '''-alt''' release, like '''alt-0.1''' instead of '''-alt1'''
%ruby_gemspecdir        /usr/lib/ruby/gemie/specifications
# Dependencies
%ruby_gemsextdir        /usr/lib64/ruby/gemie/extensions
## If it is required to use explicit dependencies the format of '''gem(gem_name) = %version''' is preferred before the '''gem-gem-name = %EVR''' one, since it set up the correct dependency to the gem, of course devel packages, which usually contains development headers and required to build the package dependencies, can be addressed with '''gem-gem-name-devel = %EVR''' form.
%ruby_gemslibdir        /usr/lib/ruby/gemie/gems
%ruby_gemsdocdir        /usr/share/ri
%ruby_includedir        /usr/include
%ruby_gemspec          %ruby_gemspecdir/%gemname-%version.gemspec
%ruby_gemextdir        %ruby_gemsextdir/%gemname-%version
%ruby_gemlibdir        %ruby_gemslibdir/%gemname-%version
%ruby_gemdocdir        %ruby_gemsdocdir/%gemname-%version
%ruby_gemincludedir    %ruby_includedir/%gemname
%ruby_sitedocdir        /usr/share/ri
%ruby_sitearch          x86_64-linux
%ruby_sitearchdir      /usr/local/lib64/ruby
%ruby_sitelibdir        /usr/local/lib/ruby
%ruby_siteincludedir    /usr/include/ruby
%ruby_vendorhdrdir      /usr/include/vendor_ruby
%ruby_vendorarchdir    /usr/lib64/ruby/vendor_ruby/
%ruby_vendorlibdir      /usr/lib/ruby/vendor_ruby
%ruby_vendorarchhdrdir /usr/include/vendor_ruby
 
</pre>
 
=== Files placement ===
 
The library files for a gem must be placed to the proper subfolders for the gem in ''/usr/lib/ruby/gems/2.5.0/'', where  2.5.0 is the current ruby version, without a patch number.
 
=== Package specification notes ===
 
# all the packages containing the main code (meaning ruby and dynamic libraries) of the gem, must have the ''Development/Ruby'' group, and packages containing executables must have group name that belongs to its purpose.
# packages, which is not containing any kind of architecture-dependent binaries, must have a declaration of ''noarch'' architecture explicitly.
 
==== Typical .spec ====
 
To fix an old or broken packages, along with the to create new ones, the maintainer can use the following typical spec file which is show [[SampleSpecs/rubymodule|here]].
 
The '''%ruby_build''' macro is being expanded to call to ''setup.rb'' install action, with fix to shebang line with auto param as ''--shebang=auto'' This forces to automatically detect ruby interpreter installation path and replace all the ruby executables with the proper shebang line.
 
=== Testing ===
 
Testing of the ruby based packages can be carried out by the '''gem-setup''' package that defines and runs required to the testing of the packages specs, which are usually resided in the ''spec''/''test''/''features'' folders. The required the execute that test procedure gems are defined in the '''Gemspec'''/'''.gemspec''' pair. In order to enable the testing procedure we need to explicitly decleare the required gems in the package '''.spec''', for example:
 
<pre>BuildRequires: gem(required_to_test_gem) >= 1.0 </pre>
 
=== This scheme migration rules ===
 
# all the '''Requires''' dependencies for all the subpackages are determined automatically, so that all the dependencies of suck kind must be rejected;
# '''BuildRequires''' dependencies for the package in case when testing shell be executed (see ''%check'' section), or when it is really necessary to do, must be explicitly defined, and can be got by the '''setup.rb''' application;
# when the package has been renamed, it is required to explicitly define the old package name in '''Provides''' and '''Obsoletes''' keywords;
# In the ''%prep'' all the macros pointing to ''ruby'' are rejected, in particular '''%update_setup_rb''';
# In the ''%build'' all the macros pointing to ''ruby'' are replaced with '''%ruby_build''', with '''%ruby_config''' in particular '''%ruby_build''';
# In the ''%install'' all the macros pointing to ''ruby'' are replaced with '''%ruby_install''';
# In the ''%check'' all the macros pointing to ''ruby'' are replaced with '''%ruby_test''', although in case of the extremely and urgent need this macro can be grayed out;
# In the ''%files'' class sections the following rules of replacement are applied: '''%[ruby]gem_specdir''' to '''%ruby_gemspecdir''', '''%ruby_vendorlibdir''' to '''%ruby_gemlibdir''', '''%ruby_vendorarchdir''' to '''%ruby_gemextdir''';
# In the ''%files'' for documentation '''%ruby_ri_sitedir''' is replaced with '''%ruby_gemdocdir'''.
 
=== Automatization ===
 
To automatize the watch and update the ruby-based packages the gem '''gem-rubobot''' shall be used, to accomplish the following functions:
* execute the watch for new ruby-based package versions and notify the subscribers about their appearance on '''rubygems.org''';
* execute the watch for new version of ruby language, notify the subscribers about their appearance;
* make an integrity control over the gem subsystem in Sisyphus, by the aim of the specific '''Gemfile''';
* monitor on the new non-packaged gem download count (from '''rubygems.org'''), and when the count is become more than a ''packetize threshold'' (by default 1M downloads), in automatic mode create an RPM-package based on the gem, and notify in both case of success or failure;
* try to update gem packages in automatic mode keeping integrity control of the built gem packages, and notify when it is impossible;
* try to update ruby application packages in automatic mode keeping integrity control of the built gem packages, the build here is allowing on-the-fly the replacement gem version to packages that are presented in the repo, and notify when it is impossible;
* update gem packages packages in semi-automatic mode (i.e. by the directive from maintainer) keeping integrity control of the built gem packages, with an automatic creation of the gem compatible packages;
* update ruby language package packages in semi-automatic mode (i.e. by the directive from maintainer) with automatic rebuild of dependent packages.
 
[[Category:Ruby]]

Latest revision as of 01:46, 9 February 2024

Stub.png
Sisyphus Policy draft
Authur(s) — Малъ Скрылевъ
Discussion in devel@
Discussed since 05.02.2019


Ruby Policy 2.0

Rules for ruby module and application packages

  1. The name of package depends on the repo containments and there is of three main types, so list below shows types and their according namings:
    1. gem: project contains one of several gems depending of one of the contained gems, like: concurrent-ruby, train, have to be renamed with one, which begins with gem- prefix of shorest gem included in repo, with replacement '_', and '.' chars into '-', like name smart_proxy.one has to become gem-smart-proxy-one when packaged.
    2. gemfile: projects based on the Gemfile or Rakefile, for example rails or sinatra based projects like: foreman, keep their names as is;
    3. complex: gem grouped projects, name of which can't be unambiguously inferred, like: gnome2, keep their names as is;
  2. Placement of the ruby site, vendor or gem modules is defined by rpm-ruby-macros package, which is generated by ruby;
  3. Conflicts
    1. To solve name conflict, for example if there are gem name like gem.name and gem_name that gives the same package name, alternate secondary packages must have -alt suffix in their names;
    2. Frequent version conflicts can be simply solved with specific ruby macros like of gem-nokogiri;
    3. In some cases maintainer is requiring to use lesser version of the ruby module, so there two approaches:
      1. downgrade package version with increment of epoch number (not recommended);
      2. build compatibility version package appending -compat suffix it package's name, like gem-smart-proxy-compat;
  4. Versioning
    1. Versioning of the ruby modules is conforming original package's semantic versioning approach.
    2. In case if a maintainer requires to apply changes to a module, for example, either with a patch, or/and from a specific commit from the git tree, it should add a patch version counting from the nearest back release to the current commit, to affect gemspec version change, and the add "zero" right after -alt release, like alt-0.1 instead of -alt1
  5. Dependencies
    1. If it is required to use explicit dependencies the format of gem(gem_name) = %version is preferred before the gem-gem-name = %EVR one, since it set up the correct dependency to the gem, of course devel packages, which usually contains development headers and required to build the package dependencies, can be addressed with gem-gem-name-devel = %EVR form.