Ruby Policy 2.0

Материал из ALT Linux Wiki
(перенаправлено с «Ruby»)
Stub.png
Черновик политики Sisyphus
Автор(ы) — Малъ Скрылевъ
Обсуждение в devel@
Обсуждается с 05.02.2019


Ruby Policy

Правила упаковки модулей и программ на языке Ruby

Введение

Руби и модульность

Для простоты употребления ссылок, они будут действительны для 2.6.1.

Язык разработки руби обладает модульностю начиная с зари его изобретения в 1993-м, так если все основные функции, описанные в ядре руби, находятся в libruby, и являются его неотъемлемой частью, то стандартная библиотека как раз модуляризирована, и ее модули расположены в папке /usr/lib/ruby/, в которой уже расположены руби интерпретируемые модули, в подпапках с соответствующим названием, напр. модуль parser находится в модуле racc:

   /usr/lib/ruby/racc/parser.rb

В подпапке с названием <архитектура>-<ось>, находятся компилируемые части соответственных модулей, модуль 'cparse находится в модуле racc:

   /usr/lib/ruby/x86_64-linux/racc/cparse.so

Если какая либо стороннаяя библиотека (модуль) должна была быть использована в рядовых программах руби, то ее библиотеки должны были быть положены в соотвестствующие папки в /usr/lib/ruby/site_ruby/, которая является местом размещения библиотек руби не из числа стандартного набора.

В нынешней ситуации действительной на 2019 год, для развертывания дополнительных модулей используются модули rubygems и bundler, ныне поставляющиеся в виде пакета вида с англоязычным назанием gem (русское название бисер будет употребляться далее), хотя первый был когда-то обычным сторонним модулем. Проект rubygems был начат в апреле 2009, bundler четырьмя месяцами позднее в августе того же года. На текущий момент пара этих бисеров является по праву и на деле единственной системой определения зависимостей рубишной системы.

Зависимости в текущих проектах руби бывают 2-х видов:

  • через <gem>.gemspec;
  • через Gemfile.

.gemspec является единственным файлом описания зависимостей для системы rubygems бисеров, зависимости в котором могут быть скучены в 2 группы, зависимости исполения (runtime), и зависимости разработки (development), и последние также используются для тестирования проектов.

Gemfile является файлом описания как зависимостей разработки для бисеров (в дополнение к .gemspec), а также зависимостей различного рода для иных проектов, напр. для окружений разработки, тестирования, боевого, и прочих, не являющихся собственно бисерами. В современных проектах для рельс (rails), или других напр. синатры (sinatra), файла требуемые для определенного окружения описываются практически полностью только файлом Gemfile, то есть bundler сам решает в зависимости от писанного в том файле какие бисеры и их модули загружать для текущего окружения. Что во многих проектах приводит к тому, что явные вызовы метода require опускаются, например в проектах chef, puppet, или foreman современных версий. Типичный Gemfile можно найти тут.

Зависимости на версию ruby также определяется на основе Gemfile и .gemspec.

Версионирование

В руби и подсистеме бисеров для изменения правок бисеров применяется так называемый подход семантического версионирования, при котором первое число отражает "великое" изменение в библиотеке, когда меняется скажем API, второе число - "малое" изменение, при добавлении функциональности без ломки обратной совместимости, а третье - заплаточное изменение (patch number).


Сравнение с предыдущей политикою

Собственно подход с поиском зависимостей сугубо по вызовам метода require к текущему моменту существенно ограничивается следующими моментами:

  • вызовы этого метода не всегда являются определяющими для точного обнаружения и составления списка зависимостей, для бисеров, а в большей мере для проектов на основе Gemfile, поскольку в коде могут быть фиктивные вызовы обрамленные некоторым кодом, который делает этот вызов не обязательным, но возможным для работы бисера;
  • для Gemfile проектов потому, что вызовы require являются неявными и выполняются bundler-ом опосредованно на основе анализа Gemfile и .gemspec если таковой присутствует. также bundler следит за тем, чтобы среда и окружение разработки таких проектов были целостными, это приводит к том, что вызов require к внешнему модулю при необъявленная зависимость на него в Gemfile, приведет к критическому затруднения разработки проекта;
  • имя внешнего модуля не всегда может быть извлечено из анализа вызова require, из-за нестанрдартного объявления имен модулей в таких библиотеках, напр. бисер имеет имя 'foo-bar', модули в нем могу быть вида: FooBar, FooBar, или Foo/Bar, с файловыми соответствиями foo_bar, foo/bar, последний вызовет ложное срабатывание на модуль с именем foo;
  • из-за неявности загрузки как внешних модулей проекта, поскольку вызовы require к внешним библиотекам делает сам bundler;
  • методика определения зависимостей из require не способна проконтролировать версии API зависимого пакета при смене оного, особенно в случае, когда под зависимостям на деле пакету требуется старое, и необновленное API.

Посему в нынешнюю пору намного надежнее использовать анализ пары Gemfile/.gemspec файлов для определения зависимостей.

Пакет ruby

В системе может быть только единственная библиотека libruby, с единственным набором модулей stdlibs.

При сборке новой версии libruby должны быть пересобраны все модули stdlibs, также все бисеры, которые содержат подгружаемые .so библиотеки, и все модули, для которых в файлах Gemfile/.gemspec прописано явное требование к версии руби, которое удовлетворяет новая версия libruby.

Внешний вид пакета

  1. пакеты собранные на основе бисера имеют префикс gem-;
  2. исходные пакеты с приложениями на рельсах, синатре или подобных средах могут быть без префикса, при конфликтах возможен префикс rubyapp-;
  3. модули stdlibs, а также иные проекты, не попадающие в предыдущие два пункта, и библиотеки которых кладутся в папку site_ruby, должны иметь префикс ruby-;
  4. символы подчёркивания "_" заменяются в имени пакета на символы дефиса "-".

