diff --git a/CHANGELOG.md b/CHANGELOG.md index ea27ee7..b7d72a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Unreleased] -*no unreleased changes* +### Added +* Capistrano: install rbenv and ruby from /opt/rbenv.tar.gz or vendor/rbenv/ ## 7.2.6 / 2024-11-13 ### Fixed diff --git a/lib/ndr_dev_support/capistrano/assets.rb b/lib/ndr_dev_support/capistrano/assets.rb index 3acfab4..b00e0c0 100644 --- a/lib/ndr_dev_support/capistrano/assets.rb +++ b/lib/ndr_dev_support/capistrano/assets.rb @@ -4,8 +4,8 @@ task :configure_assets do asset_script = fetch(:asset_script, <<~SHELL) set -e - ruby -ryaml -e "puts YAML.dump('production' => { 'secret_key_base' => 'compile_me' })" > config/secrets.yml - ruby -ryaml -e "puts YAML.dump('production' => { 'adapter' => 'placeholder' })" > config/database.yml + ruby -e "require 'yaml'; puts YAML.dump('production' => { 'secret_key_base' => 'compile_me' })" > config/secrets.yml + ruby -e "require 'yaml'; puts YAML.dump('production' => { 'adapter' => 'placeholder' })" > config/database.yml RAILS_ENV=production bundle exec rake assets:clobber assets:precompile rm config/secrets.yml config/database.yml SHELL diff --git a/lib/ndr_dev_support/capistrano/install_ruby.rb b/lib/ndr_dev_support/capistrano/install_ruby.rb new file mode 100644 index 0000000..87ab17b --- /dev/null +++ b/lib/ndr_dev_support/capistrano/install_ruby.rb @@ -0,0 +1,103 @@ +Capistrano::Configuration.instance(:must_exist).load do + namespace :ndr_dev_support do + desc <<~DESC + Ensure that the required ruby version is installed. + + This can be installed from /opt/rbenv.tar.gz (first installation only) or vendor/rbenv/ + + To place an offline copy of rbenv in /opt/rbenv.tar.gz + For ruby 3.1.6, run the following commands: + $ mkdir clone_rbenv + $ git clone https://github.com/rbenv/rbenv.git clone_rbenv/.rbenv + $ git clone https://github.com/rbenv/ruby-build.git clone_rbenv/.rbenv/plugins/ruby-build + $ mkdir clone_rbenv/.rbenv/cache + $ (cd clone_rbenv/.rbenv/cache; curl -O https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.6.tar.gz) + $ (cd clone_rbenv; rm -f ../rbenv.tar.gz; tar czf ../rbenv.tar.gz .rbenv) + $ rm -rf clone_rbenv + $ scp -p rbenv.tar.gz app-server:/opt/rbenv.tar.gz + + To add rbenv, ruby-build and additional ruby versions to the application vendor directory + For ruby 3.2.6: + $ mkdir clone_rbenv + $ git clone https://github.com/rbenv/rbenv.git clone_rbenv/.rbenv + $ mkdir -p vendor/rbenv; rm -f vendor/rbenv/rbenv.tar.gz + $ tar czf vendor/rbenv/rbenv.tar.gz -C clone_rbenv .rbenv + $ rm -rf clone_rbenv + $ mkdir clone_ruby-build + $ git clone https://github.com/rbenv/ruby-build.git clone_ruby-build/ruby-build + $ mkdir -p vendor/rbenv/cache; rm -f vendor/rbenv/ruby-build.tar.gz + $ tar czf vendor/rbenv/ruby-build.tar.gz -C clone_ruby-build ruby-build + $ rm -rf clone_ruby-build + $ (cd vendor/rbenv/cache; curl -O https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.6.tar.gz) + $ git add vendor/rbenv/{rbenv,ruby-build}.tar.gz vendor/rbenv/cache/* + DESC + task :install_ruby do + version = fetch(:ruby) + # Note that ruby 3.1.x on CentOS 7 generally installs successfully but then reports an error: + # ERROR: While executing gem ... (URI::InvalidURIError) + # bad URI(is not URI?): "bundler\r" + # For this reason, we ignore the exit status, and run our own test. + # + # For some reason, SSH keepalive options have no effect with capistrano 2 and net-ssh 7. + # So we run a poor-man's keepalive, because this installation can take over 10 minutes + # and some of our SSH servers disconnect inactive sessions after 5 minutes. + # We have a keepalive time limit of 40 minutes in case the installation fails unexpectedly. + # + # We remove ~/.rbenv paths from capistrano-defined PATH when running `rbenv init` + # so that it knows the path is needed in ~/.bash_profile + # + # We use latest_release: this is broadly well-defined, and will point to the in-progress + # release if we're part way through a deployment, or the most recent release if run + # after a deployment has happened, or be blank if attempted after cap deploy:setup + run <<~SHELL + set -e; + if ! rbenv versions --bare 2> /dev/null | grep -q ^#{Regexp.escape(version)}$; then + echo Installing ruby #{version}; + { sleep 10; for i in `seq 1 80`; do echo -n '.'; sleep 30; done & } 2> /dev/null; + sudo -i -n -u #{fetch(:application_user)} sh -c " + if [ ! -e .rbenv ] && [ -e /opt/rbenv.tar.gz ]; then + tar xf /opt/rbenv.tar.gz .rbenv; + PATH=\\`echo \\"\\$PATH\\"|sed -e \\"s_[^:=]*/[.]rbenv[^:]*:__g\\"\\` .rbenv/bin/rbenv init bash; + fi; + if [ ! -e .rbenv ] && [ -f #{latest_release}/vendor/rbenv/rbenv.tar.gz ]; then + tar xf #{latest_release}/vendor/rbenv/rbenv.tar.gz .rbenv; + PATH=\\`echo \\"\\$PATH\\"|sed -e \\"s_[^:=]*/[.]rbenv[^:]*:__g\\"\\` .rbenv/bin/rbenv init bash; + fi; + if [ ! -e .rbenv ]; then + echo rbenv not installed: aborting; + else + if [ -d #{latest_release}/vendor/rbenv/cache ] && [ -n \\"\\`ls #{latest_release}/vendor/rbenv/cache\\`\\" ]; then + mkdir -p .rbenv/cache/; + cp -nvp #{latest_release}/vendor/rbenv/cache/* .rbenv/cache/; + fi; + if [ -f #{latest_release}/vendor/rbenv/ruby-build.tar.gz ]; then + mkdir -p .rbenv/plugins/; + tar xf #{latest_release}/vendor/rbenv/ruby-build.tar.gz -C .rbenv/plugins/ ruby-build; + fi; + eval \\"\\$(.rbenv/bin/rbenv init - --no-rehash bash)\\"; + export TMPDIR=\\`mktemp -d \\"\\$HOME\\"/rbenv_tmp_XXXX\\`; + if rbenv install #{version} --skip-existing 2>&1; then + RBENV_VERSION=#{version} ruby --version; + rm -rf \\"\\`printenv TMPDIR\\`\\"; + rbenv global #{version}; + fi; + fi; + "; + { kill % && wait; } 2> /dev/null; + set -e; + if [ "`RBENV_VERSION=#{version} gem list --exact --installed bundler`" == "true" ]; then + echo 'Please ignore the following error above:'; + echo '> ERROR: While executing gem ... (URI::InvalidURIError)'; + echo '> bad URI(is not URI?): "bundler\\r"'; + echo Successfully installed ruby #{version}; + else + echo ERROR: Failure installing ruby #{version}: aborting; + exit 1; + fi; + fi + SHELL + end + end + + before 'bundle:install', 'ndr_dev_support:install_ruby' +end diff --git a/lib/ndr_dev_support/capistrano/ndr_model.rb b/lib/ndr_dev_support/capistrano/ndr_model.rb index 287b4fa..f0917e0 100644 --- a/lib/ndr_dev_support/capistrano/ndr_model.rb +++ b/lib/ndr_dev_support/capistrano/ndr_model.rb @@ -2,6 +2,7 @@ # Discrete bits of functionality we use automatically: require_relative 'assets' +require_relative 'install_ruby' require_relative 'restart' require_relative 'revision_logger' require_relative 'ruby_version' @@ -172,7 +173,7 @@ end def release_config_for(env) - branches = YAML.load_file('config/deployments.yml') + branches = YAML.safe_load_file('config/deployments.yml', permitted_classes: [Date]) branches.fetch(env.to_s) { raise 'Unknown release branch!' } end diff --git a/ndr_dev_support.gemspec b/ndr_dev_support.gemspec index 7ff7327..74b9982 100644 --- a/ndr_dev_support.gemspec +++ b/ndr_dev_support.gemspec @@ -25,6 +25,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'pry' # Audit dependencies: + spec.add_dependency 'csv' spec.add_dependency 'highline', '>= 1.6.0' # Rubocop dependencies: