Ruby Policy 2.0: Difference between revisions

From ALT Linux Wiki
(redirect added)
Tag: New redirect
 
(8 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{DraftPolicy
#REDIRECT [[Ruby/Policy_2.0]]
|responsible=[[Участник:majioa|Малъ Скрылевъ]]
|discussion_link=https://lists.altlinux.org/pipermail/devel/2019-February/206679.html
|discussion_since=05.02.2019
}}
 
== Ruby Policy ==
 
__TOC__
 
'''''Rules for ruby module and application packages'''''
 
=== Introduction ===
 
==== 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'' (russian ''''бисер'''), 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 '''gem_replace_version''', for example from '''~> 1.1.7''' to '''~> 1.7''':
 
    <pre>%gem_replace_version '~> 1.1.7' '~> 1.7'</pre>
 
# 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 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.
 
=== Макросы RPM ===
 
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>:
 
Ruby build macros are the following:
 
<pre>
%gemname                %(/bin/sed -e "s/\\(dbus\\|ldap\\)/ruby-\\1/" -e "s/^\\(ruby\\|gem\\)-//" -e "s/-\\(doc\\|devel\\)$//" -e "s/rcairo/cairo/" <<< %name)
 
%__ruby                %_bindir/ruby
%__setup_rb            %_bindir/setup.rb
%bundle                %_bindir/bundle
 
%ruby_func()            %__ruby -rrubygems -rrbconfig -e %*
%ruby_rubyconf_func()  %__ruby -rrubygems -rrbconfig -e 'print RbConfig::CONFIG["%*"]'
 
%ruby_gemspec          %(%ruby_func 'print File.join( Gem.dir, "specifications", "%gemname-%version.gemspec" )')
%ruby_gemspecdir        %(%ruby_func 'print File.join( Gem.dir, "specifications" )')
%ruby_gemextdir        %(%ruby_func 'print File.join( Gem.dir, "extensions", RbConfig::CONFIG["sitearch"], RbConfig::CONFIG["ruby_version"], "%gemname-%version" )')
%ruby_gemlibdir        %(%ruby_func 'print File.join( Gem.dir, "gems", "%gemname-%version" )')
%ruby_gemdocdir        %(%ruby_func 'print File.join( Gem.dir, "doc", "%gemname-%version" )')
%ruby_gemincludedir    %(%ruby_func 'print File.join( RbConfig::CONFIG["includedir"], "%gemname" )')
%ruby_includedir        %(%ruby_rubyconf_func includedir)')
%ruby_gemsextdir        %(%ruby_func 'print File.join( Gem.dir, "extensions", RbConfig::CONFIG["sitearch"], RbConfig::CONFIG["ruby_version"])')
%ruby_gemslibdir        %(%ruby_func 'print File.join( Gem.dir, "gems" )')
%ruby_gemsdocdir        %(%ruby_func 'print File.join( Gem.dir, "doc" )')
%ruby_ridir            %(%ruby_rubyconf_func ridir)
 
%gem_build              %__setup_rb build
%gem_install            %__setup_rb install --install_prefix=%buildroot
%gem_test              %__setup_rb test
%gem_show              %__setup_rb show --install_prefix=%buildroot
 
%rails_build            %__setup_rb build
%rails_install          %__setup_rb install --install_prefix=%buildroot
%rails_test            %__setup_rb test
%rails_show            %__setup_rb show --install_prefix=%buildroot
# env required macros
%_ruby_gem_rplc_list    %nil
%gem_replace_version()  %global _ruby_gem_rplc_list %_ruby_gem_rplc_list:%*
</pre>
 
=== Расположение файлов ===
 
Файлы бисеров будут располагаться в соответствующих подпапках для бисеров папки ''/usr/lib/ruby/gems/2.5.0/'', где 2.5.0 версия руби без учета заплаточного числа.
 
=== Заметки по спецификации пакета ===
 
# также пакеты содержащие основной код бисера, должны иметь группу ''Development/Ruby''. А пакеты содержащие исполняемый код из того или иного бисера, должны иметь группу соответствующую назначению пакета;
# пакеты, не содержащие двоичного исполняемого кода модуля, должны содержать явное объявление архитектуры noarch.
 
==== Типовой .spec ====
 
Для исправления старых или сломанных пактов, а также создания новых можно использовать такой типовой файл спецификации:
 