Зависимости

Зависимости теперь отслеживаются на основе анализа пары Gemfile/.gemspec.

Экспорт зависимостей на бисеры имеет вид gem(gemname) = version и rubygem(gemname) = version, для совместимости с пакетами rpm от redhat.

Конфликт зависимостей

В случае конфликта зависимостей применяются один из двух способов их разрешения:

  • в случае сохранения работоспособности бисера следует применять схему с явным объявлением макроса подмены версии требуемой библиотеки, имеющим имя gem_replace_version, напр. с ~> 1.1.7 на ~> 1.7:
%gem_replace_version '~> 1.1.7' '~> 1.7'
  • поскольку bundler позволяет хранить в одном и том же дереве бисеров несколько версий для каждого из них, а система альта такого не позволяет делать, требуется создание пакета совместимости версии, который будет содержать заполненную дефисами версию бисера без заплаточного числа (patch number), в дополнение к его имени, напр. такой бисер compat-gem будет иметь имя пакета: gem-compat-gem-1-2-1.2.0.src.rpm;
  • зависимость бисеров, в которых явным образом проводится сборка библиотек .so из исходников, автоматически проставляется зависимость на текущую версию руби, чтобы при пересборке самого руби с новой версиею, при ее изменении принудительно приходилось пересобирать и такие пакеты.

Макросы RPM

Макросы, требуемые для опакечивания бисеров и приложений руби, определены в rpm-, который советуем объявить в спецификации пакета в сборочном требовании в пометой (pre), так BuildRequires(pre): rpm-build-ruby:

Сборочные макросы таковы:

%__ruby                 %_bindir/ruby
%__setup_rb             %_bindir/setup.rb

%ruby_func()            %__ruby -rrubygems -rrbconfig -e %*
%ruby_rubyconf_func()   %__ruby -rrubygems -rrbconfig -e 'print RbConfig::CONFIG["%*"]'

%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"] )')
%ruby_gemlibdir         %(%ruby_func 'print File.join( Gem.dir, "gems" )')
%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


Расположение файлов

Файлы бисеров будут располагаться в соответствующих подпапках для бисеров папки /usr/lib/ruby/gems/2.5.0/, где 2.5.0 версия руби без учета заплаточного числа.

Заметки по спецификации пакета

  1. также пакеты содержащие основной код бисера, должны иметь группу Development/Ruby. А пакеты содержащие исполняемый код из того или иного бисера, должны иметь группу соответствующую назначению пакета;
  2. пакеты, не содержащие двоичного исполняемого кода модуля, должны содержать явное объявление архитектуры noarch.

Типовой .spec

Для исправления старых или сломанных пактов, а также создания новых можно использовать такой типовой файл спецификации:

%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

Тестирование

Тестирование пакетов руби подсистемы проводится с помощью пакета gem-setup, который определяет и запускает необходимые для проверки руби пакета тесты, определенные в Gemspec/.gemspec. Для включения тестирования пакета необходимо включать в .spec пакета явные требования к зависимым пакетам на этапе сборки, напр.:

BuildRequires: gem(another_gem) >= 1.0 

Правила перехода на схему сию

  1. Все зависимости Requires для всех подпакетов определяются автоматически, посему все такие зависимости должны быть отвержены;
  2. Зависимости BuildRequires для пакета в случае, если проводится тестиование (раздел %check) или если это является крайне необходимым, должны быть прописаны явно, получены они могут быть с помощью приложения setup.rb;
  3. Также в случае, если пакет был переименован, требуется явным образом прописать в Provides старое имя пакета;
  4. В разделе %prep отвергаются все макросы указывающие на ruby, в частности %update_setup_rb;
  5. В разделе %build все макросы указывающие на ruby, например %ruby_build, %ruby_config заменяются на %gem_build;
  6. В разделе %install все макросы указывающие на ruby заменяются на %gem_install;
  7. В разделе %check все макросы указывающие на ruby заменяются на %gem_test, хотя при крайней и неотложной необходимсти этот макрос может быть засерен;
  8. В разделе %files для корневого пакета заменяются следующие макросы %[ruby]gem_specdir на %ruby_gemspecdir, %ruby_sitelibdir на %ruby_gemlibdir, %ruby_sitearchdir на %ruby_gemextdir;
  9. В разделе %files для пакета документации %ruby_ri_sitedir макрос заменяется на %ruby_ridir.


Автоматизация

Для автоматизации надзора и обновления рубишных пакетов используется бисер gem-rubobot, который будет выполнять следующие функции:

  • проводить надзор за новыми версиями пакетов и уведомлять об их появлении на rubygems.org;
  • проводить надзор за новыми версиями руби и уведомлять об их появлении;
  • осуществлять контроль целостности подсистемы бисеров с помощью специально созданного Gemfile;
  • при достижении числа скачиваний бисера более определенного значения (по умолчанию 1 миллион), в автоматизированном пытаться опакечивать такой бисер, и уведомлять в случае как успеха, так и обратном случае;
  • в автоматическом режиме пытаться обновить бисеры с учётом сохранения целостности бисерной подсистемы и уведомлять в случае невозможности обновить бисерный пакет;
  • в автоматическом режиме пытаться обновить пакеты с приложениями на руби, который здесь также подразумевает попытку собирать приложение с заменой версии с помощью макроса, и уведомлять при невозможности собрать пакеты с приведенными правками;
  • в полуавтоматическом режиме (то есть по прямой указивке оператора) собирать приложения руби с автоматическим созданием пакетов совместимости.
  • в полуавтоматическом режиме (то есть по прямой указивке оператора) собирать руби с автоматическою пересборкою необходимых пакетов по зависимостям.