В статье «Работа с Capistrano: задачи, роли и переменные» мы исследовали фундаментальные концепции Capistrano. Мы написали две полезные задачи. Один для входа в сервер, а другой для доступа к консоли Rails в производственной системе. На этот раз мы собираемся написать более вовлеченный сценарий, состоящий из многих задач. Сами по себе эти задачи не будут выполнять слишком много. Но сумма из них поможет нам автоматизировать установку и модернизацию Ruby на протяжении всей жизни нашего приложения.
Не так давно на каждом сервере развертывания, с которым я работал, была установлена только одна версия Ruby. Эта общеобразовательная установка заставила приложения All Rails использовать очень специфическую версию Ruby, например, 2.1.5. Вы не только застряли с рубиновой версией, но и женаты на конкретном выпуске Rails, который имел минимальное требование Ruby. Не нужно много, чтобы понять недостатки этого подхода. Любое обновление Ruby требует новой настройки сервера производственного сервера и дорогостоящей миграции. У кого есть время и деньги на это? С точки зрения надежности Руби становится единственной точкой отказа. Если бы Руби каким -то образом повреждена, это повлияет на все веб -приложения на одном и том же сервере.
Способ облегчить эту боль, чтобы использовать менеджер версий Ruby. Инструмент, который позволит многим различным версиям Ruby сосуществовать на одном сервере. Далее, каждое приложение Rails может иметь свою собственную среду Ruby и быть обновленным независимо. Разработчики могут легко изменить Ruby в течение жизни своего применения, не влияя на другие проекты — хороший бонус.
Есть много замечательных инструментов, таких как RVM или Chruby Это может помочь вам управлять рубинами. Но rbenv
мой предпочтительный выбор. Он достигает идеального баланса между функциями и простотой. Проект также поддерживается членами команды Ruby Core, и это то, что я продемонстрирую в этой статье. В то время как мы исследуем RBENV, мы поднимаем завесу и изучим больше методов Capistrano, чтобы добавить к вашему набору инструментов.
В следующих образцах кода мы в некоторых местах установим файлы на сервере и прочитаем их обратно от других. Я предполагаю, что операционная система, такая как Linux, такая как Linux, так как я больше всего знаком. Возможно, вам придется отрегулировать несколько бит в соответствии с выбранной вашей операционной системой.
С этим давайте погрузимся в rbenv
установка.
Где установить rbenv?
Прежде чем мы решим, где установить RBENV, мы захотим сначала назвать и организовать нашу новую задачу. Отличная функция, которую Capistrano наследует от инструмента для сборки Rake, — это метод пространства имен. Это дает нам способ логически группировать задачи под одним именем. Побочным преимуществом является то, что мы можем повторно использовать подобные имена задач, которые в противном случае могли бы противоречить встроенным. Я склонен сгруппировать задачи на основе общей функции, которая связывает их вместе. Rbenv как пространство имен, кажется, хорошо подходит. Мы пойдем вперед и внутри lib/capistrano/tasks каталог создать файл с именем rbenv.rake с : rbenv
Пространство имен:
# lib/capistrano/tasks/rbenv.rake namespace :rbenv do ... end
Чтобы установить RBENV, мы должны решить, где мы хотим, чтобы он был установлен в первую очередь. Есть два основных способа установить RBENV: по всей системе и за учетную запись пользователя . Какая разница? Установка всего системы означает, что данная версия Ruby устанавливается только один раз на сервер в центральном месте. Например, в системе UNIX, которая будет /usr/local/rbenv Анкет И наоборот, установка учетной записи пользователя ограничивает заданное Ruby в местоположениях, доступных для этого конкретного пользователя. Обычно это скрытая папка в домашнем каталоге пользователя, например, $ Home/.rbenv Анкет Выбор последнего может привести к тому, что на одном и том же сервере существуют подобные версии Ruby. Это уравновешивающий акт.
Как мы уже обсуждали, общесистемная установка страдает от одной проблемы с отказом. Из -за этого, по умолчанию мы собираемся установить RBENV для каждой учетной записи пользователя развертывания. Но мы разоблачим : rbenv_path
переменная, чтобы разрешить конфигурацию пользовательского пути. Для этого мы создадим вспомогательный метод rbenv_path По умолчанию это будет удерживать путь установки RBENV, устанавливаемый в домашний каталог пользователя:
def rbenv_path fetch(:rbenv_path, "$HOME/.rbenv") end
Fetch Метод принимает в качестве второго аргумента, значение по умолчанию, которое будет использоваться, когда в вашем скрипте не будет установлено значение. Есть еще один способ, которым мы могли бы сделать это, используя блок.
def rbenv_path fetch(:rbenv_path) { "$HOME/.rbenv" } end
Разница в том, что значение по умолчанию, предоставленное через блок, будет оценивается ленивым. Это означает, что оценка значения по умолчанию произойдет только тогда, когда rbenv_path
Метод сначала используется. Это удобно в ситуации, когда у вас есть, например, динамическое значение, которое зависит от оценки других методов. Мы собираемся придерживаться второй опции аргумента здесь.
Один из них: при работе с путями файловой системы лучше представлять их как типы данных, а не строки. Что это значит? Строка является плохим представлением для системного пути. Это не дает вам дополнительной информации, например, является ли путь относительным или абсолютным. Стандартная библиотека Руби включает ПАТЕНАМА Это может обернуть представление строки пути. В отличие от строки, объект Pathname может сообщить нам информацию о пути, который он представляет, и улучшить наши операции пути. Мы можем строить пути независимыми от системы. Присоединиться к двум путям так же просто, как и их вместе. Объект PathName автоматически использует правильный сепаратор пути файла. Если вы хотите узнать больше, я рекомендую Пути не строки статья.
Вооружены этим знанием, мы меняем rbenv_path
вместо этого вернуть объект пути:
# lib/capistrano/tasks/rbenv.rake require "pathname" namespace :rbenv do def rbenv_path Pathname.new fetch(:rbenv_path, "$HOME/.rbenv") end end
Теперь нам нужно найти способ сказать Capistrano использовать rbenv_path
Метод найти исполняемый файл RBENV и сделать его доступным для использования других задач.
Переменная пути
При развертывании приложений Capistrano использует так называемое не логин ракушка. Другими словами, Capistrano ничего не предполагает о базовой системе, кроме создания сеанса SSH. К этому сеансу нет визуального терминала. Ни один из файлов конфигурации оболочки, как файл профиля Bash, не загружается. Это очень желательное поведение, которое делает наши развертывания безопасными и повторяющимися. Но это также означает, что мы не можем полагаться на любые переменные среды конфигурации, которые будут предварительно загружены и доступны для наших сценариев.
В частности, мы не можем полагаться на систему, чтобы сообщить нашим сценариям местоположение бинарной установки RBENV. Как мы можем обойти это? Нам нужно свернуть наши рукава и показать Capistrano, где Rbenv устанавливается сами. То, как мы можем сделать это, — это изменить $ Path переменная среды. Эта переменная содержит список каталогов, разграниченных толстой кишкой, которую оболочка Unix использует для поиска исполняемых файлов при запуске команд. Чтобы проверить значение вашего пути, вы можете запустить следующее в своей оболочке:
$ echo $PATH
Ваш путь может выглядеть примерно так:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Возникает вопрос … как мы можем дать указание Capistrano изменить, на какие места указывает переменная $ Path? Помните, что мы пытаемся сделать RBENV доступным для не логиновой оболочки.
Картирование двоичных файлов с Sshkit
Сердце Капистрано — Sshkit Ruby Gem — это инструментарий для работы с серверами через SSH. SSHKIT отвечает за тяжелую работу с точки зрения определения и выполнения команд на сервере. Кроме того, он обеспечивает доступ к текущей конфигурации сеанса SSH с Sshkit.config
метод В частности, конфигурация
разоблачает default_env
Метод, который по умолчанию возвращает пустой хэш. Когда команда выполняется, все ключи и их значения преобразуются в соответствующие переменные среды. Имейте в виду, что только ключи, предоставленные в качестве символов, изменятся на название прописного:
SSHKit.config.default_env[:path] = "$HOME/.rbenv/bin:$PATH"
Это равно:
SSHKit.config.default_env["PATH"] = "$HOME/.rbenv/bin:$PATH"
Оба примера приведут к тому, что команда будет префикс по пути:
$ PATH="$HOME/.rbenv/bin:$PATH"
default_env
именно место для изменения $ Path
переменная, чтобы включить местоположение пути установки RBENV. В частности, мы хотим добавить два каталога с исполняемыми файлами:
$ Home/.rbenv/bin
$ Home/.rbenv/shims
Теперь мы готовы испачкать руки и заставить Capistrano найти Rbenv. Начнем с добавления задания под названием map_bins Это заставит обыкновенные рубиновые двоичные файлы использовать исполняемый файл RBENV — но мы опередили себя. Давайте сначала изменим переменную $ path.
# lib/capistrano/tasks/rbenv.rake require "pathname" namespace :rbenv do desc "Map Ruby binaries to use rbenv" task :map_bins do ... end def rbenv_path Pathname.new fetch(:rbenv_path, "$HOME/.rbenv") end end
С определенной задачей мы читаем текущее значение для : Путь
Ключ для сохранения того, что могли бы уже настроить другие задачи:
desc "Map Ruby binaries to use rbenv" task :map_bins do path = SSHKit.config.default_env[:path] end
Возможно, вы заметили, что мы не использовали на объем. Область необходима только тогда, когда мы хотим выполнить удаленные команды на сервере или получить доступ к конфигурации сервера. Ничего из этого не необходимо для поставленной задачи.
Далее мы изменяем путь с помощью расположения каталогов RBENV. Мы используем rbenv_path
Помощник, чтобы добавить наши два места RBENV. Если путь не настроен, мы подготовим два каталога непосредственно к переменной пути $. Если путь настроен, мы готовимся к нему:
desc "Map Ruby binaries to use rbenv" task :map_bins do path = SSHKit.config.default_env[:path] path_env = "#{rbenv_path.join('shims'}:#{rbenv_path.join('bin')}:" + (path.nil? ? "$PATH" : path) end
Наконец, мы переопределяем sshkit default_env
Хэш с новым модифицированным пути среды:
desc "Map Ruby binaries to use rbenv" task :map_bins do path = SSHKit.config.default_env[:path] path_env = "#{rbenv_path.join('shims'}:#{rbenv_path.join('bin')}:" + (path.nil? ? "$PATH" : path) SSHKit.config.default_env.merge!(path: path_env) end
Но мы еще не закончили. Если мы собираемся сделать что -нибудь полезное сервер, такой как установка рельсов, нам нужен способ запустить такие команды Ruby, как драгоценный камень
или пучок
. Это необходимо, чтобы позже мы могли выпустить следующие команды для начальной загрузки Rails.
$ gem install bundler $ bundle install
Допустим, мы хотим запустить "Установка пакета"
Команда, но уже установлено много версий Ruby. Какая версия Ruby предоставит пакет
исполняемый файл? Нам нужно как -то сказать нашей оболочке, какую версию Ruby использовать. Снова $ Path
Переменная приходит на помощь. Мы можем расширить $ Path
С правильной версией Ruby перед запуском нашего исполняемого файла. Например, для использования исполняемого пакета из установки Ruby 2.6.3 мы запустили бы следующее в оболочке UNIX:
$ PATH="$HOME/.rbenv/versions/2.6.3/bin:$PATH" bundle install
Но эта команда длинная, некрасивая и трудно автоматизировать. К счастью, RBENV предоставляет rbenv Exec Команда, которая может обнаружить в настоящее время загруженную версию Ruby и включить ее в наш путь для нас. Поскольку мы уже настроили Capistrano, чтобы найти Binary Rbenv, мы можем использовать exec
Чтобы переписать нашу предыдущую команду так:
$ rbenv exec bundle install
Теперь мы знаем, как написать наши команды вручную. Но как мы можем сказать Capistrano, чтобы он подготовил обыкновенные исполнители Ruby, такие как пакет
с rbenv Exec
командовать нам? Прежде чем мы ответим на этот вопрос, полезно понять, как выполнить Метод работает первым.
Запуск удаленных команд
Sshkit
предоставляет выполнить Метод запуска любой команды оболочки на сервере. Это мощный метод, который ведет себя по -разному в зависимости от передаваемых аргументов. Взгляд под обложками раскрывает довольно краткую и короткую реализацию:
# lib/sshkit/backends/abstract.rb def execute(*args) options = args.extract_options! create_command_and_execute(args, options).success? end
Приведенный выше фрагмент не выглядит так сильно, но за выполнять
метод Концептуально, процесс запуска любой команды разделен на две части. Первая часть берет команду и преобразует ее в Sshkit:: command
объект. Этот объект обрабатывает сборку полной, готовой к оболочке из различных битов, таких как префиксы, флаги и аргументы. Вторая часть заботится о выполнении команды, которая может быть увлечена любым из Sshkit:: Backends
Анкет Этот подход позволяет создавать команду один раз и запускать ее, используя различные бэкэнды на многих серверах. Это такое отличное дизайнерское решение!
При запуске с «простой» строкой без каких -либо пробелов, сначала выполните команду по команде Sshkit:: CommandMap
. Внутренне, CommandMap
Содержит хэш команд, пустые по умолчанию, которые допускают дополнительную конфигурацию:
# lib/sshkit/command_map.rb module SSHKit class CommandMap def initialize(value = nil) @map = CommandHash.new(value || defaults) end end end
По умолчанию, если не указаны пользовательские префиксы, команда префикс префикс «/usr/bin/env» Анкет За исключением Если
, тест
, время
и exec
, команды, которые не получают префиксов. Например, если бы мы проверили версию Ruby, используя Ruby Executiveabquebable в качестве первого аргумента:
execute :ruby, "--version"
Это приведет к поиску по умолчанию и следующему выводу:
/usr/bin/env ruby --version
Если, с другой стороны, мы должны были предоставить команду в качестве необработанной строки с пробелом:
execute "ruby --version"
Это будет выполнено как есть без какого-либо префикса, сбегания оболочки или изменений каталога. В основном нет участия от Капистрано или Sshkit
Как бы то ни было:
ruby --version
Это часто обманывает новичков Капистрано, которые в загадке царапают голову относительно того, почему команда не работает на сервере. Так что считайте себя предупрежденным.
Команды префикса
Чтобы изменить команды, прежде чем они будут запущены, мы можем использовать command_map Метод, выявленный Sshkit
объект конфигурации. Затем мы можем взять CommandMap
и подключить его вместе с Префикс Метод для добавления новых префиксов. Как CommandMap
, возвращенные PrefixProvider
хранит хэш команды для сопоставления префикса. По умолчанию любой поиск ключей для несуществующей команды возвращает пустой массив. Зная это, мы можем переопределить префикс команды Ruby по умолчанию:
SSHKit.config.command_map.prefix[:ruby] << "rbenv exec"
Затем снова запустите нашу команду Ruby:
execute :ruby, "--version"
Но на этот раз мы получаем совершенно другой результат:
rbenv exec ruby --version
Это было долгое, но необходимое отвлечение от map_bins
Реализация задачи. Теперь мы знаем, как префикс общих исполняемых файлов, таких как пучок
, драгоценный камень
, или Рейбл
с rbenv Exec
Анкет Сначала мы добавим rbenv_map_bins
Метод, позволяющий конфигурации рубиновых двоичных дефолтов, не выполняя их наиболее распространенной:
def rbenv_map_bins fetch(:rbenv_map_bins, %w[bundle gem rails ruby rake]) end
Вооружившись нашими знаниями об настройке команд, мы можем префикс каждого двоичного файла и убедиться, что мы не меняем существующих префиксов:
rbenv_map_bins.each do |exe| SSHKit.config.command_map.prefix[exe.to_sym].unshift("rbenv exec") end
Имея префикс -бинарны Ruby, полная задача для сопоставления команд будет выглядеть следующим образом:
desc "Map Ruby binaries to use rbenv" task :map_bins do path = SSHKit.config.default_env[:path] path_env = "#{rbenv_path.join('shims')}:#{rbenv_path.join('bin')}:" + (path.nil? ? "$PATH" : path) SSHKit.config.default_env.merge!(path: path_env) rbenv_map_bins.each do |exe| SSHKit.config.command_map.prefix[exe.to_sym].unshift("rbenv exec") end end
Теперь бег "Установка пакета"
(и аналогичные) команды будут работать, потому что они будут выполнены с правильной версией Ruby. Мы можем применить нашу задачу, запустив:
$ cap [stage] rbenv:map_bins
То, что мы настроили до сих пор, это не логиновая оболочка. Но если пользователь хочет войти в сервер, исполняемый файл RBENV не будет доступен, и ни одно из двоичных файлов Ruby не будет работать. Давайте изменим это!
Изменение файла запуска оболочки
Было бы раздражать заставлять любого, кто входит в сервер для вручную запустить инструкции по настройке RBENV, прежде чем они смогут запустить, например, команду пакета.
Так в чем решение?
Когда пользователь входит в операционную систему, это запускает загрузку файлов запуска оболочки. Файл запуска оболочки позволяет выполнять произвольные сценарии оболочки, устанавливать переменные среды и так далее. Похоже, что нам нужно. Мы могли бы изменить файл запуска оболочки, чтобы установить для нас пути RBENV. Это сделало бы Ruby и любые связанные исполнители доступными внутри регистрированной пользовательской оболочки.
Многие распределения Linux, такие как Debian, обычно используют оболочку Bash в качестве оболочки по умолчанию. Когда начинается как интерактивная оболочка, Bash читает файл запуска под названием bash_profile
. Следующий скрипт Bash — это все, что нам нужно вставить в файл запуска Bash для автоматической загрузки RBENV:
if [ -d ~/.rbenv ]; then export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" fi
Этот фрагмент кода впервые проверяет, что существует каталог RBENV. Когда это произойдет, он добавляет бинар Rbenv к $ Path
переменная и делает его доступным в оболочке. Затем он запускает сценарий инициализации RBENV.
Теперь у нас есть все, что нам нужно, чтобы создать задачу под названием modify_shell_file
Это, как вы, вероятно, догадались, изменит файл запуска оболочки. Мы используем на Область с все Роль, чтобы указать серверы, затронутые нашими изменениями:
# lib/capistrano/tasks/rbenv.rake namespace :rbenv do desc "Map Ruby binaries to use rbenv" task :map_bins do ... end desc "Change shell startup file to setup rbenv" task :modify_shell_file do on release_roles(:all) do end end end
Тестирование удаленных команд
Что мы не хотим делать, так это добавлять скрипт инициализации RBENV в файл запуска более одного раза. Если сценарий уже присутствует, мы хотим пропустить его добавление. Мы могли бы использовать выполнить
Метод для запуска этого чека, но это не заставит нас далеко. Это почему? выполнить
Метод, хотя и мощный, используется для выполнения команд, где нам не волнует захват окончательного вывода. Мы заботимся только о том, что команда преуспевает, и сценарий продолжается или не удается и завершает задачу. Но в ситуации, когда нам нужно решить, успешно ли команда успешно или потерпела неудачу, не останавливая задачу, Capistrano предоставляет тест метод Этот метод равен выполнить
С той разницей, что это не вызывает ошибки для не нулевого кода выхода. Есть даже : rave_on_non_zero_exit
Опция, которую вы можете использовать, чтобы получить это поведение с помощью метода выполнения:
execute ..., raise_on_non_zero_exit: false
Чтобы сканировать файл для сопоставления контента, мы будем использовать Греп командование Это базовая утилита UNIX, которая уже должна быть установлена на сервере. Если совпадение найдено, Греп
закончит с нулевым кодом выхода — именно то, что нам нужно. Чтобы пропустить задачу и предотвратить дальнейшее выполнение, мы называем Следующий Анкет Если эта задача является частью многих, сценарий продолжится со следующей задачей. Это обычная идиома, используемая в плагинах Capistrano. Стоит отметить, что перерыв
или Выход
немедленно прекратит весь сценарий и вынесет ошибку.
Так Проверка, чтобы увидеть, содержит ли сценарий инициализации RBENV RBENV.
desc "Change shell startup file to setup rbenv" task :modify_shell_file do on release_roles(:all) do next if test "grep -qs 'rbenv init' ~/.bash_profile" end end
Нам нужно прыгнуть через некоторые обручи в оболочках Unix, чтобы программно вставить многослойную строку. При выполнении многослойной команды Capistrano заменяет каждый символ Newline на полуколон, чтобы по существу развернуть команду в одну строку. Чтобы предотвратить это и вставить строку скрипта RBENV в линию в файл запуска Bash, мы собираемся использовать printf утилита. Если вы использовали Ruby’s printf Метод, Unix printf
Утилита будет очень знакомой. Они оба используют строку формата, за которой следует входные аргументы, разница в оболочке использует пробелы для разделения входов.
Давайте добавим скрипт инициализации RBENV в файл профиля Bash:
desc "Change shell startup file to setup rbenv" task :modify_shell_file do on release_roles(:all) do next if test "grep -qs 'rbenv init' ~/.bash_profile" execute %q{printf "%s\n %s\n %s\n%s" 'if [ -d ~/.rbenv ]; then' 'export PATH="$HOME/.rbenv/bin:$PATH"' 'eval "$(rbenv init -)"' 'fi' >> ~/.bash_profile} end end
Настроив RBENV как для входа в систему, так и для не login Shell, мы готовы установить сам RBENV. Это было довольно немного настройки, но я обещаю, что это того стоило.
Установка сборки RBENV & RBENV
На этом этапе мы не собираемся изобретать колесо и вместо этого использовать Rbenv Установитель Анкет Установщик RBENV включает в себя сценарий врача, который проверит, что RBENV правильно настроен. Если это не так, то сценарий установки не будет продолжаться дальше и вернуть код выхода из ненулевого выхода. Это остановит процесс установки в своих треках.
Преимущество использования установщика RBENV заключается в том, что он также установит RBENV-BUILD Инструмент для компиляции и установки Ruby. RBENV по умолчанию не включает в себя сценарии для установки Ruby, он ограничивает его функциональность только управлять версиями Ruby. Однако в нашем случае мы также хотим установить Ruby.
Давайте изменим rbenv.rake
Файл и добавьте новый Run_installer задача:
# lib/capistrano/tasks/rbenv.rake namespace :rbenv do desc "Map Ruby binaries to use rbenv" task :map_bins do ... end desc "Change shell startup file to setup rbenv" task :modify_shell_file do ... end desc "Install rbenv and rbenv-build tools" task :run_installer do end end
Подобно задаче для изменения файла запуска оболочки, мы хотим пропустить установку RBENV, если он уже настроен. Для этого мы собираемся использовать Bash Shell, чтобы проверить существование Diftory Rbenv:
desc "Install rbenv and rbenv-build tools" task :run_installer do on release_roles(:all) do next if test("[ -d #{rbenv_path} ]") end end
Мы начнем с сохранения расположения установщика RBENV в методе rbenv_installer_url
:
def rbenv_installer_url "https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-installer" end
Одно слово предостережения здесь. Перед использованием стороннего сценария лучше всего прочитать и проверить, что сценарий не содержит никакого вредоносного кода в первую очередь. Для дополнительной меры желательно хранить такой сценарий во внутренней сети и загрузить его только оттуда.
Затем установка RBENV является лишь вопросом загрузки сценария установки с помощью wget Утилита в тихом режиме. Как только сценарий установки загружен, мы собираемся поднять его контент в оболочку Bash для выполнения процесса установки:
desc "Install rbenv and rbenv-build tools" task :run_installer do on release_roles(:all) do next if test(" [ -d #{rbenv_path} ] ") execute :wget, "-q", rbenv_installer_url, "-O-", "|", :bash end end
И при этом мы успешно установили утилиту RBENV на нашем сервере. Таким образом, мы открыли двери для автоматизации установки Ruby.
Резюме
В этой статье мы написали несколько довольно сложных задач, которые дали нам гораздо больший вкус возможности Capistrano. Мы изменили переменные среды, обновили файл запуска оболочки и установили утилиту RBENV. Когда мы все это делали, мы исследовали больше концепций Shell Unix и узнали, как работает RBENV.
В качестве побочной выгоды эта статья продемонстрировала нам крайнюю гибкость, которую нам дает архитектура Capistrano. Это позволяет вам предоставить инфраструктуру приложения так, как вам нужно. Вы описываете свои требования, используя определения задач и, viola! Не уверен насчет вас, но я впечатлен дизайном Capistrano. Он охватывает динамическую природу и объектную модель Ruby, чтобы предоставить вам интуитивно понятный способ выражения концепций развертывания.
Ницца. Мы еще один уровень на нашем пути к мастерству Капистрано. В следующей статье мы продолжим изучать, как объединить все задачи, чтобы установить совершенно новый Ruby.
Эта статья была первоначально опубликована на PiotrMurach.com .
Фотография Тинчо Франко на Unsplash
Оригинал: «https://dev.to/piotrmurach/working-with-capistrano-environment-variables-and-remote-commands-44ob»