<pre>
%define  pkgname gemmodule
Name:    ruby-%pkgname
Version:  0.0.0
Release:  alt1
Summary:  summary
Group:    Development/Ruby
License:  <LICENSE>
URL:      https://site.org/
# VCS:    https://github.com/user/gemmodule.git
Source:  %pkgname-%version.tar
 
BuildRequires(pre): rpm-build-ruby
BuildRequires: gem(another_gem)
 
%description
%summary
 
%package -n %pkgname
Summary: summary
Group: Development/Other
BuildArch: noarch
 
%description -n %pkgname
%summary
 
%package doc
Summary: Documentation for <package>
Group: Development/Documentation
BuildArch: noarch
 
%description doc
Documentation for <package>.
 
%prep
%setup -q -n %pkgname-%version
 
%build
%gem_build
 
%install
%gem_show
%gem_install
 
%check
%gem_test
 
%files
%rubygem_gemdir/*
%rubygem_extdir/*
%rubygem_specdir/*
 
%files -n %pkgname
%_bindir/*
 
%files doc
%ruby_ridir/*
 
%changelog
 
</pre>
 
=== Тестирование ===
 
Тестирование пакетов руби подсистемы проводится с помощью пакета '''gem-setup''', который определяет и запускает необходимые для проверки руби пакета тесты, определенные в '''Gemspec'''/'''.gemspec'''. Для включения тестирования пакета необходимо включать в '''.spec''' пакета явные требования к зависимым пакетам на этапе сборки, напр.:
 
<pre>BuildRequires: gem(another_gem) >= 1.0 </pre>
 
=== Правила перехода на схему сию ===
 
# Все зависимости '''Requires''' для всех подпакетов определяются автоматически, посему все такие зависимости должны быть отвержены;
# Зависимости '''BuildRequires''' для пакета в случае, если проводится тестиование (раздел ''%check'') или если это является крайне необходимым, должны быть прописаны явно, получены они могут быть с помощью приложения '''setup.rb''';
# Также в случае, если пакет был переименован, требуется явным образом прописать в '''Provides''' старое имя пакета;
# В разделе ''%prep'' отвергаются все макросы указывающие на ''ruby'', в частности '''%update_setup_rb''';
# В разделе ''%build'' все макросы указывающие на ''ruby'', например '''%ruby_build''', '''%ruby_config''' заменяются на '''%gem_build''';
# В разделе ''%install'' все макросы указывающие на ''ruby'' заменяются на '''%gem_install''';
# В разделе ''%check'' все макросы указывающие на ''ruby'' заменяются на '''%gem_test''', хотя при крайней и неотложной необходимсти этот макрос может быть засерен;
# В разделе ''%files'' для корневого пакета заменяются следующие макросы '''%[ruby]gem_specdir''' на '''%ruby_gemspecdir''', '''%ruby_sitelibdir''' на '''%ruby_gemlibdir''', '''%ruby_sitearchdir''' на '''%ruby_gemextdir''';
# В разделе ''%files'' для пакета документации '''%ruby_ri_sitedir''' макрос заменяется на '''%ruby_ridir'''.
 
 
=== Автоматизация ===
 
Для автоматизации надзора и обновления рубишных пакетов используется бисер '''gem-rubobot''', который будет выполнять следующие функции:
 
* проводить надзор за новыми версиями пакетов и уведомлять об их появлении на '''rubygems.org''';
* проводить надзор за новыми версиями руби и уведомлять об их появлении;
* осуществлять контроль целостности подсистемы бисеров с помощью специально созданного '''Gemfile''';
* при достижении числа скачиваний бисера более определенного значения (по умолчанию 1 миллион), в автоматизированном пытаться опакечивать такой бисер, и уведомлять в случае как успеха, так и обратном случае;
* в автоматическом режиме пытаться обновить бисеры с учётом сохранения целостности бисерной подсистемы и уведомлять в случае невозможности обновить бисерный пакет;
* в автоматическом режиме пытаться обновить пакеты с приложениями на руби, который здесь также подразумевает попытку собирать приложение с заменой версии с помощью макроса, и уведомлять при невозможности собрать пакеты с приведенными правками;
* в полуавтоматическом режиме (то есть по прямой указивке оператора) собирать приложения руби с автоматическим созданием пакетов совместимости.
* в полуавтоматическом режиме (то есть по прямой указивке оператора) собирать руби с автоматическою пересборкою необходимых пакетов по зависимостям.
 
[[Категория:Ruby]]

Latest revision as of 11:47, 18 March 2019

Redirect to: