diff -Nru ruby-foreman-0.85.0/data/export/systemd/master.target.erb ruby-foreman-0.87.2/data/export/systemd/master.target.erb --- ruby-foreman-0.85.0/data/export/systemd/master.target.erb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/data/export/systemd/master.target.erb 2020-12-14 01:40:41.000000000 +0000 @@ -1,5 +1,5 @@ [Unit] -Wants=<%= process_master_names.join(' ') %> +Wants=<%= service_names.join(' ') %> [Install] WantedBy=multi-user.target diff -Nru ruby-foreman-0.85.0/data/export/systemd/process_master.target.erb ruby-foreman-0.87.2/data/export/systemd/process_master.target.erb --- ruby-foreman-0.85.0/data/export/systemd/process_master.target.erb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/data/export/systemd/process_master.target.erb 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -[Unit] -PartOf=<%= app %>.target diff -Nru ruby-foreman-0.85.0/data/export/systemd/process.service.erb ruby-foreman-0.87.2/data/export/systemd/process.service.erb --- ruby-foreman-0.85.0/data/export/systemd/process.service.erb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/data/export/systemd/process.service.erb 2020-12-14 01:40:41.000000000 +0000 @@ -1,15 +1,18 @@ [Unit] -PartOf=<%= app %>-<%= name %>.target +PartOf=<%= app %>.target +StopWhenUnneeded=yes [Service] User=<%= user %> WorkingDirectory=<%= engine.root %> -Environment=PORT=%i +Environment=PORT=<%= port %> +Environment=PS=<%= process_name %> <% engine.env.each_pair do |var,env| -%> Environment="<%= var %>=<%= env %>" <% end -%> -ExecStart=/bin/bash -lc 'exec <%= process.command %>' +ExecStart=/bin/bash -lc 'exec -a "<%= app %>-<%= process_name %>" <%= process.command %>' Restart=always +RestartSec=14s StandardInput=null StandardOutput=syslog StandardError=syslog diff -Nru ruby-foreman-0.85.0/debian/changelog ruby-foreman-0.87.2/debian/changelog --- ruby-foreman-0.85.0/debian/changelog 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/changelog 2023-04-05 18:36:46.000000000 +0000 @@ -1,3 +1,29 @@ +ruby-foreman (0.87.2-1ubuntu1~ppa1) lunar; urgency=medium + + * test. + + -- Lucas Kanashiro Wed, 05 Apr 2023 15:36:46 -0300 + +ruby-foreman (0.87.2-1) unstable; urgency=medium + + * Team upload + * New upstream version 0.87.2 + * Add upstream metadata file + * debian/control: update with dh-make-ruby + - Bump debhelper compat level to 13 + - Bump gem2deb build dependency to >= 1 + - Add Rules-Requires-Root: no + - Drop *-Ruby-Versions fields + - Replace hardcoded dependencies with ${ruby:Depends} + * Rebase patches + * Drop build dependency on ruby-thor. + Upstream has now bundled a copy of Thor. + * smoke-test: drop usage of deprecated AUTOPKGTEST_TMP + * Refresh patches + * Bump Standards-Version to 4.6.2; no changes needed + + -- Antonio Terceiro Fri, 10 Feb 2023 15:29:15 +0100 + ruby-foreman (0.85.0-2) unstable; urgency=medium * Team upload diff -Nru ruby-foreman-0.85.0/debian/control ruby-foreman-0.87.2/debian/control --- ruby-foreman-0.85.0/debian/control 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/control 2023-04-05 18:36:46.000000000 +0000 @@ -1,10 +1,11 @@ Source: ruby-foreman Section: ruby Priority: optional -Maintainer: Debian Ruby Team +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian Ruby Team Uploaders: Per Andersson , -Build-Depends: debhelper-compat (= 12), - gem2deb, +Build-Depends: debhelper-compat (= 13), + gem2deb (>= 1), rake, ruby-bundler, ruby-dotenv, @@ -12,23 +13,19 @@ ruby-rr, ruby-rspec, ruby-simplecov, - ruby-thor, ruby-timecop, yard -Standards-Version: 4.4.0 +Standards-Version: 4.6.2 Vcs-Git: https://salsa.debian.org/ruby-team/ruby-foreman.git Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-foreman Homepage: https://github.com/ddollar/foreman Testsuite: autopkgtest-pkg-ruby -XS-Ruby-Versions: all +Rules-Requires-Root: no Package: ruby-foreman Architecture: all -XB-Ruby-Versions: ${ruby:Versions} -Depends: ruby | ruby-interpreter, - ruby-dotenv, - ruby-thor (>= 0.19.1), - ${misc:Depends}, +Depends: ${misc:Depends}, + ${ruby:Depends}, ${shlibs:Depends} Description: Process manager for applications with multiple components Foreman is a process manager commonly used during development of diff -Nru ruby-foreman-0.85.0/debian/patches/0001-spec_helper-fix-running-tests-on-Debian.patch ruby-foreman-0.87.2/debian/patches/0001-spec_helper-fix-running-tests-on-Debian.patch --- ruby-foreman-0.85.0/debian/patches/0001-spec_helper-fix-running-tests-on-Debian.patch 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0001-spec_helper-fix-running-tests-on-Debian.patch 2023-04-05 18:36:36.000000000 +0000 @@ -0,0 +1,51 @@ +From: Antonio Terceiro +Date: Wed, 4 Feb 2015 11:31:10 -0200 +Subject: spec_helper: fix running tests on Debian + +Fixes running tests against installed package, and also against +different Ruby interpreters. + +Forwarded: not-needed +--- + spec/spec_helper.rb | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb +index dbeb752..defc7c6 100644 +--- a/spec/spec_helper.rb ++++ b/spec/spec_helper.rb +@@ -19,7 +19,9 @@ require "pp" + require "fakefs/safe" + require "fakefs/spec_helpers" + +-$:.unshift File.expand_path("../../lib", __FILE__) ++unless ENV['AUTOPKGTEST_TMP'] ++ ENV['PATH'] = 'bin:' + ENV['PATH'] ++end + + def mock_export_error(message) + expect { yield }.to raise_error(Foreman::Export::Exception, message) +@@ -45,9 +47,13 @@ def foreman(args) + end + end + ++def ruby ++ RbConfig::CONFIG['RUBY_INSTALL_NAME'] ++end ++ + def forked_foreman(args) + rd, wr = make_pipe +- Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr) ++ Process.spawn("#{ruby} -S foreman #{args}", :out => wr, :err => wr) + wr.close + rd.read + end +@@ -72,7 +78,7 @@ def fork_and_capture(&blk) + end + + def fork_and_get_exitstatus(args) +- pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null") ++ pid = Process.spawn("#{ruby} -S foreman #{args}", :out => "/dev/null", :err => "/dev/null") + Process.wait(pid) + $?.exitstatus + end diff -Nru ruby-foreman-0.85.0/debian/patches/0002-Add-missing-.env-file-required-for-tests.patch ruby-foreman-0.87.2/debian/patches/0002-Add-missing-.env-file-required-for-tests.patch --- ruby-foreman-0.85.0/debian/patches/0002-Add-missing-.env-file-required-for-tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0002-Add-missing-.env-file-required-for-tests.patch 2023-04-05 18:36:36.000000000 +0000 @@ -0,0 +1,17 @@ +From: Antonio Terceiro +Date: Wed, 4 Feb 2015 13:02:39 -0200 +Subject: Add missing .env file required for tests + +Forwarded: not-needed +--- + spec/resources/.env | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 spec/resources/.env + +diff --git a/spec/resources/.env b/spec/resources/.env +new file mode 100644 +index 0000000..c075a74 +--- /dev/null ++++ b/spec/resources/.env +@@ -0,0 +1 @@ ++FOO=bar diff -Nru ruby-foreman-0.85.0/debian/patches/0003-spec_helper-don-t-delete-tmp-after-each-test.patch ruby-foreman-0.87.2/debian/patches/0003-spec_helper-don-t-delete-tmp-after-each-test.patch --- ruby-foreman-0.85.0/debian/patches/0003-spec_helper-don-t-delete-tmp-after-each-test.patch 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0003-spec_helper-don-t-delete-tmp-after-each-test.patch 2023-04-05 18:36:36.000000000 +0000 @@ -0,0 +1,23 @@ +From: Antonio Terceiro +Date: Fri, 19 Feb 2021 17:53:32 -0300 +Subject: spec_helper: don't delete /tmp after each test + +For some reason that I could not investigate yet, in some situations (e.g. when +running the tests under autopkgtest/lxc) this is causing the real /tmp to be +deleted. There must be a race condition somewhere. +--- + spec/spec_helper.rb | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb +index defc7c6..5592025 100644 +--- a/spec/spec_helper.rb ++++ b/spec/spec_helper.rb +@@ -187,7 +187,4 @@ RSpec.configure do |config| + config.before(:each) do + FileUtils.mkdir_p('/tmp') + end +- config.after(:each) do +- FileUtils.rm_rf('/tmp') +- end + end diff -Nru ruby-foreman-0.85.0/debian/patches/0003-spec_helper-misc-changes-to-run-against-Debian-packa.patch ruby-foreman-0.87.2/debian/patches/0003-spec_helper-misc-changes-to-run-against-Debian-packa.patch --- ruby-foreman-0.85.0/debian/patches/0003-spec_helper-misc-changes-to-run-against-Debian-packa.patch 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0003-spec_helper-misc-changes-to-run-against-Debian-packa.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -From: Antonio Terceiro -Date: Wed, 4 Feb 2015 11:31:10 -0200 -Subject: spec_helper: misc changes to run against Debian packages - ---- - spec/spec_helper.rb | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb -index 712313a..bc446d1 100644 ---- a/spec/spec_helper.rb -+++ b/spec/spec_helper.rb -@@ -19,7 +19,9 @@ require "pp" - require "fakefs/safe" - require "fakefs/spec_helpers" - --$:.unshift File.expand_path("../../lib", __FILE__) -+unless ENV['ADTTMP'] -+ ENV['PATH'] = 'bin:' + ENV['PATH'] -+end - - def mock_export_error(message) - expect { yield }.to raise_error(Foreman::Export::Exception, message) -@@ -45,9 +47,13 @@ def foreman(args) - end - end - -+def ruby -+ RbConfig::CONFIG['RUBY_INSTALL_NAME'] -+end -+ - def forked_foreman(args) - rd, wr = make_pipe -- Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr) -+ Process.spawn("#{ruby} -S foreman #{args}", :out => wr, :err => wr) - wr.close - rd.read - end -@@ -72,7 +78,7 @@ def fork_and_capture(&blk) - end - - def fork_and_get_exitstatus(args) -- pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null") -+ pid = Process.spawn("#{ruby} -S foreman #{args}", :out => "/dev/null", :err => "/dev/null") - Process.wait(pid) - $?.exitstatus - end diff -Nru ruby-foreman-0.85.0/debian/patches/0004-Add-missing-.env-file-required-for-tests.patch ruby-foreman-0.87.2/debian/patches/0004-Add-missing-.env-file-required-for-tests.patch --- ruby-foreman-0.85.0/debian/patches/0004-Add-missing-.env-file-required-for-tests.patch 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0004-Add-missing-.env-file-required-for-tests.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -From: Antonio Terceiro -Date: Wed, 4 Feb 2015 13:02:39 -0200 -Subject: Add missing .env file required for tests - ---- - spec/resources/.env | 1 + - 1 file changed, 1 insertion(+) - create mode 100644 spec/resources/.env - -diff --git a/spec/resources/.env b/spec/resources/.env -new file mode 100644 -index 0000000..c075a74 ---- /dev/null -+++ b/spec/resources/.env -@@ -0,0 +1 @@ -+FOO=bar diff -Nru ruby-foreman-0.85.0/debian/patches/0004-spec_helper-don-t-delete-tmp-after-each-test.patch ruby-foreman-0.87.2/debian/patches/0004-spec_helper-don-t-delete-tmp-after-each-test.patch --- ruby-foreman-0.85.0/debian/patches/0004-spec_helper-don-t-delete-tmp-after-each-test.patch 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0004-spec_helper-don-t-delete-tmp-after-each-test.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -From: Antonio Terceiro -Date: Fri, 19 Feb 2021 17:53:32 -0300 -Subject: spec_helper: don't delete /tmp after each test - ---- - spec/spec_helper.rb | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb -index bc446d1..48ebd26 100644 ---- a/spec/spec_helper.rb -+++ b/spec/spec_helper.rb -@@ -187,7 +187,4 @@ RSpec.configure do |config| - config.before(:each) do - FileUtils.mkdir_p('/tmp') - end -- config.after(:each) do -- FileUtils.rm_rf('/tmp') -- end - end diff -Nru ruby-foreman-0.85.0/debian/patches/0005-relax-dependencies.patch ruby-foreman-0.87.2/debian/patches/0005-relax-dependencies.patch --- ruby-foreman-0.85.0/debian/patches/0005-relax-dependencies.patch 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/0005-relax-dependencies.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -From: Jongmin Kim -Date: Sat, 7 Sep 2019 16:53:55 -0300 -Subject: Relax the version of dependencies - -Last-Update: 2019-05-18 - -Last-Update: 2019-05-18 ---- - foreman.gemspec | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/foreman.gemspec b/foreman.gemspec -index 2b6c74a..7121ff5 100644 ---- a/foreman.gemspec -+++ b/foreman.gemspec -@@ -25,11 +25,11 @@ Gem::Specification.new do |s| - s.specification_version = 4 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then -- s.add_runtime_dependency(%q.freeze, ["~> 0.19.1"]) -+ s.add_runtime_dependency(%q.freeze, [">= 0.19.1"]) - else -- s.add_dependency(%q.freeze, ["~> 0.19.1"]) -+ s.add_dependency(%q.freeze, [">= 0.19.1"]) - end - else -- s.add_dependency(%q.freeze, ["~> 0.19.1"]) -+ s.add_dependency(%q.freeze, [">= 0.19.1"]) - end - end diff -Nru ruby-foreman-0.85.0/debian/patches/series ruby-foreman-0.87.2/debian/patches/series --- ruby-foreman-0.85.0/debian/patches/series 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/patches/series 2023-04-05 18:36:46.000000000 +0000 @@ -1,4 +1,3 @@ -0003-spec_helper-misc-changes-to-run-against-Debian-packa.patch -0004-Add-missing-.env-file-required-for-tests.patch -0005-relax-dependencies.patch -0004-spec_helper-don-t-delete-tmp-after-each-test.patch +0001-spec_helper-fix-running-tests-on-Debian.patch +0002-Add-missing-.env-file-required-for-tests.patch +0003-spec_helper-don-t-delete-tmp-after-each-test.patch diff -Nru ruby-foreman-0.85.0/debian/ruby-tests.rake ruby-foreman-0.87.2/debian/ruby-tests.rake --- ruby-foreman-0.85.0/debian/ruby-tests.rake 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/ruby-tests.rake 2023-04-05 18:36:46.000000000 +0000 @@ -1,5 +1,7 @@ require 'gem2deb/rake/spectask' +ENV["TERM"] = "xterm" + Gem2Deb::Rake::RSpecTask.new do |spec| spec.pattern = './spec/**/*_spec.rb' spec.exclude_pattern = './spec/foreman/export/**/*_spec.rb' if ENV['AUTOPKGTEST_TMP'] diff -Nru ruby-foreman-0.85.0/debian/rules ruby-foreman-0.87.2/debian/rules --- ruby-foreman-0.85.0/debian/rules 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/rules 2023-04-04 21:10:24.000000000 +0000 @@ -1,7 +1,6 @@ #!/usr/bin/make -f export GEM2DEB_TEST_RUNNER = --check-dependencies -BINARY_PKG := ruby-foreman export LC_ALL=C.UTF-8 export DH_RUBY = --gem-install diff -Nru ruby-foreman-0.85.0/debian/salsa-ci.yml ruby-foreman-0.87.2/debian/salsa-ci.yml --- ruby-foreman-0.85.0/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/debian/salsa-ci.yml 2023-04-04 21:10:24.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru ruby-foreman-0.85.0/debian/tests/control ruby-foreman-0.87.2/debian/tests/control --- ruby-foreman-0.85.0/debian/tests/control 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/tests/control 2023-04-04 21:10:24.000000000 +0000 @@ -1,4 +1 @@ Tests: smoke-test - -# Test-Command: gem2deb-test-runner --autopkgtest --check-dependencies 2>&1 -# Depends: @, gem2deb-test-runner, @builddeps@ diff -Nru ruby-foreman-0.85.0/debian/tests/smoke-test ruby-foreman-0.87.2/debian/tests/smoke-test --- ruby-foreman-0.85.0/debian/tests/smoke-test 2021-02-19 21:18:01.000000000 +0000 +++ ruby-foreman-0.87.2/debian/tests/smoke-test 2023-04-04 21:10:24.000000000 +0000 @@ -3,7 +3,7 @@ exec 2>&1 set -ex -cd $ADTTMP +cd $AUTOPKGTEST_TMP mkdir app cd app diff -Nru ruby-foreman-0.85.0/debian/upstream/metadata ruby-foreman-0.87.2/debian/upstream/metadata --- ruby-foreman-0.85.0/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/debian/upstream/metadata 2023-04-04 21:10:24.000000000 +0000 @@ -0,0 +1,7 @@ +--- +Archive: GitHub +Bug-Database: http://github.com/ddollar/foreman/issues +Bug-Submit: http://github.com/ddollar/foreman/issues/new +Changelog: http://github.com/ddollar/foreman/tags +Repository: http://github.com/ddollar/foreman.git +Repository-Browse: http://github.com/ddollar/foreman diff -Nru ruby-foreman-0.85.0/foreman.gemspec ruby-foreman-0.87.2/foreman.gemspec --- ruby-foreman-0.85.0/foreman.gemspec 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/foreman.gemspec 2020-12-14 01:40:41.000000000 +0000 @@ -2,34 +2,22 @@ # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- -# stub: foreman 0.85.0 ruby lib +# stub: foreman 0.87.2 ruby lib Gem::Specification.new do |s| s.name = "foreman".freeze - s.version = "0.85.0" + s.version = "0.87.2" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["David Dollar".freeze] - s.date = "2018-06-18" + s.date = "2020-08-07" s.description = "Process manager for applications with multiple components".freeze s.email = "ddollar@gmail.com".freeze s.executables = ["foreman".freeze] - s.files = ["README.md".freeze, "bin/foreman".freeze, "bin/foreman-runner".freeze, "data/example/Procfile".freeze, "data/example/Procfile.without_colon".freeze, "data/example/error".freeze, "data/example/log/neverdie.log".freeze, "data/example/spawnee".freeze, "data/example/spawner".freeze, "data/example/ticker".freeze, "data/example/utf8".freeze, "data/export/bluepill/master.pill.erb".freeze, "data/export/daemon/master.conf.erb".freeze, "data/export/daemon/process.conf.erb".freeze, "data/export/daemon/process_master.conf.erb".freeze, "data/export/launchd/launchd.plist.erb".freeze, "data/export/runit/log/run.erb".freeze, "data/export/runit/run.erb".freeze, "data/export/supervisord/app.conf.erb".freeze, "data/export/systemd/master.target.erb".freeze, "data/export/systemd/process.service.erb".freeze, "data/export/systemd/process_master.target.erb".freeze, "data/export/upstart/master.conf.erb".freeze, "data/export/upstart/process.conf.erb".freeze, "data/export/upstart/process_master.conf.erb".freeze, "lib/foreman.rb".freeze, "lib/foreman/cli.rb".freeze, "lib/foreman/distribution.rb".freeze, "lib/foreman/engine.rb".freeze, "lib/foreman/engine/cli.rb".freeze, "lib/foreman/env.rb".freeze, "lib/foreman/export.rb".freeze, "lib/foreman/export/base.rb".freeze, "lib/foreman/export/bluepill.rb".freeze, "lib/foreman/export/daemon.rb".freeze, "lib/foreman/export/inittab.rb".freeze, "lib/foreman/export/launchd.rb".freeze, "lib/foreman/export/runit.rb".freeze, "lib/foreman/export/supervisord.rb".freeze, "lib/foreman/export/systemd.rb".freeze, "lib/foreman/export/upstart.rb".freeze, "lib/foreman/helpers.rb".freeze, "lib/foreman/process.rb".freeze, "lib/foreman/procfile.rb".freeze, "lib/foreman/version.rb".freeze, "man/foreman.1".freeze, "spec/foreman/cli_spec.rb".freeze, "spec/foreman/engine_spec.rb".freeze, "spec/foreman/export/base_spec.rb".freeze, "spec/foreman/export/bluepill_spec.rb".freeze, "spec/foreman/export/daemon_spec.rb".freeze, "spec/foreman/export/inittab_spec.rb".freeze, "spec/foreman/export/launchd_spec.rb".freeze, "spec/foreman/export/runit_spec.rb".freeze, "spec/foreman/export/supervisord_spec.rb".freeze, "spec/foreman/export/systemd_spec.rb".freeze, "spec/foreman/export/upstart_spec.rb".freeze, "spec/foreman/export_spec.rb".freeze, "spec/foreman/helpers_spec.rb".freeze, "spec/foreman/process_spec.rb".freeze, "spec/foreman/procfile_spec.rb".freeze, "spec/foreman_spec.rb".freeze, "spec/helper_spec.rb".freeze, "spec/resources/Procfile".freeze, "spec/resources/Procfile.bad".freeze, "spec/resources/bin/echo".freeze, "spec/resources/bin/env".freeze, "spec/resources/bin/test".freeze, "spec/resources/bin/utf8".freeze, "spec/resources/export/bluepill/app-concurrency.pill".freeze, "spec/resources/export/bluepill/app.pill".freeze, "spec/resources/export/daemon/app-alpha-1.conf".freeze, "spec/resources/export/daemon/app-alpha-2.conf".freeze, "spec/resources/export/daemon/app-alpha.conf".freeze, "spec/resources/export/daemon/app-bravo-1.conf".freeze, "spec/resources/export/daemon/app-bravo.conf".freeze, "spec/resources/export/daemon/app.conf".freeze, "spec/resources/export/inittab/inittab.concurrency".freeze, "spec/resources/export/inittab/inittab.default".freeze, "spec/resources/export/launchd/launchd-a.default".freeze, "spec/resources/export/launchd/launchd-b.default".freeze, "spec/resources/export/launchd/launchd-c.default".freeze, "spec/resources/export/runit/app-alpha-1/log/run".freeze, "spec/resources/export/runit/app-alpha-1/run".freeze, "spec/resources/export/runit/app-alpha-2/log/run".freeze, "spec/resources/export/runit/app-alpha-2/run".freeze, "spec/resources/export/runit/app-bravo-1/log/run".freeze, "spec/resources/export/runit/app-bravo-1/run".freeze, "spec/resources/export/supervisord/app-alpha-1.conf".freeze, "spec/resources/export/supervisord/app-alpha-2.conf".freeze, "spec/resources/export/systemd/app-alpha.target".freeze, "spec/resources/export/systemd/app-alpha@.service".freeze, "spec/resources/export/systemd/app-bravo.target".freeze, "spec/resources/export/systemd/app-bravo@.service".freeze, "spec/resources/export/systemd/app.target".freeze, "spec/resources/export/upstart/app-alpha-1.conf".freeze, "spec/resources/export/upstart/app-alpha-2.conf".freeze, "spec/resources/export/upstart/app-alpha.conf".freeze, "spec/resources/export/upstart/app-bravo-1.conf".freeze, "spec/resources/export/upstart/app-bravo.conf".freeze, "spec/resources/export/upstart/app.conf".freeze, "spec/spec_helper.rb".freeze] + s.files = ["README.md".freeze, "bin/foreman".freeze, "bin/foreman-runner".freeze, "data/example/Procfile".freeze, "data/example/Procfile.without_colon".freeze, "data/example/error".freeze, "data/example/log/neverdie.log".freeze, "data/example/spawnee".freeze, "data/example/spawner".freeze, "data/example/ticker".freeze, "data/example/utf8".freeze, "data/export/bluepill/master.pill.erb".freeze, "data/export/daemon/master.conf.erb".freeze, "data/export/daemon/process.conf.erb".freeze, "data/export/daemon/process_master.conf.erb".freeze, "data/export/launchd/launchd.plist.erb".freeze, "data/export/runit/log/run.erb".freeze, "data/export/runit/run.erb".freeze, "data/export/supervisord/app.conf.erb".freeze, "data/export/systemd/master.target.erb".freeze, "data/export/systemd/process.service.erb".freeze, "data/export/upstart/master.conf.erb".freeze, "data/export/upstart/process.conf.erb".freeze, "data/export/upstart/process_master.conf.erb".freeze, "lib/foreman.rb".freeze, "lib/foreman/cli.rb".freeze, "lib/foreman/distribution.rb".freeze, "lib/foreman/engine.rb".freeze, "lib/foreman/engine/cli.rb".freeze, "lib/foreman/env.rb".freeze, "lib/foreman/export.rb".freeze, "lib/foreman/export/base.rb".freeze, "lib/foreman/export/bluepill.rb".freeze, "lib/foreman/export/daemon.rb".freeze, "lib/foreman/export/inittab.rb".freeze, "lib/foreman/export/launchd.rb".freeze, "lib/foreman/export/runit.rb".freeze, "lib/foreman/export/supervisord.rb".freeze, "lib/foreman/export/systemd.rb".freeze, "lib/foreman/export/upstart.rb".freeze, "lib/foreman/helpers.rb".freeze, "lib/foreman/process.rb".freeze, "lib/foreman/procfile.rb".freeze, "lib/foreman/vendor/thor/lib/thor.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/create_file.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/create_link.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/directory.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb".freeze, "lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb".freeze, "lib/foreman/vendor/thor/lib/thor/base.rb".freeze, "lib/foreman/vendor/thor/lib/thor/command.rb".freeze, "lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb".freeze, "lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb".freeze, "lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb".freeze, "lib/foreman/vendor/thor/lib/thor/error.rb".freeze, "lib/foreman/vendor/thor/lib/thor/group.rb".freeze, "lib/foreman/vendor/thor/lib/thor/invocation.rb".freeze, "lib/foreman/vendor/thor/lib/thor/line_editor.rb".freeze, "lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb".freeze, "lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb".freeze, "lib/foreman/vendor/thor/lib/thor/parser.rb".freeze, "lib/foreman/vendor/thor/lib/thor/parser/argument.rb".freeze, "lib/foreman/vendor/thor/lib/thor/parser/arguments.rb".freeze, "lib/foreman/vendor/thor/lib/thor/parser/option.rb".freeze, "lib/foreman/vendor/thor/lib/thor/parser/options.rb".freeze, "lib/foreman/vendor/thor/lib/thor/rake_compat.rb".freeze, "lib/foreman/vendor/thor/lib/thor/runner.rb".freeze, "lib/foreman/vendor/thor/lib/thor/shell.rb".freeze, "lib/foreman/vendor/thor/lib/thor/shell/basic.rb".freeze, "lib/foreman/vendor/thor/lib/thor/shell/color.rb".freeze, "lib/foreman/vendor/thor/lib/thor/shell/html.rb".freeze, "lib/foreman/vendor/thor/lib/thor/util.rb".freeze, "lib/foreman/vendor/thor/lib/thor/version.rb".freeze, "lib/foreman/version.rb".freeze, "man/foreman.1".freeze, "spec/foreman/cli_spec.rb".freeze, "spec/foreman/engine_spec.rb".freeze, "spec/foreman/export/base_spec.rb".freeze, "spec/foreman/export/bluepill_spec.rb".freeze, "spec/foreman/export/daemon_spec.rb".freeze, "spec/foreman/export/inittab_spec.rb".freeze, "spec/foreman/export/launchd_spec.rb".freeze, "spec/foreman/export/runit_spec.rb".freeze, "spec/foreman/export/supervisord_spec.rb".freeze, "spec/foreman/export/systemd_spec.rb".freeze, "spec/foreman/export/upstart_spec.rb".freeze, "spec/foreman/export_spec.rb".freeze, "spec/foreman/helpers_spec.rb".freeze, "spec/foreman/process_spec.rb".freeze, "spec/foreman/procfile_spec.rb".freeze, "spec/foreman_spec.rb".freeze, "spec/helper_spec.rb".freeze, "spec/resources/Procfile".freeze, "spec/resources/Procfile.bad".freeze, "spec/resources/bin/echo".freeze, "spec/resources/bin/env".freeze, "spec/resources/bin/test".freeze, "spec/resources/bin/utf8".freeze, "spec/resources/export/bluepill/app-concurrency.pill".freeze, "spec/resources/export/bluepill/app.pill".freeze, "spec/resources/export/daemon/app-alpha-1.conf".freeze, "spec/resources/export/daemon/app-alpha-2.conf".freeze, "spec/resources/export/daemon/app-alpha.conf".freeze, "spec/resources/export/daemon/app-bravo-1.conf".freeze, "spec/resources/export/daemon/app-bravo.conf".freeze, "spec/resources/export/daemon/app.conf".freeze, "spec/resources/export/inittab/inittab.concurrency".freeze, "spec/resources/export/inittab/inittab.default".freeze, "spec/resources/export/launchd/launchd-a.default".freeze, "spec/resources/export/launchd/launchd-b.default".freeze, "spec/resources/export/launchd/launchd-c.default".freeze, "spec/resources/export/runit/app-alpha-1/log/run".freeze, "spec/resources/export/runit/app-alpha-1/run".freeze, "spec/resources/export/runit/app-alpha-2/log/run".freeze, "spec/resources/export/runit/app-alpha-2/run".freeze, "spec/resources/export/runit/app-bravo-1/log/run".freeze, "spec/resources/export/runit/app-bravo-1/run".freeze, "spec/resources/export/supervisord/app-alpha-1.conf".freeze, "spec/resources/export/supervisord/app-alpha-2.conf".freeze, "spec/resources/export/systemd/app-alpha.1.service".freeze, "spec/resources/export/systemd/app-alpha.2.service".freeze, "spec/resources/export/systemd/app-alpha.target".freeze, "spec/resources/export/systemd/app-bravo.1.service".freeze, "spec/resources/export/systemd/app-bravo.target".freeze, "spec/resources/export/systemd/app.target".freeze, "spec/resources/export/upstart/app-alpha-1.conf".freeze, "spec/resources/export/upstart/app-alpha-2.conf".freeze, "spec/resources/export/upstart/app-alpha.conf".freeze, "spec/resources/export/upstart/app-bravo-1.conf".freeze, "spec/resources/export/upstart/app-bravo.conf".freeze, "spec/resources/export/upstart/app.conf".freeze, "spec/spec_helper.rb".freeze] s.homepage = "http://github.com/ddollar/foreman".freeze s.licenses = ["MIT".freeze] s.rubygems_version = "2.5.2.1".freeze s.summary = "Process manager for applications with multiple components".freeze - - if s.respond_to? :specification_version then - s.specification_version = 4 - - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q.freeze, ["~> 0.19.1"]) - else - s.add_dependency(%q.freeze, ["~> 0.19.1"]) - end - else - s.add_dependency(%q.freeze, ["~> 0.19.1"]) - end end diff -Nru ruby-foreman-0.85.0/lib/foreman/cli.rb ruby-foreman-0.87.2/lib/foreman/cli.rb --- ruby-foreman-0.85.0/lib/foreman/cli.rb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/cli.rb 2020-12-14 01:40:41.000000000 +0000 @@ -6,9 +6,9 @@ require "foreman/version" require "shellwords" require "yaml" -require "thor" +require "foreman/vendor/thor/lib/thor" -class Foreman::CLI < Thor +class Foreman::CLI < Foreman::Thor include Foreman::Helpers @@ -157,6 +157,6 @@ original_options = super return original_options unless File.file?(".foreman") defaults = ::YAML::load_file(".foreman") || {} - Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) + Foreman::Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) end end diff -Nru ruby-foreman-0.85.0/lib/foreman/export/systemd.rb ruby-foreman-0.87.2/lib/foreman/export/systemd.rb --- ruby-foreman-0.85.0/lib/foreman/export/systemd.rb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/export/systemd.rb 2020-12-14 01:40:41.000000000 +0000 @@ -17,22 +17,16 @@ clean_dir file end - process_master_names = [] + service_names = [] engine.each_process do |name, process| - service_fn = "#{app}-#{name}@.service" - write_template "systemd/process.service.erb", service_fn, binding - - create_directory("#{app}-#{name}.target.wants") - 1.upto(engine.formation[name]) - .collect { |num| engine.port_for(process, num) } - .collect { |port| "#{app}-#{name}@#{port}.service" } - .each do |process_name| - create_symlink("#{app}-#{name}.target.wants/#{process_name}", "../#{service_fn}") rescue Errno::EEXIST # This is needed because rr-mocks do not call the origial cleanup + 1.upto(engine.formation[name]) do |num| + port = engine.port_for(process, num) + process_name = "#{name}.#{num}" + service_filename = "#{app}-#{process_name}.service" + write_template "systemd/process.service.erb", service_filename, binding + service_names << service_filename end - - write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding - process_master_names << "#{app}-#{name}.target" end write_template "systemd/master.target.erb", "#{app}.target", binding diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,103 @@ +require "foreman/vendor/thor/lib/thor/actions/empty_directory" + +class Foreman::Thor + module Actions + # Create a new file relative to the destination root with the given data, + # which is the return value of a block or a data string. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # data:: the data to append to the file. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # create_file "lib/fun_party.rb" do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # create_file "config/apache.conf", "your apache config" + # + def create_file(destination, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + data = args.first + action CreateFile.new(self, destination, block || data.to_s, config) + end + alias_method :add_file, :create_file + + # CreateFile is a subset of Template, which instead of rendering a file with + # ERB, it gets the content from the user. + # + class CreateFile < EmptyDirectory #:nodoc: + attr_reader :data + + def initialize(base, destination, data, config = {}) + @data = data + super(base, destination, config) + end + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.binread(destination) == render + end + + # Holds the content to be added to the file. + # + def render + @render ||= if data.is_a?(Proc) + data.call + else + data + end + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + File.open(destination, "wb") { |f| f.write render } + end + given_destination + end + + protected + + # Now on conflict we check if the file is identical or not. + # + def on_conflict_behavior(&block) + if identical? + say_status :identical, :blue + else + options = base.options.merge(config) + force_or_skip_or_conflict(options[:force], options[:skip], &block) + end + end + + # If force is true, run the action, otherwise check if it's not being + # skipped. If both are false, show the file_collision menu, if the menu + # returns true, force it, otherwise skip. + # + def force_or_skip_or_conflict(force, skip, &block) + if force + say_status :force, :yellow + yield unless pretend? + elsif skip + say_status :skip, :yellow + else + say_status :conflict, :red + force_or_skip_or_conflict(force_on_collision?, true, &block) + end + end + + # Shows the file collision menu to the user and gets the result. + # + def force_on_collision? + base.shell.file_collision(destination) { render } + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,59 @@ +require "foreman/vendor/thor/lib/thor/actions/create_file" + +class Foreman::Thor + module Actions + # Create a new file relative to the destination root from the given source. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # source:: the relative path to the source root. + # config:: give :verbose => false to not log the status. + # :: give :symbolic => false for hard link. + # + # ==== Examples + # + # create_link "config/apache.conf", "/etc/apache.conf" + # + def create_link(destination, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + source = args.first + action CreateLink.new(self, destination, source, config) + end + alias_method :add_link, :create_link + + # CreateLink is a subset of CreateFile, which instead of taking a block of + # data, just takes a source string from the user. + # + class CreateLink < CreateFile #:nodoc: + attr_reader :data + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.identical?(render, destination) + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + # Create a symlink by default + config[:symbolic] = true if config[:symbolic].nil? + File.unlink(destination) if exists? + if config[:symbolic] + File.symlink(render, destination) + else + File.link(render, destination) + end + end + given_destination + end + + def exists? + super || File.symlink?(destination) + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/directory.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/directory.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/directory.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/directory.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,118 @@ +require "foreman/vendor/thor/lib/thor/actions/empty_directory" + +class Foreman::Thor + module Actions + # Copies recursively the files from source directory to root directory. + # If any of the files finishes with .tt, it's considered to be a template + # and is placed in the destination without the extension .tt. If any + # empty directory is found, it's copied and all .empty_directory files are + # ignored. If any file name is wrapped within % signs, the text within + # the % signs will be executed as a method and replaced with the returned + # value. Let's suppose a doc directory with the following files: + # + # doc/ + # components/.empty_directory + # README + # rdoc.rb.tt + # %app_name%.rb + # + # When invoked as: + # + # directory "doc" + # + # It will create a doc directory in the destination with the following + # files (assuming that the `app_name` method returns the value "blog"): + # + # doc/ + # components/ + # README + # rdoc.rb + # blog.rb + # + # Encoded path note: Since Foreman::Thor internals use Object#respond_to? to check if it can + # expand %something%, this `something` should be a public method in the class calling + # #directory. If a method is private, Foreman::Thor stack raises PrivateMethodEncodedError. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # If :recursive => false, does not look for paths recursively. + # If :mode => :preserve, preserve the file mode from the source. + # If :exclude_pattern => /regexp/, prevents copying files that match that regexp. + # + # ==== Examples + # + # directory "doc" + # directory "doc", "docs", :recursive => false + # + def directory(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + action Directory.new(self, source, destination || source, config, &block) + end + + class Directory < EmptyDirectory #:nodoc: + attr_reader :source + + def initialize(base, source, destination = nil, config = {}, &block) + @source = File.expand_path(base.find_in_source_paths(source.to_s)) + @block = block + super(base, destination, {:recursive => true}.merge(config)) + end + + def invoke! + base.empty_directory given_destination, config + execute! + end + + def revoke! + execute! + end + + protected + + def execute! + lookup = Util.escape_globs(source) + lookup = config[:recursive] ? File.join(lookup, "**") : lookup + lookup = file_level_lookup(lookup) + + files(lookup).sort.each do |file_source| + next if File.directory?(file_source) + next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern]) + file_destination = File.join(given_destination, file_source.gsub(source, ".")) + file_destination.gsub!("/./", "/") + + case file_source + when /\.empty_directory$/ + dirname = File.dirname(file_destination).gsub(%r{/\.$}, "") + next if dirname == given_destination + base.empty_directory(dirname, config) + when /#{TEMPLATE_EXTNAME}$/ + base.template(file_source, file_destination[0..-4], config, &@block) + else + base.copy_file(file_source, file_destination, config, &@block) + end + end + end + + if RUBY_VERSION < "2.0" + def file_level_lookup(previous_lookup) + File.join(previous_lookup, "{*,.[a-z]*}") + end + + def files(lookup) + Dir[lookup] + end + else + def file_level_lookup(previous_lookup) + File.join(previous_lookup, "*") + end + + def files(lookup) + Dir.glob(lookup, File::FNM_DOTMATCH) + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,135 @@ +class Foreman::Thor + module Actions + # Creates an empty directory. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # empty_directory "doc" + # + def empty_directory(destination, config = {}) + action EmptyDirectory.new(self, destination, config) + end + + # Class which holds create directory logic. This is the base class for + # other actions like create_file and directory. + # + # This implementation is based in Templater actions, created by Jonas Nicklas + # and Michael S. Klishin under MIT LICENSE. + # + class EmptyDirectory #:nodoc: + attr_reader :base, :destination, :given_destination, :relative_destination, :config + + # Initializes given the source and destination. + # + # ==== Parameters + # base:: A Foreman::Thor::Base instance + # source:: Relative path to the source of this file + # destination:: Relative path to the destination of this file + # config:: give :verbose => false to not log the status. + # + def initialize(base, destination, config = {}) + @base = base + @config = {:verbose => true}.merge(config) + self.destination = destination + end + + # Checks if the destination file already exists. + # + # ==== Returns + # Boolean:: true if the file exists, false otherwise. + # + def exists? + ::File.exist?(destination) + end + + def invoke! + invoke_with_conflict_check do + ::FileUtils.mkdir_p(destination) + end + end + + def revoke! + say_status :remove, :red + ::FileUtils.rm_rf(destination) if !pretend? && exists? + given_destination + end + + protected + + # Shortcut for pretend. + # + def pretend? + base.options[:pretend] + end + + # Sets the absolute destination value from a relative destination value. + # It also stores the given and relative destination. Let's suppose our + # script is being executed on "dest", it sets the destination root to + # "dest". The destination, given_destination and relative_destination + # are related in the following way: + # + # inside "bar" do + # empty_directory "baz" + # end + # + # destination #=> dest/bar/baz + # relative_destination #=> bar/baz + # given_destination #=> baz + # + def destination=(destination) + return unless destination + @given_destination = convert_encoded_instructions(destination.to_s) + @destination = ::File.expand_path(@given_destination, base.destination_root) + @relative_destination = base.relative_to_original_destination_root(@destination) + end + + # Filenames in the encoded form are converted. If you have a file: + # + # %file_name%.rb + # + # It calls #file_name from the base and replaces %-string with the + # return value (should be String) of #file_name: + # + # user.rb + # + # The method referenced can be either public or private. + # + def convert_encoded_instructions(filename) + filename.gsub(/%(.*?)%/) do |initial_string| + method = $1.strip + base.respond_to?(method, true) ? base.send(method) : initial_string + end + end + + # Receives a hash of options and just execute the block if some + # conditions are met. + # + def invoke_with_conflict_check(&block) + if exists? + on_conflict_behavior(&block) + else + say_status :create, :green + yield unless pretend? + end + + destination + end + + # What to do when the destination file already exists. + # + def on_conflict_behavior + say_status :exist, :blue + end + + # Shortcut to say_status shell method. + # + def say_status(status, color) + base.shell.say_status status, relative_destination, color if config[:verbose] + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,327 @@ +require "erb" +require "open-uri" + +class Foreman::Thor + module Actions + # Copies the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status, and + # :mode => :preserve, to preserve the file mode from the source. + + # + # ==== Examples + # + # copy_file "README", "doc/README" + # + # copy_file "doc/README" + # + def copy_file(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_file destination, nil, config do + content = File.binread(source) + content = yield(content) if block + content + end + if config[:mode] == :preserve + mode = File.stat(source).mode + chmod(destination, mode, config) + end + end + + # Links the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # link_file "README", "doc/README" + # + # link_file "doc/README" + # + def link_file(source, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_link destination, source, config + end + + # Gets the content at the given address and places it at the given relative + # destination. If a block is given instead of destination, the content of + # the url is yielded and used as location. + # + # ==== Parameters + # source:: the address of the given content. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # get "http://gist.github.com/103208", "doc/README" + # + # get "http://gist.github.com/103208" do |content| + # content.split("\n").first + # end + # + def get(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first + + source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ %r{^https?\://} + render = open(source) { |input| input.binmode.read } + + destination ||= if block_given? + block.arity == 1 ? yield(render) : yield + else + File.basename(source) + end + + create_file destination, render, config + end + + # Gets an ERB template at the relative source, executes it and makes a copy + # at the relative destination. If the destination is not given it's assumed + # to be equal to the source removing .tt from the filename. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # template "README", "doc/README" + # + # template "doc/README" + # + def template(source, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "") + + source = File.expand_path(find_in_source_paths(source.to_s)) + context = config.delete(:context) || instance_eval("binding") + + create_file destination, nil, config do + content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").result(context) + content = yield(content) if block + content + end + end + + # Changes the mode of the given file or directory. + # + # ==== Parameters + # mode:: the file mode + # path:: the name of the file to change mode + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # chmod "script/server", 0755 + # + def chmod(path, mode, config = {}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) + FileUtils.chmod_R(mode, path) unless options[:pretend] + end + + # Prepend text to a file. Since it depends on insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to prepend to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # prepend_to_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def prepend_to_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config[:after] = /\A/ + insert_into_file(path, *(args << config), &block) + end + alias_method :prepend_file, :prepend_to_file + + # Append text to a file. Since it depends on insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to append to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # append_to_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # append_to_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def append_to_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config[:before] = /\z/ + insert_into_file(path, *(args << config), &block) + end + alias_method :append_file, :append_to_file + + # Injects text right after the class definition. Since it depends on + # insert_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # klass:: the class to be manipulated + # data:: the data to append to the class, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n" + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController do + # " filter_parameter :password\n" + # end + # + def inject_into_class(path, klass, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config[:after] = /class #{klass}\n|class #{klass} .*\n/ + insert_into_file(path, *(args << config), &block) + end + + # Run a regular expression replacement on a file. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string to be replaced + # replacement:: the replacement, can be also given as a block + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + # gsub_file 'README', /rake/, :green do |match| + # match << " no more. Use thor!" + # end + # + def gsub_file(path, flag, *args, &block) + return unless behavior == :invoke + config = args.last.is_a?(Hash) ? args.pop : {} + + path = File.expand_path(path, destination_root) + say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + unless options[:pretend] + content = File.binread(path) + content.gsub!(flag, *args, &block) + File.open(path, "wb") { |file| file.write(content) } + end + end + + # Uncomment all lines matching a given regex. It will leave the space + # which existed before the comment hash in tact but will remove any spacing + # between the comment hash and the beginning of the line. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to uncomment + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # uncomment_lines 'config/initializers/session_store.rb', /active_record/ + # + def uncomment_lines(path, flag, *args) + flag = flag.respond_to?(:source) ? flag.source : flag + + gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args) + end + + # Comment all lines matching a given regex. It will leave the space + # which existed before the beginning of the line in tact and will insert + # a single space after the comment hash. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string used to decide which lines to comment + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # comment_lines 'config/initializers/session_store.rb', /cookie_store/ + # + def comment_lines(path, flag, *args) + flag = flag.respond_to?(:source) ? flag.source : flag + + gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args) + end + + # Removes a file at the given location. + # + # ==== Parameters + # path:: path of the file to be changed + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # remove_file 'README' + # remove_file 'app/controllers/application_controller.rb' + # + def remove_file(path, config = {}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + + say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) + ::FileUtils.rm_rf(path) if !options[:pretend] && File.exist?(path) + end + alias_method :remove_dir, :remove_file + + attr_accessor :output_buffer + private :output_buffer, :output_buffer= + + private + + def concat(string) + @output_buffer.concat(string) + end + + def capture(*args) + with_output_buffer { yield(*args) } + end + + def with_output_buffer(buf = "") #:nodoc: + self.output_buffer, old_buffer = buf, output_buffer + yield + output_buffer + ensure + self.output_buffer = old_buffer + end + + # Foreman::Thor::Actions#capture depends on what kind of buffer is used in ERB. + # Thus CapturableERB fixes ERB to use String buffer. + class CapturableERB < ERB + def set_eoutvar(compiler, eoutvar = "_erbout") + compiler.put_cmd = "#{eoutvar}.concat" + compiler.insert_cmd = "#{eoutvar}.concat" + compiler.pre_cmd = ["#{eoutvar} = ''"] + compiler.post_cmd = [eoutvar] + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,103 @@ +require "foreman/vendor/thor/lib/thor/actions/empty_directory" + +class Foreman::Thor + module Actions + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: give :verbose => false to not log the status and the flag + # for injection (:after or :before) or :force => true for + # insert two or more times the same content. + # + # ==== Examples + # + # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # + # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") + # end + # + def insert_into_file(destination, *args, &block) + data = block_given? ? block : args.shift + config = args.shift + action InjectIntoFile.new(self, destination, data, config) + end + alias_method :inject_into_file, :insert_into_file + + class InjectIntoFile < EmptyDirectory #:nodoc: + attr_reader :replacement, :flag, :behavior + + def initialize(base, destination, data, config) + super(base, destination, {:verbose => true}.merge(config)) + + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] + else + [:before, @config.delete(:before)] + end + + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + end + + def invoke! + say_status :invoke + + content = if @behavior == :after + '\0' + replacement + else + replacement + '\0' + end + + replace!(/#{flag}/, content, config[:force]) + end + + def revoke! + say_status :revoke + + regexp = if @behavior == :after + content = '\1\2' + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m + else + content = '\2\3' + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m + end + + replace!(regexp, content, true) + end + + protected + + def say_status(behavior) + status = if behavior == :invoke + if flag == /\A/ + :prepend + elsif flag == /\z/ + :append + else + :insert + end + else + :subtract + end + + super(status, config[:verbose]) + end + + # Adds the content to the file. + # + def replace!(regexp, string, force) + return if base.options[:pretend] + content = File.binread(destination) + if force || !content.include?(replacement) + content.gsub!(regexp, string) + File.open(destination, "wb") { |file| file.write(content) } + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/actions.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,318 @@ +require "fileutils" +require "uri" +require "foreman/vendor/thor/lib/thor/core_ext/io_binary_read" +require "foreman/vendor/thor/lib/thor/actions/create_file" +require "foreman/vendor/thor/lib/thor/actions/create_link" +require "foreman/vendor/thor/lib/thor/actions/directory" +require "foreman/vendor/thor/lib/thor/actions/empty_directory" +require "foreman/vendor/thor/lib/thor/actions/file_manipulation" +require "foreman/vendor/thor/lib/thor/actions/inject_into_file" + +class Foreman::Thor + module Actions + attr_accessor :behavior + + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Hold source paths for one Foreman::Thor instance. source_paths_for_search is the + # method responsible to gather source_paths from this current class, + # inherited paths and the source root. + # + def source_paths + @_source_paths ||= [] + end + + # Stores and return the source root for this class + def source_root(path = nil) + @_source_root = path if path + @_source_root ||= nil + end + + # Returns the source paths in the following order: + # + # 1) This class source paths + # 2) Source root + # 3) Parents source paths + # + def source_paths_for_search + paths = [] + paths += source_paths + paths << source_root if source_root + paths += from_superclass(:source_paths, []) + paths + end + + # Add runtime options that help actions execution. + # + def add_runtime_options! + class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Suppress status output" + + class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + end + end + + # Extends initializer to add more configuration options. + # + # ==== Configuration + # behavior:: The actions default behavior. Can be :invoke or :revoke. + # It also accepts :force, :skip and :pretend to set the behavior + # and the respective option. + # + # destination_root:: The root directory needed for some actions. + # + def initialize(args = [], options = {}, config = {}) + self.behavior = case config[:behavior].to_s + when "force", "skip" + _cleanup_options_and_set(options, config[:behavior]) + :invoke + when "revoke" + :revoke + else + :invoke + end + + super + self.destination_root = config[:destination_root] + end + + # Wraps an action object and call it accordingly to the thor class behavior. + # + def action(instance) #:nodoc: + if behavior == :revoke + instance.revoke! + else + instance.invoke! + end + end + + # Returns the root for this thor class (also aliased as destination root). + # + def destination_root + @destination_stack.last + end + + # Sets the root for this thor class. Relatives path are added to the + # directory where the script was invoked and expanded. + # + def destination_root=(root) + @destination_stack ||= [] + @destination_stack[0] = File.expand_path(root || "") + end + + # Returns the given path relative to the absolute root (ie, root where + # the script started). + # + def relative_to_original_destination_root(path, remove_dot = true) + path = path.dup + if path.gsub!(@destination_stack[0], ".") + remove_dot ? (path[2..-1] || "") : path + else + path + end + end + + # Holds source paths in instance so they can be manipulated. + # + def source_paths + @source_paths ||= self.class.source_paths_for_search + end + + # Receives a file or directory and search for it in the source paths. + # + def find_in_source_paths(file) + possible_files = [file, file + TEMPLATE_EXTNAME] + relative_root = relative_to_original_destination_root(destination_root, false) + + source_paths.each do |source| + possible_files.each do |f| + source_file = File.expand_path(f, File.join(source, relative_root)) + return source_file if File.exist?(source_file) + end + end + + message = "Could not find #{file.inspect} in any of your source paths. " + + unless self.class.source_root + message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. " + end + + message << if source_paths.empty? + "Currently you have no source paths." + else + "Your current source paths are: \n#{source_paths.join("\n")}" + end + + raise Error, message + end + + # Do something in the root or on a provided subfolder. If a relative path + # is given it's referenced from the current root. The full path is yielded + # to the block you provide. The path is set back to the previous path when + # the method exits. + # + # ==== Parameters + # dir:: the directory to move to. + # config:: give :verbose => true to log and use padding. + # + def inside(dir = "", config = {}, &block) + verbose = config.fetch(:verbose, false) + pretend = options[:pretend] + + say_status :inside, dir, verbose + shell.padding += 1 if verbose + @destination_stack.push File.expand_path(dir, destination_root) + + # If the directory doesnt exist and we're not pretending + if !File.exist?(destination_root) && !pretend + FileUtils.mkdir_p(destination_root) + end + + if pretend + # In pretend mode, just yield down to the block + block.arity == 1 ? yield(destination_root) : yield + else + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + end + + @destination_stack.pop + shell.padding -= 1 if verbose + end + + # Goes to the root and execute the given block. + # + def in_root + inside(@destination_stack.first) { yield } + end + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + def apply(path, config = {}) + verbose = config.fetch(:verbose, true) + is_uri = path =~ %r{^https?\://} + path = find_in_source_paths(path) unless is_uri + + say_status :apply, path, verbose + shell.padding += 1 if verbose + + contents = if is_uri + open(path, "Accept" => "application/x-thor-template", &:read) + else + open(path, &:read) + end + + instance_eval(contents, path) + shell.padding -= 1 if verbose + end + + # Executes a command returning the contents of the command. + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with + # to append an executable to command execution. + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, config = {}) + return unless behavior == :invoke + + destination = relative_to_original_destination_root(destination_root, false) + desc = "#{command} from #{destination.inspect}" + + if config[:with] + desc = "#{File.basename(config[:with].to_s)} #{desc}" + command = "#{config[:with]} #{command}" + end + + say_status :run, desc, config.fetch(:verbose, true) + + !options[:pretend] && config[:capture] ? `#{command}` : system(command.to_s) + end + + # Executes a ruby script (taking into account WIN32 platform quirks). + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status. + # + def run_ruby_script(command, config = {}) + return unless behavior == :invoke + run command, config.merge(:with => Foreman::Thor::Util.ruby_command) + end + + # Run a thor command. A hash of options can be given and it's converted to + # switches. + # + # ==== Parameters + # command:: the command to be invoked + # args:: arguments to the command + # config:: give :verbose => false to not log the status, :capture => true to hide to output. + # Other options are given as parameter to Foreman::Thor. + # + # + # ==== Examples + # + # thor :install, "http://gist.github.com/103208" + # #=> thor install http://gist.github.com/103208 + # + # thor :list, :all => true, :substring => 'rails' + # #=> thor list --all --substring=rails + # + def thor(command, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + verbose = config.key?(:verbose) ? config.delete(:verbose) : true + pretend = config.key?(:pretend) ? config.delete(:pretend) : false + capture = config.key?(:capture) ? config.delete(:capture) : false + + args.unshift(command) + args.push Foreman::Thor::Options.to_switches(config) + command = args.join(" ").strip + + run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture + end + + protected + + # Allow current root to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:destination_root => destination_root) + end + + def _cleanup_options_and_set(options, key) #:nodoc: + case options + when Array + %w(--force -f --skip -s).each { |i| options.delete(i) } + options << "--#{key}" + when Hash + [:force, :skip, "force", "skip"].each { |i| options.delete(i) } + options.merge!(key => true) + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/base.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/base.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/base.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/base.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,656 @@ +require "foreman/vendor/thor/lib/thor/command" +require "foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access" +require "foreman/vendor/thor/lib/thor/core_ext/ordered_hash" +require "foreman/vendor/thor/lib/thor/error" +require "foreman/vendor/thor/lib/thor/invocation" +require "foreman/vendor/thor/lib/thor/parser" +require "foreman/vendor/thor/lib/thor/shell" +require "foreman/vendor/thor/lib/thor/line_editor" +require "foreman/vendor/thor/lib/thor/util" + +class Foreman::Thor + autoload :Actions, "foreman/vendor/thor/lib/thor/actions" + autoload :RakeCompat, "foreman/vendor/thor/lib/thor/rake_compat" + autoload :Group, "foreman/vendor/thor/lib/thor/group" + + # Shortcuts for help. + HELP_MAPPINGS = %w(-h -? --help -D) + + # Foreman::Thor methods that should not be overwritten by the user. + THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root + action add_file create_file in_root inside run run_ruby_script) + + TEMPLATE_EXTNAME = ".tt" + + module Base + attr_accessor :options, :parent_options, :args + + # It receives arguments in an Array and two hashes, one for options and + # other for configuration. + # + # Notice that it does not check if all required arguments were supplied. + # It should be done by the parser. + # + # ==== Parameters + # args:: An array of objects. The objects are applied to their + # respective accessors declared with argument. + # + # options:: An options hash that will be available as self.options. + # The hash given is converted to a hash with indifferent + # access, magic predicates (options.skip?) and then frozen. + # + # config:: Configuration for this Foreman::Thor class. + # + def initialize(args = [], local_options = {}, config = {}) + parse_options = config[:current_command] && config[:current_command].disable_class_options ? {} : self.class.class_options + + # The start method splits inbound arguments at the first argument + # that looks like an option (starts with - or --). It then calls + # new, passing in the two halves of the arguments Array as the + # first two parameters. + + command_options = config.delete(:command_options) # hook for start + parse_options = parse_options.merge(command_options) if command_options + if local_options.is_a?(Array) + array_options = local_options + hash_options = {} + else + # Handle the case where the class was explicitly instantiated + # with pre-parsed options. + array_options = [] + hash_options = local_options + end + + # Let Foreman::Thor::Options parse the options first, so it can remove + # declared options from the array. This will leave us with + # a list of arguments that weren't declared. + stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command] + opts = Foreman::Thor::Options.new(parse_options, hash_options, stop_on_unknown) + self.options = opts.parse(array_options) + self.options = config[:class_options].merge(options) if config[:class_options] + + # If unknown options are disallowed, make sure that none of the + # remaining arguments looks like an option. + opts.check_unknown! if self.class.check_unknown_options?(config) + + # Add the remaining arguments from the options parser to the + # arguments passed in to initialize. Then remove any positional + # arguments declared using #argument (this is primarily used + # by Foreman::Thor::Group). Tis will leave us with the remaining + # positional arguments. + to_parse = args + to_parse += opts.remaining unless self.class.strict_args_position?(config) + + thor_args = Foreman::Thor::Arguments.new(self.class.arguments) + thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) } + @args = thor_args.remaining + end + + class << self + def included(base) #:nodoc: + base.extend ClassMethods + base.send :include, Invocation + base.send :include, Shell + end + + # Returns the classes that inherits from Foreman::Thor or Foreman::Thor::Group. + # + # ==== Returns + # Array[Class] + # + def subclasses + @subclasses ||= [] + end + + # Returns the files where the subclasses are kept. + # + # ==== Returns + # Hash[path => Class] + # + def subclass_files + @subclass_files ||= Hash.new { |h, k| h[k] = [] } + end + + # Whenever a class inherits from Foreman::Thor or Foreman::Thor::Group, we should track the + # class and the file on Foreman::Thor::Base. This is the method responsable for it. + # + def register_klass_file(klass) #:nodoc: + file = caller[1].match(/(.*):\d+/)[1] + Foreman::Thor::Base.subclasses << klass unless Foreman::Thor::Base.subclasses.include?(klass) + + file_subclasses = Foreman::Thor::Base.subclass_files[File.expand_path(file)] + file_subclasses << klass unless file_subclasses.include?(klass) + end + end + + module ClassMethods + def attr_reader(*) #:nodoc: + no_commands { super } + end + + def attr_writer(*) #:nodoc: + no_commands { super } + end + + def attr_accessor(*) #:nodoc: + no_commands { super } + end + + # If you want to raise an error for unknown options, call check_unknown_options! + # This is disabled by default to allow dynamic invocations. + def check_unknown_options! + @check_unknown_options = true + end + + def check_unknown_options #:nodoc: + @check_unknown_options ||= from_superclass(:check_unknown_options, false) + end + + def check_unknown_options?(config) #:nodoc: + !!check_unknown_options + end + + # If true, option parsing is suspended as soon as an unknown option or a + # regular argument is encountered. All remaining arguments are passed to + # the command as regular arguments. + def stop_on_unknown_option?(command_name) #:nodoc: + false + end + + # If you want only strict string args (useful when cascading thor classes), + # call strict_args_position! This is disabled by default to allow dynamic + # invocations. + def strict_args_position! + @strict_args_position = true + end + + def strict_args_position #:nodoc: + @strict_args_position ||= from_superclass(:strict_args_position, false) + end + + def strict_args_position?(config) #:nodoc: + !!strict_args_position + end + + # Adds an argument to the class and creates an attr_accessor for it. + # + # Arguments are different from options in several aspects. The first one + # is how they are parsed from the command line, arguments are retrieved + # from position: + # + # thor command NAME + # + # Instead of: + # + # thor command --name=NAME + # + # Besides, arguments are used inside your code as an accessor (self.argument), + # while options are all kept in a hash (self.options). + # + # Finally, arguments cannot have type :default or :boolean but can be + # optional (supplying :optional => :true or :required => false), although + # you cannot have a required argument after a non-required argument. If you + # try it, an error is raised. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :optional - If the argument is optional or not. + # :type - The type of the argument, can be :string, :hash, :array, :numeric. + # :default - Default value for this argument. It cannot be required and have default values. + # :banner - String to show on usage notes. + # + # ==== Errors + # ArgumentError:: Raised if you supply a required argument after a non required one. + # + def argument(name, options = {}) + is_thor_reserved_word?(name, :argument) + no_commands { attr_accessor name } + + required = if options.key?(:optional) + !options[:optional] + elsif options.key?(:required) + options[:required] + else + options[:default].nil? + end + + remove_argument name + + if required + arguments.each do |argument| + next if argument.required? + raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \ + "the non-required argument #{argument.human_name.inspect}." + end + end + + options[:required] = required + + arguments << Foreman::Thor::Argument.new(name, options) + end + + # Returns this class arguments, looking up in the ancestors chain. + # + # ==== Returns + # Array[Foreman::Thor::Argument] + # + def arguments + @arguments ||= from_superclass(:arguments, []) + end + + # Adds a bunch of options to the set of class options. + # + # class_options :foo => false, :bar => :required, :baz => :string + # + # If you prefer more detailed declaration, check class_option. + # + # ==== Parameters + # Hash[Symbol => Object] + # + def class_options(options = nil) + @class_options ||= from_superclass(:class_options, {}) + build_options(options, @class_options) if options + @class_options + end + + # Adds an option to the set of class options + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc:: -- Description for the argument. + # :required:: -- If the argument is required or not. + # :default:: -- Default value for this argument. + # :group:: -- The group for this options. Use by class options to output options in different levels. + # :aliases:: -- Aliases for this option. Note: Foreman::Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead. + # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner:: -- String to show on usage notes. + # :hide:: -- If you want to hide this option from the help. + # + def class_option(name, options = {}) + build_option(name, options, class_options) + end + + # Removes a previous defined argument. If :undefine is given, undefine + # accessors as well. + # + # ==== Parameters + # names:: Arguments to be removed + # + # ==== Examples + # + # remove_argument :foo + # remove_argument :foo, :bar, :baz, :undefine => true + # + def remove_argument(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + arguments.delete_if { |a| a.name == name.to_s } + undef_method name, "#{name}=" if options[:undefine] + end + end + + # Removes a previous defined class option. + # + # ==== Parameters + # names:: Class options to be removed + # + # ==== Examples + # + # remove_class_option :foo + # remove_class_option :foo, :bar, :baz + # + def remove_class_option(*names) + names.each do |name| + class_options.delete(name) + end + end + + # Defines the group. This is used when thor list is invoked so you can specify + # that only commands from a pre-defined group will be shown. Defaults to standard. + # + # ==== Parameters + # name + # + def group(name = nil) + if name + @group = name.to_s + else + @group ||= from_superclass(:group, "standard") + end + end + + # Returns the commands for this Foreman::Thor class. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Foreman::Thor::Command + # objects as values. + # + def commands + @commands ||= Foreman::Thor::CoreExt::OrderedHash.new + end + alias_method :tasks, :commands + + # Returns the commands for this Foreman::Thor class and all subclasses. + # + # ==== Returns + # OrderedHash:: An ordered hash with commands names as keys and Foreman::Thor::Command + # objects as values. + # + def all_commands + @all_commands ||= from_superclass(:all_commands, Foreman::Thor::CoreExt::OrderedHash.new) + @all_commands.merge!(commands) + end + alias_method :all_tasks, :all_commands + + # Removes a given command from this Foreman::Thor class. This is usually done if you + # are inheriting from another class and don't want it to be available + # anymore. + # + # By default it only remove the mapping to the command. But you can supply + # :undefine => true to undefine the method from the class as well. + # + # ==== Parameters + # name:: The name of the command to be removed + # options:: You can give :undefine => true if you want commands the method + # to be undefined from the class as well. + # + def remove_command(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + commands.delete(name.to_s) + all_commands.delete(name.to_s) + undef_method name if options[:undefine] + end + end + alias_method :remove_task, :remove_command + + # All methods defined inside the given block are not added as commands. + # + # So you can do: + # + # class MyScript < Foreman::Thor + # no_commands do + # def this_is_not_a_command + # end + # end + # end + # + # You can also add the method and remove it from the command list: + # + # class MyScript < Foreman::Thor + # def this_is_not_a_command + # end + # remove_command :this_is_not_a_command + # end + # + def no_commands + @no_commands = true + yield + ensure + @no_commands = false + end + alias_method :no_tasks, :no_commands + + # Sets the namespace for the Foreman::Thor or Foreman::Thor::Group class. By default the + # namespace is retrieved from the class name. If your Foreman::Thor class is named + # Scripts::MyScript, the help method, for example, will be called as: + # + # thor scripts:my_script -h + # + # If you change the namespace: + # + # namespace :my_scripts + # + # You change how your commands are invoked: + # + # thor my_scripts -h + # + # Finally, if you change your namespace to default: + # + # namespace :default + # + # Your commands can be invoked with a shortcut. Instead of: + # + # thor :my_command + # + def namespace(name = nil) + if name + @namespace = name.to_s + else + @namespace ||= Foreman::Thor::Util.namespace_from_thor_class(self) + end + end + + # Parses the command and options from the given args, instantiate the class + # and invoke the command. This method is used when the arguments must be parsed + # from an array. If you are inside Ruby and want to use a Foreman::Thor class, you + # can simply initialize it: + # + # script = MyScript.new(args, options, config) + # script.invoke(:command, first_arg, second_arg, third_arg) + # + def start(given_args = ARGV, config = {}) + config[:shell] ||= Foreman::Thor::Base.shell.new + dispatch(nil, given_args.dup, nil, config) + rescue Foreman::Thor::Error => e + config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) + exit(1) if exit_on_failure? + rescue Errno::EPIPE + # This happens if a thor command is piped to something like `head`, + # which closes the pipe when it's done reading. This will also + # mean that if the pipe is closed, further unnecessary + # computation will not occur. + exit(0) + end + + # Allows to use private methods from parent in child classes as commands. + # + # ==== Parameters + # names:: Method names to be used as commands + # + # ==== Examples + # + # public_command :foo + # public_command :foo, :bar, :baz + # + def public_command(*names) + names.each do |name| + class_eval "def #{name}(*); super end" + end + end + alias_method :public_task, :public_command + + def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: + raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace + raise UndefinedCommandError, "Could not find command #{command.inspect}." + end + alias_method :handle_no_task_error, :handle_no_command_error + + def handle_argument_error(command, error, args, arity) #:nodoc: + msg = "ERROR: \"#{basename} #{command.name}\" was called with " + msg << "no arguments" if args.empty? + msg << "arguments " << args.inspect unless args.empty? + msg << "\nUsage: #{banner(command).inspect}" + raise InvocationError, msg + end + + protected + + # Prints the class options per group. If an option does not belong to + # any group, it's printed as Class option. + # + def class_options_help(shell, groups = {}) #:nodoc: + # Group options by group + class_options.each do |_, value| + groups[value.group] ||= [] + groups[value.group] << value + end + + # Deal with default group + global_options = groups.delete(nil) || [] + print_options(shell, global_options) + + # Print all others + groups.each do |group_name, options| + print_options(shell, options, group_name) + end + end + + # Receives a set of options and print them. + def print_options(shell, options, group_name = nil) + return if options.empty? + + list = [] + padding = options.map { |o| o.aliases.size }.max.to_i * 4 + + options.each do |option| + next if option.hide + item = [option.usage(padding)] + item.push(option.description ? "# #{option.description}" : "") + + list << item + list << ["", "# Default: #{option.default}"] if option.show_default? + list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum + end + + shell.say(group_name ? "#{group_name} options:" : "Options:") + shell.print_table(list, :indent => 2) + shell.say "" + end + + # Raises an error if the word given is a Foreman::Thor reserved word. + def is_thor_reserved_word?(word, type) #:nodoc: + return false unless THOR_RESERVED_WORDS.include?(word.to_s) + raise "#{word.inspect} is a Foreman::Thor reserved word and cannot be defined as #{type}" + end + + # Build an option and adds it to the given scope. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described in both class_option and method_option. + # scope:: Options hash that is being built up + def build_option(name, options, scope) #:nodoc: + scope[name] = Foreman::Thor::Option.new(name, options) + end + + # Receives a hash of options, parse them and add to the scope. This is a + # fast way to set a bunch of options: + # + # build_options :foo => true, :bar => :required, :baz => :string + # + # ==== Parameters + # Hash[Symbol => Object] + def build_options(options, scope) #:nodoc: + options.each do |key, value| + scope[key] = Foreman::Thor::Option.parse(key, value) + end + end + + # Finds a command with the given name. If the command belongs to the current + # class, just return it, otherwise dup it and add the fresh copy to the + # current command hash. + def find_and_refresh_command(name) #:nodoc: + if commands[name.to_s] + commands[name.to_s] + elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition + commands[name.to_s] = command.clone + else + raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found." + end + end + alias_method :find_and_refresh_task, :find_and_refresh_command + + # Everytime someone inherits from a Foreman::Thor class, register the klass + # and file into baseclass. + def inherited(klass) + Foreman::Thor::Base.register_klass_file(klass) + klass.instance_variable_set(:@no_commands, false) + end + + # Fire this callback whenever a method is added. Added methods are + # tracked as commands by invoking the create_command method. + def method_added(meth) + meth = meth.to_s + + if meth == "initialize" + initialize_added + return + end + + # Return if it's not a public instance method + return unless public_method_defined?(meth.to_sym) + + @no_commands ||= false + return if @no_commands || !create_command(meth) + + is_thor_reserved_word?(meth, :command) + Foreman::Thor::Base.register_klass_file(self) + end + + # Retrieves a value from superclass. If it reaches the baseclass, + # returns default. + def from_superclass(method, default = nil) + if self == baseclass || !superclass.respond_to?(method, true) + default + else + value = superclass.send(method) + + # Ruby implements `dup` on Object, but raises a `TypeError` + # if the method is called on immediates. As a result, we + # don't have a good way to check whether dup will succeed + # without calling it and rescuing the TypeError. + begin + value.dup + rescue TypeError + value + end + + end + end + + # A flag that makes the process exit with status 1 if any error happens. + def exit_on_failure? + false + end + + # + # The basename of the program invoking the thor class. + # + def basename + File.basename($PROGRAM_NAME).split(" ").first + end + + # SIGNATURE: Sets the baseclass. This is where the superclass lookup + # finishes. + def baseclass #:nodoc: + end + + # SIGNATURE: Creates a new command if valid_command? is true. This method is + # called when a new method is added to the class. + def create_command(meth) #:nodoc: + end + alias_method :create_task, :create_command + + # SIGNATURE: Defines behavior when the initialize method is added to the + # class. + def initialize_added #:nodoc: + end + + # SIGNATURE: The hook invoked by start. + def dispatch(command, given_args, given_opts, config) #:nodoc: + raise NotImplementedError + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/command.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/command.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/command.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/command.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,133 @@ +class Foreman::Thor + class Command < Struct.new(:name, :description, :long_description, :usage, :options, :disable_class_options) + FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/ + + def initialize(name, description, long_description, usage, options = nil, disable_class_options = false) + super(name.to_s, description, long_description, usage, options || {}, disable_class_options) + end + + def initialize_copy(other) #:nodoc: + super(other) + self.options = other.options.dup if other.options + end + + def hidden? + false + end + + # By default, a command invokes a method in the thor class. You can change this + # implementation to create custom commands. + def run(instance, args = []) + arity = nil + + if private_method?(instance) + instance.class.handle_no_command_error(name) + elsif public_method?(instance) + arity = instance.method(name).arity + instance.__send__(name, *args) + elsif local_method?(instance, :method_missing) + instance.__send__(:method_missing, name.to_sym, *args) + else + instance.class.handle_no_command_error(name) + end + rescue ArgumentError => e + handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) + rescue NoMethodError => e + handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e) + end + + # Returns the formatted usage by injecting given required arguments + # and required options into the given usage. + def formatted_usage(klass, namespace = true, subcommand = false) + if namespace + namespace = klass.namespace + formatted = "#{namespace.gsub(/^(default)/, '')}:" + end + formatted = "#{klass.namespace.split(':').last} " if subcommand + + formatted ||= "" + + # Add usage with required arguments + formatted << if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map(&:usage).compact.join(" ") + end + else + usage.to_s + end + + # Add required options + formatted << " #{required_options}" + + # Strip and go! + formatted.strip + end + + protected + + def not_debugging?(instance) + !(instance.class.respond_to?(:debugging) && instance.class.debugging) + end + + def required_options + @required_options ||= options.map { |_, o| o.usage if o.required? }.compact.sort.join(" ") + end + + # Given a target, checks if this class name is a public method. + def public_method?(instance) #:nodoc: + !(instance.public_methods & [name.to_s, name.to_sym]).empty? + end + + def private_method?(instance) + !(instance.private_methods & [name.to_s, name.to_sym]).empty? + end + + def local_method?(instance, name) + methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) + !(methods & [name.to_s, name.to_sym]).empty? + end + + def sans_backtrace(backtrace, caller) #:nodoc: + saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ %r{^kernel/} && RUBY_ENGINE =~ /rbx/) } + saned - caller + end + + def handle_argument_error?(instance, error, caller) + not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin + saned = sans_backtrace(error.backtrace, caller) + # Ruby 1.9 always include the called method in the backtrace + saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") + end + end + + def handle_no_method_error?(instance, error, caller) + not_debugging?(instance) && + error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ + end + end + Task = Command + + # A command that is hidden in help messages but still invocable. + class HiddenCommand < Command + def hidden? + true + end + end + HiddenTask = HiddenCommand + + # A dynamic command that handles method missing scenarios. + class DynamicCommand < Command + def initialize(name, options = nil) + super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options) + end + + def run(instance, args = []) + if (instance.methods & [name.to_s, name.to_sym]).empty? + super + else + instance.class.handle_no_command_error(name) + end + end + end + DynamicTask = DynamicCommand +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,85 @@ +class Foreman::Thor + module CoreExt #:nodoc: + # A hash with indifferent access and magic predicates. + # + # hash = Foreman::Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true + # + # hash[:foo] #=> 'bar' + # hash['foo'] #=> 'bar' + # hash.foo? #=> true + # + class HashWithIndifferentAccess < ::Hash #:nodoc: + def initialize(hash = {}) + super() + hash.each do |key, value| + self[convert_key(key)] = value + end + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def fetch(key, *args) + super(convert_key(key), *args) + end + + def key?(key) + super(convert_key(key)) + end + + def values_at(*indices) + indices.map { |key| self[convert_key(key)] } + end + + def merge(other) + dup.merge!(other) + end + + def merge!(other) + other.each do |key, value| + self[convert_key(key)] = value + end + self + end + + # Convert to a Hash with String keys. + def to_hash + Hash.new(default).merge!(self) + end + + protected + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + # Magic predicates. For instance: + # + # options.force? # => !!options['force'] + # options.shebang # => "/usr/lib/local/ruby" + # options.test_framework?(:rspec) # => options[:test_framework] == :rspec + # + def method_missing(method, *args) + method = method.to_s + if method =~ /^(\w+)\?$/ + if args.empty? + !!self[$1] + else + self[$1] == args.first + end + else + self[method] + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,12 @@ +class IO #:nodoc: + class << self + unless method_defined? :binread + def binread(file, *args) + raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 + File.open(file, "rb") do |f| + f.read(*args) + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,129 @@ +class Foreman::Thor + module CoreExt + class OrderedHash < ::Hash + if RUBY_VERSION < "1.9" + def initialize(*args, &block) + super + @keys = [] + end + + def initialize_copy(other) + super + # make a deep copy of keys + @keys = other.keys + end + + def []=(key, value) + @keys << key unless key?(key) + super + end + + def delete(key) + if key? key + index = @keys.index(key) + @keys.delete_at index + end + super + end + + def delete_if + super + sync_keys! + self + end + + alias_method :reject!, :delete_if + + def reject(&block) + dup.reject!(&block) + end + + def keys + @keys.dup + end + + def values + @keys.map { |key| self[key] } + end + + def to_hash + self + end + + def to_a + @keys.map { |key| [key, self[key]] } + end + + def each_key + return to_enum(:each_key) unless block_given? + @keys.each { |key| yield(key) } + self + end + + def each_value + return to_enum(:each_value) unless block_given? + @keys.each { |key| yield(self[key]) } + self + end + + def each + return to_enum(:each) unless block_given? + @keys.each { |key| yield([key, self[key]]) } + self + end + + def each_pair + return to_enum(:each_pair) unless block_given? + @keys.each { |key| yield(key, self[key]) } + self + end + + alias_method :select, :find_all + + def clear + super + @keys.clear + self + end + + def shift + k = @keys.first + v = delete(k) + [k, v] + end + + def merge!(other_hash) + if block_given? + other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } + else + other_hash.each { |k, v| self[k] = v } + end + self + end + + alias_method :update, :merge! + + def merge(other_hash, &block) + dup.merge!(other_hash, &block) + end + + # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. + def replace(other) + super + @keys = other.keys + self + end + + def inspect + "#<#{self.class} #{super}>" + end + + private + + def sync_keys! + @keys.delete_if { |k| !key?(k) } + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/error.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/error.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/error.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/error.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,32 @@ +class Foreman::Thor + # Foreman::Thor::Error is raised when it's caused by wrong usage of thor classes. Those + # errors have their backtrace suppressed and are nicely shown to the user. + # + # Errors that are caused by the developer, like declaring a method which + # overwrites a thor keyword, SHOULD NOT raise a Foreman::Thor::Error. This way, we + # ensure that developer errors are shown with full backtrace. + class Error < StandardError + end + + # Raised when a command was not found. + class UndefinedCommandError < Error + end + UndefinedTaskError = UndefinedCommandError + + class AmbiguousCommandError < Error + end + AmbiguousTaskError = AmbiguousCommandError + + # Raised when a command was found, but not invoked properly. + class InvocationError < Error + end + + class UnknownArgumentError < Error + end + + class RequiredArgumentMissingError < InvocationError + end + + class MalformattedArgumentError < InvocationError + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/group.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/group.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/group.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/group.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,281 @@ +require "foreman/vendor/thor/lib/thor/base" + +# Foreman::Thor has a special class called Foreman::Thor::Group. The main difference to Foreman::Thor class +# is that it invokes all commands at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Foreman::Thor +# commands. +class Foreman::Thor::Group + class << self + # The description for this Foreman::Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description:: The description for this Foreman::Thor::Group. + # + def desc(description = nil) + if description + @desc = description + else + @desc ||= from_superclass(:desc, nil) + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell) + shell.say "Usage:" + shell.say " #{banner}\n" + shell.say + class_options_help(shell) + shell.say desc if desc + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and command. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, true) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, '_')} + klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Foreman::Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional command. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is going to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + raise ArgumentError, "You have to define the option #{name.inspect} " \ + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, command = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, command, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_command(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, groups = {}) #:nodoc: + get_options_from_invocations(groups, class_options) do |klass| + klass.send(:get_options_from_invocations, groups, class_options) + end + super(shell, groups) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, _ = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |class_option| + base_options[class_option.name.to_sym].nil? && class_option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == class_option.name } + end + + yield klass if block_given? + end + end + + # Returns commands ready to be printed. + def printable_commands(*) + item = [] + item << banner + item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "") + [item] + end + alias_method :printable_tasks, :printable_commands + + def handle_argument_error(command, error, _args, arity) #:nodoc: + msg = "#{basename} #{command.name} takes #{arity} argument" + msg << "s" if arity > 1 + msg << ", but it should not." + raise error, msg + end + + protected + + # The method responsible for dispatching given the args. + def dispatch(command, given_args, given_opts, config) #:nodoc: + if Foreman::Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Foreman::Thor::Options.split(given_args) + opts = given_opts || opts + + instance = new(args, opts, config) + yield instance if block_given? + + if command + instance.invoke_command(all_commands[command]) + else + instance.invoke_all + end + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Foreman::Thor::Runner. + def banner + "#{basename} #{self_command.formatted_usage(self, false)}" + end + + # Represents the whole class as a command. + def self_command #:nodoc: + Foreman::Thor::DynamicCommand.new(namespace, class_options) + end + alias_method :self_task, :self_command + + def baseclass #:nodoc: + Foreman::Thor::Group + end + + def create_command(meth) #:nodoc: + commands[meth.to_s] = Foreman::Thor::Command.new(meth, nil, nil, nil, nil) + true + end + alias_method :create_task, :create_command + end + + include Foreman::Thor::Base + +protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc: + with_padding do + if block + case block.arity + when 3 + yield(self, klass, command) + when 2 + yield(self, klass) + when 1 + instance_exec(klass, &block) + end + else + invoke klass, command, *args + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/invocation.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/invocation.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/invocation.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/invocation.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,177 @@ +class Foreman::Thor + module Invocation + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # This method is responsible for receiving a name and find the proper + # class and command for it. The key is an optional parameter which is + # available only in class methods invocations (i.e. in Foreman::Thor::Group). + def prepare_for_invocation(key, name) #:nodoc: + case name + when Symbol, String + Foreman::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key) + else + name + end + end + end + + # Make initializer aware of invocations and the initialization args. + def initialize(args = [], options = {}, config = {}, &block) #:nodoc: + @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] } + @_initializer = [args, options, config] + super + end + + # Make the current command chain accessible with in a Foreman::Thor-(sub)command + def current_command_chain + @_invocations.values.flatten.map(&:to_sym) + end + + # Receives a name and invokes it. The name can be a string (either "command" or + # "namespace:command"), a Foreman::Thor::Command, a Class or a Foreman::Thor instance. If the + # command cannot be guessed by name, it can also be supplied as second argument. + # + # You can also supply the arguments, options and configuration values for + # the command to be invoked, if none is given, the same values used to + # initialize the invoker are used to initialize the invoked. + # + # When no name is given, it will invoke the default command of the current class. + # + # ==== Examples + # + # class A < Foreman::Thor + # def foo + # invoke :bar + # invoke "b:hello", ["Erik"] + # end + # + # def bar + # invoke "b:hello", ["Erik"] + # end + # end + # + # class B < Foreman::Thor + # def hello(name) + # puts "hello #{name}" + # end + # end + # + # You can notice that the method "foo" above invokes two commands: "bar", + # which belongs to the same class and "hello" which belongs to the class B. + # + # By using an invocation system you ensure that a command is invoked only once. + # In the example above, invoking "foo" will invoke "b:hello" just once, even + # if it's invoked later by "bar" method. + # + # When class A invokes class B, all arguments used on A initialization are + # supplied to B. This allows lazy parse of options. Let's suppose you have + # some rspec commands: + # + # class Rspec < Foreman::Thor::Group + # class_option :mock_framework, :type => :string, :default => :rr + # + # def invoke_mock_framework + # invoke "rspec:#{options[:mock_framework]}" + # end + # end + # + # As you noticed, it invokes the given mock framework, which might have its + # own options: + # + # class Rspec::RR < Foreman::Thor::Group + # class_option :style, :type => :string, :default => :mock + # end + # + # Since it's not rspec concern to parse mock framework options, when RR + # is invoked all options are parsed again, so RR can extract only the options + # that it's going to use. + # + # If you want Rspec::RR to be initialized with its own set of options, you + # have to do that explicitly: + # + # invoke "rspec:rr", [], :style => :foo + # + # Besides giving an instance, you can also give a class to invoke: + # + # invoke Rspec::RR, [], :style => :foo + # + def invoke(name = nil, *args) + if name.nil? + warn "[Foreman::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}" + return invoke_all + end + + args.unshift(nil) if args.first.is_a?(Array) || args.first.nil? + command, args, opts, config = args + + klass, command = _retrieve_class_and_command(name, command) + raise "Missing Foreman::Thor class for invoke #{name}" unless klass + raise "Expected Foreman::Thor class, got #{klass}" unless klass <= Foreman::Thor::Base + + args, opts, config = _parse_initialization_options(args, opts, config) + klass.send(:dispatch, command, args, opts, config) do |instance| + instance.parent_options = options + end + end + + # Invoke the given command if the given args. + def invoke_command(command, *args) #:nodoc: + current = @_invocations[self.class] + + unless current.include?(command.name) + current << command.name + command.run(self, *args) + end + end + alias_method :invoke_task, :invoke_command + + # Invoke all commands for the current instance. + def invoke_all #:nodoc: + self.class.all_commands.map { |_, command| invoke_command(command) } + end + + # Invokes using shell padding. + def invoke_with_padding(*args) + with_padding { invoke(*args) } + end + + protected + + # Configuration values that are shared between invocations. + def _shared_configuration #:nodoc: + {:invocations => @_invocations} + end + + # This method simply retrieves the class and command to be invoked. + # If the name is nil or the given name is a command in the current class, + # use the given name and return self as class. Otherwise, call + # prepare_for_invocation in the current class. + def _retrieve_class_and_command(name, sent_command = nil) #:nodoc: + if name.nil? + [self.class, nil] + elsif self.class.all_commands[name.to_s] + [self.class, name.to_s] + else + klass, command = self.class.prepare_for_invocation(nil, name) + [klass, command || sent_command] + end + end + alias_method :_retrieve_class_and_task, :_retrieve_class_and_command + + # Initialize klass using values stored in the @_initializer. + def _parse_initialization_options(args, opts, config) #:nodoc: + stored_args, stored_opts, stored_config = @_initializer + + args ||= stored_args.dup + opts ||= stored_opts.dup + + config ||= {} + config = stored_config.merge(_shared_configuration).merge!(config) + + [args, opts, config] + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,35 @@ +class Foreman::Thor + module LineEditor + class Basic + attr_reader :prompt, :options + + def self.available? + true + end + + def initialize(prompt, options) + @prompt = prompt + @options = options + end + + def readline + $stdout.print(prompt) + get_input + end + + private + + def get_input + if echo? + $stdin.gets + else + $stdin.noecho(&:gets) + end + end + + def echo? + options.fetch(:echo, true) + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,88 @@ +begin + require "readline" +rescue LoadError +end + +class Foreman::Thor + module LineEditor + class Readline < Basic + def self.available? + Object.const_defined?(:Readline) + end + + def readline + if echo? + ::Readline.completion_append_character = nil + # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil. + if complete = completion_proc + ::Readline.completion_proc = complete + end + ::Readline.readline(prompt, add_to_history?) + else + super + end + end + + private + + def add_to_history? + options.fetch(:add_to_history, true) + end + + def completion_proc + if use_path_completion? + proc { |text| PathCompletion.new(text).matches } + elsif completion_options.any? + proc do |text| + completion_options.select { |option| option.start_with?(text) } + end + end + end + + def completion_options + options.fetch(:limited_to, []) + end + + def use_path_completion? + options.fetch(:path, false) + end + + class PathCompletion + attr_reader :text + private :text + + def initialize(text) + @text = text + end + + def matches + relative_matches + end + + private + + def relative_matches + absolute_matches.map { |path| path.sub(base_path, "") } + end + + def absolute_matches + Dir[glob_pattern].map do |path| + if File.directory?(path) + "#{path}/" + else + path + end + end + end + + def glob_pattern + "#{base_path}#{text}*" + end + + def base_path + "#{Dir.pwd}/" + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/line_editor.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,17 @@ +require "foreman/vendor/thor/lib/thor/line_editor/basic" +require "foreman/vendor/thor/lib/thor/line_editor/readline" + +class Foreman::Thor + module LineEditor + def self.readline(prompt, options = {}) + best_available.new(prompt, options).readline + end + + def self.best_available + [ + Foreman::Thor::LineEditor::Readline, + Foreman::Thor::LineEditor::Basic + ].detect(&:available?) + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/argument.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/argument.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/argument.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/argument.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,70 @@ +class Foreman::Thor + class Argument #:nodoc: + VALID_TYPES = [:numeric, :hash, :array, :string] + + attr_reader :name, :description, :enum, :required, :type, :default, :banner + alias_method :human_name, :name + + def initialize(name, options = {}) + class_name = self.class.name.split("::").last + + type = options[:type] + + raise ArgumentError, "#{class_name} name can't be nil." if name.nil? + raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) + + @name = name.to_s + @description = options[:desc] + @required = options.key?(:required) ? options[:required] : true + @type = (type || :string).to_sym + @default = options[:default] + @banner = options[:banner] || default_banner + @enum = options[:enum] + + validate! # Trigger specific validations + end + + def usage + required? ? banner : "[#{banner}]" + end + + def required? + required + end + + def show_default? + case default + when Array, String, Hash + !default.empty? + else + default + end + end + + protected + + def validate! + raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil? + raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array) + end + + def valid_type?(type) + self.class::VALID_TYPES.include?(type.to_sym) + end + + def default_banner + case type + when :boolean + nil + when :string, :default + human_name.upcase + when :numeric + "N" + when :hash + "key:value" + when :array + "one two three" + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,175 @@ +class Foreman::Thor + class Arguments #:nodoc: # rubocop:disable ClassLength + NUMERIC = /[-+]?(\d*\.\d+|\d+)/ + + # Receives an array of args and returns two arrays, one with arguments + # and one with switches. + # + def self.split(args) + arguments = [] + + args.each do |item| + break if item =~ /^-/ + arguments << item + end + + [arguments, args[Range.new(arguments.size, -1)]] + end + + def self.parse(*args) + to_parse = args.pop + new(*args).parse(to_parse) + end + + # Takes an array of Foreman::Thor::Argument objects. + # + def initialize(arguments = []) + @assigns = {} + @non_assigned_required = [] + @switches = arguments + + arguments.each do |argument| + if !argument.default.nil? + @assigns[argument.human_name] = argument.default + elsif argument.required? + @non_assigned_required << argument + end + end + end + + def parse(args) + @pile = args.dup + + @switches.each do |argument| + break unless peek + @non_assigned_required.delete(argument) + @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) + end + + check_requirement! + @assigns + end + + def remaining + @pile + end + + private + + def no_or_skip?(arg) + arg =~ /^--(no|skip)-([-\w]+)$/ + $2 + end + + def last? + @pile.empty? + end + + def peek + @pile.first + end + + def shift + @pile.shift + end + + def unshift(arg) + if arg.is_a?(Array) + @pile = arg + @pile + else + @pile.unshift(arg) + end + end + + def current_is_value? + peek && peek.to_s !~ /^-/ + end + + # Runs through the argument array getting strings that contains ":" and + # mark it as a hash: + # + # [ "name:string", "age:integer" ] + # + # Becomes: + # + # { "name" => "string", "age" => "integer" } + # + def parse_hash(name) + return shift if peek.is_a?(Hash) + hash = {} + + while current_is_value? && peek.include?(":") + key, value = shift.split(":", 2) + raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key + hash[key] = value + end + hash + end + + # Runs through the argument array getting all strings until no string is + # found or a switch is found. + # + # ["a", "b", "c"] + # + # And returns it as an array: + # + # ["a", "b", "c"] + # + def parse_array(name) + return shift if peek.is_a?(Array) + array = [] + array << shift while current_is_value? + array + end + + # Check if the peek is numeric format and return a Float or Integer. + # Check if the peek is included in enum if enum is provided. + # Otherwise raises an error. + # + def parse_numeric(name) + return shift if peek.is_a?(Numeric) + + unless peek =~ NUMERIC && $& == peek + raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}" + end + + value = $&.index(".") ? shift.to_f : shift.to_i + if @switches.is_a?(Hash) && switch = @switches[name] + if switch.enum && !switch.enum.include?(value) + raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" + end + end + value + end + + # Parse string: + # for --string-arg, just return the current value in the pile + # for --no-string-arg, nil + # Check if the peek is included in enum if enum is provided. Otherwise raises an error. + # + def parse_string(name) + if no_or_skip?(name) + nil + else + value = shift + if @switches.is_a?(Hash) && switch = @switches[name] + if switch.enum && !switch.enum.include?(value) + raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" + end + end + value + end + end + + # Raises an error if @non_assigned_required array is not empty. + # + def check_requirement! + return if @non_assigned_required.empty? + names = @non_assigned_required.map do |o| + o.respond_to?(:switch_name) ? o.switch_name : o.human_name + end.join("', '") + class_name = self.class.name.split("::").last.downcase + raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'" + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/option.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/option.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/option.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/option.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,146 @@ +class Foreman::Thor + class Option < Argument #:nodoc: + attr_reader :aliases, :group, :lazy_default, :hide + + VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] + + def initialize(name, options = {}) + options[:required] = false unless options.key?(:required) + super + @lazy_default = options[:lazy_default] + @group = options[:group].to_s.capitalize if options[:group] + @aliases = Array(options[:aliases]) + @hide = options[:hide] + end + + # This parse quick options given as method_options. It makes several + # assumptions, but you can be more specific using the option method. + # + # parse :foo => "bar" + # #=> Option foo with default value bar + # + # parse [:foo, :baz] => "bar" + # #=> Option foo with default value bar and alias :baz + # + # parse :foo => :required + # #=> Required option foo without default value + # + # parse :foo => 2 + # #=> Option foo with default value 2 and type numeric + # + # parse :foo => :numeric + # #=> Option foo without default value and type numeric + # + # parse :foo => true + # #=> Option foo with default value true and type boolean + # + # The valid types are :boolean, :numeric, :hash, :array and :string. If none + # is given a default type is assumed. This default type accepts arguments as + # string (--foo=value) or booleans (just --foo). + # + # By default all options are optional, unless :required is given. + # + def self.parse(key, value) + if key.is_a?(Array) + name, *aliases = key + else + name = key + aliases = [] + end + + name = name.to_s + default = value + + type = case value + when Symbol + default = nil + if VALID_TYPES.include?(value) + value + elsif required = (value == :required) # rubocop:disable AssignmentInCondition + :string + end + when TrueClass, FalseClass + :boolean + when Numeric + :numeric + when Hash, Array, String + value.class.name.downcase.to_sym + end + + new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases) + end + + def switch_name + @switch_name ||= dasherized? ? name : dasherize(name) + end + + def human_name + @human_name ||= dasherized? ? undasherize(name) : name + end + + def usage(padding = 0) + sample = if banner && !banner.to_s.empty? + "#{switch_name}=#{banner}" + else + switch_name + end + + sample = "[#{sample}]" unless required? + + if boolean? + sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-") + end + + if aliases.empty? + (" " * padding) << sample + else + "#{aliases.join(', ')}, #{sample}" + end + end + + VALID_TYPES.each do |type| + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{type}? + self.type == #{type.inspect} + end + RUBY + end + + protected + + def validate! + raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? + validate_default_type! + end + + def validate_default_type! + default_type = case @default + when nil + return + when TrueClass, FalseClass + required? ? :string : :boolean + when Numeric + :numeric + when Symbol + :string + when Hash, Array, String + @default.class.name.downcase.to_sym + end + + # TODO: This should raise an ArgumentError in a future version of Foreman::Thor + warn "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type + end + + def dasherized? + name.index("-") == 0 + end + + def undasherize(str) + str.sub(/^-{1,2}/, "") + end + + def dasherize(str) + (str.length > 1 ? "--" : "-") + str.tr("_", "-") + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/options.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/options.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser/options.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/options.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,220 @@ +class Foreman::Thor + class Options < Arguments #:nodoc: # rubocop:disable ClassLength + LONG_RE = /^(--\w+(?:-\w+)*)$/ + SHORT_RE = /^(-[a-z])$/i + EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i + SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args + SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i + OPTS_END = "--".freeze + + # Receives a hash and makes it switches. + def self.to_switches(options) + options.map do |key, value| + case value + when true + "--#{key}" + when Array + "--#{key} #{value.map(&:inspect).join(' ')}" + when Hash + "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}" + when nil, false + "" + else + "--#{key} #{value.inspect}" + end + end.join(" ") + end + + # Takes a hash of Foreman::Thor::Option and a hash with defaults. + # + # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters + # an unknown option or a regular argument. + def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false) + @stop_on_unknown = stop_on_unknown + options = hash_options.values + super(options) + + # Add defaults + defaults.each do |key, value| + @assigns[key.to_s] = value + @non_assigned_required.delete(hash_options[key]) + end + + @shorts = {} + @switches = {} + @extra = [] + + options.each do |option| + @switches[option.switch_name] = option + + option.aliases.each do |short| + name = short.to_s.sub(/^(?!\-)/, "-") + @shorts[name] ||= option.switch_name + end + end + end + + def remaining + @extra + end + + def peek + return super unless @parsing_options + + result = super + if result == OPTS_END + shift + @parsing_options = false + super + else + result + end + end + + def parse(args) # rubocop:disable MethodLength + @pile = args.dup + @parsing_options = true + + while peek + if parsing_options? + match, is_switch = current_is_switch? + shifted = shift + + if is_switch + case shifted + when SHORT_SQ_RE + unshift($1.split("").map { |f| "-#{f}" }) + next + when EQ_RE, SHORT_NUM + unshift($2) + switch = $1 + when LONG_RE, SHORT_RE + switch = $1 + end + + switch = normalize_switch(switch) + option = switch_option(switch) + @assigns[option.human_name] = parse_peek(switch, option) + elsif @stop_on_unknown + @parsing_options = false + @extra << shifted + @extra << shift while peek + break + elsif match + @extra << shifted + @extra << shift while peek && peek !~ /^-/ + else + @extra << shifted + end + else + @extra << shift + end + end + + check_requirement! + + assigns = Foreman::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns) + assigns.freeze + assigns + end + + def check_unknown! + # an unknown option starts with - or -- and has no more --'s afterward. + unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } + raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? + end + + protected + + # Check if the current value in peek is a registered switch. + # + # Two booleans are returned. The first is true if the current value + # starts with a hyphen; the second is true if it is a registered switch. + def current_is_switch? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + [true, switch?($1)] + when SHORT_SQ_RE + [true, $1.split("").any? { |f| switch?("-#{f}") }] + else + [false, false] + end + end + + def current_is_switch_formatted? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE + true + else + false + end + end + + def current_is_value? + peek && (!parsing_options? || super) + end + + def switch?(arg) + switch_option(normalize_switch(arg)) + end + + def switch_option(arg) + if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition + @switches[arg] || @switches["--#{match}"] + else + @switches[arg] + end + end + + # Check if the given argument is actually a shortcut. + # + def normalize_switch(arg) + (@shorts[arg] || arg).tr("_", "-") + end + + def parsing_options? + peek + @parsing_options + end + + # Parse boolean values which can be given as --foo=true, --foo or --no-foo. + # + def parse_boolean(switch) + if current_is_value? + if ["true", "TRUE", "t", "T", true].include?(peek) + shift + true + elsif ["false", "FALSE", "f", "F", false].include?(peek) + shift + false + else + true + end + else + @switches.key?(switch) || !no_or_skip?(switch) + end + end + + # Parse the value at the peek analyzing if it requires an input or not. + # + def parse_peek(switch, option) + if parsing_options? && (current_is_switch_formatted? || last?) + if option.boolean? + # No problem for boolean types + elsif no_or_skip?(switch) + return nil # User set value to nil + elsif option.string? && !option.required? + # Return the default if there is one, else the human name + return option.lazy_default || option.default || option.human_name + elsif option.lazy_default + return option.lazy_default + else + raise MalformattedArgumentError, "No value provided for option '#{switch}'" + end + end + + @non_assigned_required.delete(option) + send(:"parse_#{option.type}", switch) + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/parser.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,4 @@ +require "foreman/vendor/thor/lib/thor/parser/argument" +require "foreman/vendor/thor/lib/thor/parser/arguments" +require "foreman/vendor/thor/lib/thor/parser/option" +require "foreman/vendor/thor/lib/thor/parser/options" diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/rake_compat.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/rake_compat.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/rake_compat.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/rake_compat.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,71 @@ +require "rake" +require "rake/dsl_definition" + +class Foreman::Thor + # Adds a compatibility layer to your Foreman::Thor classes which allows you to use + # rake package tasks. For example, to use rspec rake tasks, one can do: + # + # require 'foreman/vendor/thor/lib/thor/rake_compat' + # require 'rspec/core/rake_task' + # + # class Default < Foreman::Thor + # include Foreman::Thor::RakeCompat + # + # RSpec::Core::RakeTask.new(:spec) do |t| + # t.spec_opts = ['--options', './.rspec'] + # t.spec_files = FileList['spec/**/*_spec.rb'] + # end + # end + # + module RakeCompat + include Rake::DSL if defined?(Rake::DSL) + + def self.rake_classes + @rake_classes ||= [] + end + + def self.included(base) + # Hack. Make rakefile point to invoker, so rdoc task is generated properly. + rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) + Rake.application.instance_variable_set(:@rakefile, rakefile) + rake_classes << base + end + end +end + +# override task on (main), for compatibility with Rake 0.9 +instance_eval do + alias rake_namespace namespace + + def task(*) + task = super + + if klass = Foreman::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition + non_namespaced_name = task.name.split(":").last + + description = non_namespaced_name + description << task.arg_names.map { |n| n.to_s.upcase }.join(" ") + description.strip! + + klass.desc description, Rake.application.last_description || non_namespaced_name + Rake.application.last_description = nil + klass.send :define_method, non_namespaced_name do |*args| + Rake::Task[task.name.to_sym].invoke(*args) + end + end + + task + end + + def namespace(name) + if klass = Foreman::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition + const_name = Foreman::Thor::Util.camel_case(name.to_s).to_sym + klass.const_set(const_name, Class.new(Foreman::Thor)) + new_klass = klass.const_get(const_name) + Foreman::Thor::RakeCompat.rake_classes << new_klass + end + + super + Foreman::Thor::RakeCompat.rake_classes.pop + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/runner.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/runner.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/runner.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/runner.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,322 @@ +require "foreman/vendor/thor/lib/thor" +require "foreman/vendor/thor/lib/thor/group" +require "foreman/vendor/thor/lib/thor/core_ext/io_binary_read" + +require "fileutils" +require "open-uri" +require "yaml" +require "digest/md5" +require "pathname" + +class Foreman::Thor::Runner < Foreman::Thor #:nodoc: # rubocop:disable ClassLength + map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version + + def self.banner(command, all = false, subcommand = false) + "thor " + command.formatted_usage(self, all, subcommand) + end + + def self.exit_on_failure? + true + end + + # Override Foreman::Thor#help so it can give information about any class and any method. + # + def help(meth = nil) + if meth && !respond_to?(meth) + initialize_thorfiles(meth) + klass, command = Foreman::Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + klass.start(["-h", command].compact, :shell => shell) + else + super + end + end + + # If a command is not found on Foreman::Thor::Runner, method missing is invoked and + # Foreman::Thor::Runner is then responsible for finding the command in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, command = Foreman::Thor::Util.find_class_and_command_by_namespace(meth) + self.class.handle_no_command_error(command, false) if klass.nil? + args.unshift(command) if command + klass.start(args, :shell => shell) + end + + desc "install NAME", "Install an optionally named Foreman::Thor file into your system commands" + method_options :as => :string, :relative => :boolean, :force => :boolean + def install(name) # rubocop:disable MethodLength + initialize_thorfiles + + # If a directory name is provided as the argument, look for a 'main.thor' + # command in said directory. + begin + if File.directory?(File.expand_path(name)) + base = File.join(name, "main.thor") + package = :directory + contents = open(base, &:read) + else + base = name + package = :file + contents = open(name, &:read) + end + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + rescue Errno::ENOENT + raise Error, "Error opening file '#{name}'" + end + + say "Your Foreman::Thorfile contains:" + say contents + + unless options["force"] + return false if no?("Do you wish to continue [y/N]?") + end + + as = options["as"] || begin + first_line = contents.split("\n")[0] + (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil + end + + unless as + basename = File.basename(name) + as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") + as = basename if as.empty? + end + + location = if options[:relative] || name =~ %r{^https?://} + name + else + File.expand_path(name) + end + + thor_yaml[as] = { + :filename => Digest::MD5.hexdigest(name + as), + :location => location, + :namespaces => Foreman::Thor::Util.namespaces_in_content(contents, base) + } + + save_yaml(thor_yaml) + say "Storing thor file in your system repository" + destination = File.join(thor_root, thor_yaml[as][:filename]) + + if package == :file + File.open(destination, "w") { |f| f.puts contents } + else + FileUtils.cp_r(name, destination) + end + + thor_yaml[as][:filename] # Indicate success + end + + desc "version", "Show Foreman::Thor version" + def version + require "foreman/vendor/thor/lib/thor/version" + say "Foreman::Thor #{Foreman::Thor::VERSION}" + end + + desc "uninstall NAME", "Uninstall a named Foreman::Thor module" + def uninstall(name) + raise Error, "Can't find module '#{name}'" unless thor_yaml[name] + say "Uninstalling #{name}." + FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s)) + + thor_yaml.delete(name) + save_yaml(thor_yaml) + + puts "Done." + end + + desc "update NAME", "Update a Foreman::Thor file from its original location" + def update(name) + raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] + + say "Updating '#{name}' from #{thor_yaml[name][:location]}" + + old_filename = thor_yaml[name][:filename] + self.options = options.merge("as" => name) + + if File.directory? File.expand_path(name) + FileUtils.rm_rf(File.join(thor_root, old_filename)) + + thor_yaml.delete(old_filename) + save_yaml(thor_yaml) + + filename = install(name) + else + filename = install(thor_yaml[name][:location]) + end + + File.delete(File.join(thor_root, old_filename)) unless filename == old_filename + end + + desc "installed", "List the installed Foreman::Thor modules and commands" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + display_klasses(true, options["internal"]) + end + + desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)" + method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean + def list(search = "") + initialize_thorfiles + + search = ".*#{search}" if options["substring"] + search = /^#{search}.*/i + group = options[:group] || "standard" + + klasses = Foreman::Thor::Base.subclasses.select do |k| + (options[:all] || k.group == group) && k.namespace =~ search + end + + display_klasses(false, false, klasses) + end + +private + + def thor_root + Foreman::Thor::Util.thor_root + end + + def thor_yaml + @thor_yaml ||= begin + yaml_file = File.join(thor_root, "thor.yml") + yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file) + yaml || {} + end + end + + # Save the yaml file. If none exists in thor root, creates one. + # + def save_yaml(yaml) + yaml_file = File.join(thor_root, "thor.yml") + + unless File.exist?(yaml_file) + FileUtils.mkdir_p(thor_root) + yaml_file = File.join(thor_root, "thor.yml") + FileUtils.touch(yaml_file) + end + + File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } + end + + # Load the Foreman::Thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Foreman::Thor files, as + # described in thorfiles. This look up can be skipped by supplying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to = nil, skip_lookup = false) + thorfiles(relevant_to, skip_lookup).each do |f| + Foreman::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Foreman::Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Foreman::Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Foreman::Thor file, we stop. + # + # We also ensure that system-wide Foreman::Thorfiles are loaded first, so local + # Foreman::Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Foreman::Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Foreman::Thorfiles found! + # + def thorfiles(relevant_to = nil, skip_lookup = false) + thorfiles = [] + + unless skip_lookup + Pathname.pwd.ascend do |path| + thorfiles = Foreman::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten + break unless thorfiles.empty? + end + end + + files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Foreman::Thor::Util.thor_root_glob) + files += thorfiles + files -= ["#{thor_root}/thor.yml"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Load Foreman::Thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + def thorfiles_relevant_to(meth) + lookup = [meth, meth.split(":")[0...-1].join(":")] + + files = thor_yaml.select do |_, v| + v[:namespaces] && !(v[:namespaces] & lookup).empty? + end + + files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) } + end + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + def display_klasses(with_modules = false, show_internal = false, klasses = Foreman::Thor::Base.subclasses) + klasses -= [Foreman::Thor, Foreman::Thor::Runner, Foreman::Thor::Group] unless show_internal + + raise Error, "No Foreman::Thor commands available" if klasses.empty? + show_modules if with_modules && !thor_yaml.empty? + + list = Hash.new { |h, k| h[k] = [] } + groups = klasses.select { |k| k.ancestors.include?(Foreman::Thor::Group) } + + # Get classes which inherit from Foreman::Thor + (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) } + + # Get classes which inherit from Foreman::Thor::Base + groups.map! { |k| k.printable_commands(false).first } + list["root"] = groups + + # Order namespaces with default coming first + list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") } + list.each { |n, commands| display_commands(n, commands) unless commands.empty? } + end + + def display_commands(namespace, list) #:nodoc: + list.sort! { |a, b| a[0] <=> b[0] } + + say shell.set_color(namespace, :blue, true) + say "-" * namespace.size + + print_table(list, :truncate => true) + say + end + alias_method :display_tasks, :display_commands + + def show_modules #:nodoc: + info = [] + labels = %w(Modules Namespaces) + + info << labels + info << ["-" * labels[0].size, "-" * labels[1].size] + + thor_yaml.each do |name, hash| + info << [name, hash[:namespaces].join(", ")] + end + + print_table info + say "" + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/basic.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/basic.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/basic.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/basic.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,436 @@ +require "tempfile" +require "io/console" if RUBY_VERSION > "1.9.2" + +class Foreman::Thor + module Shell + class Basic + attr_accessor :base + attr_reader :padding + + # Initialize base, mute and padding to nil. + # + def initialize #:nodoc: + @base = nil + @mute = false + @padding = 0 + @always_force = false + end + + # Mute everything that's inside given block + # + def mute + @mute = true + yield + ensure + @mute = false + end + + # Check if base is muted + # + def mute? + @mute + end + + # Sets the output padding, not allowing less than zero values. + # + def padding=(value) + @padding = [0, value].max + end + + # Sets the output padding while executing a block and resets it. + # + def indent(count = 1) + orig_padding = padding + self.padding = padding + count + yield + self.padding = orig_padding + end + + # Asks something to the user and receives a response. + # + # If asked to limit the correct responses, you can pass in an + # array of acceptable answers. If one of those is not supplied, + # they will be shown a message stating that one of those answers + # must be given and re-asked the question. + # + # If asking for sensitive information, the :echo option can be set + # to false to mask user input from $stdin. + # + # If the required input is a path, then set the path option to + # true. This will enable tab completion for file paths relative + # to the current working directory on systems that support + # Readline. + # + # ==== Example + # ask("What is your name?") + # + # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) + # + # ask("What is your password?", :echo => false) + # + # ask("Where should the file be saved?", :path => true) + # + def ask(statement, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + color = args.first + + if options[:limited_to] + ask_filtered(statement, color, options) + else + ask_simply(statement, color, options) + end + end + + # Say (print) something to the user. If the sentence ends with a whitespace + # or tab character, a new line is not appended (print + flush). Otherwise + # are passed straight to puts (behavior got from Highline). + # + # ==== Example + # say("I know you knew that.") + # + def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/)) + buffer = prepare_message(message, *color) + buffer << "\n" if force_new_line && !message.to_s.end_with?("\n") + + stdout.print(buffer) + stdout.flush + end + + # Say a status with the given color and appends the message. Since this + # method is used frequently by actions, it allows nil or false to be given + # in log_status, avoiding the message from being shown. If a Symbol is + # given in log_status, it's used as the color. + # + def say_status(status, message, log_status = true) + return if quiet? || log_status == false + spaces = " " * (padding + 1) + color = log_status.is_a?(Symbol) ? log_status : :green + + status = status.to_s.rjust(12) + status = set_color status, color, true if color + + buffer = "#{status}#{spaces}#{message}" + buffer << "\n" unless buffer.end_with?("\n") + + stdout.print(buffer) + stdout.flush + end + + # Make a question the to user and returns true if the user replies "y" or + # "yes". + # + def yes?(statement, color = nil) + !!(ask(statement, color, :add_to_history => false) =~ is?(:yes)) + end + + # Make a question the to user and returns true if the user replies "n" or + # "no". + # + def no?(statement, color = nil) + !!(ask(statement, color, :add_to_history => false) =~ is?(:no)) + end + + # Prints values in columns + # + # ==== Parameters + # Array[String, String, ...] + # + def print_in_columns(array) + return if array.empty? + colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2 + array.each_with_index do |value, index| + # Don't output trailing spaces when printing the last column + if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length + stdout.puts value + else + stdout.printf("%-#{colwidth}s", value) + end + end + end + + # Prints a table. + # + # ==== Parameters + # Array[Array[String, String, ...]] + # + # ==== Options + # indent:: Indent the first column by indent value. + # colwidth:: Force the first column to colwidth spaces wide. + # + def print_table(array, options = {}) # rubocop:disable MethodLength + return if array.empty? + + formats = [] + indent = options[:indent].to_i + colwidth = options[:colwidth] + options[:truncate] = terminal_width if options[:truncate] == true + + formats << "%-#{colwidth + 2}s" if colwidth + start = colwidth ? 1 : 0 + + colcount = array.max { |a, b| a.size <=> b.size }.size + + maximas = [] + + start.upto(colcount - 1) do |index| + maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max + maximas << maxima + formats << if index == colcount - 1 + # Don't output 2 trailing spaces when printing the last column + "%-s" + else + "%-#{maxima + 2}s" + end + end + + formats[0] = formats[0].insert(0, " " * indent) + formats << "%s" + + array.each do |row| + sentence = "" + + row.each_with_index do |column, index| + maxima = maximas[index] + + f = if column.is_a?(Numeric) + if index == row.size - 1 + # Don't output 2 trailing spaces when printing the last column + "%#{maxima}s" + else + "%#{maxima}s " + end + else + formats[index] + end + sentence << f % column.to_s + end + + sentence = truncate(sentence, options[:truncate]) if options[:truncate] + stdout.puts sentence + end + end + + # Prints a long string, word-wrapping the text to the current width of the + # terminal display. Ideal for printing heredocs. + # + # ==== Parameters + # String + # + # ==== Options + # indent:: Indent each line of the printed paragraph by indent value. + # + def print_wrapped(message, options = {}) + indent = options[:indent] || 0 + width = terminal_width - indent + paras = message.split("\n\n") + + paras.map! do |unwrapped| + unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } + end + + paras.each do |para| + para.split("\n").each do |line| + stdout.puts line.insert(0, " " * indent) + end + stdout.puts unless para == paras.last + end + end + + # Deals with file collision and returns true if the file should be + # overwritten and false otherwise. If a block is given, it uses the block + # response as the content for the diff. + # + # ==== Parameters + # destination:: the destination file to solve conflicts + # block:: an optional block that returns the value to be used in diff + # + def file_collision(destination) + return true if @always_force + options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + + loop do + answer = ask( + %[Overwrite #{destination}? (enter "h" for help) #{options}], + :add_to_history => false + ) + + case answer + when is?(:yes), is?(:force), "" + return true + when is?(:no), is?(:skip) + return false + when is?(:always) + return @always_force = true + when is?(:quit) + say "Aborting..." + raise SystemExit + when is?(:diff) + show_diff(destination, yield) if block_given? + say "Retrying..." + else + say file_collision_help + end + end + end + + # This code was copied from Rake, available under MIT-LICENSE + # Copyright (c) 2003, 2004 Jim Weirich + def terminal_width + result = if ENV["THOR_COLUMNS"] + ENV["THOR_COLUMNS"].to_i + else + unix? ? dynamic_width : 80 + end + result < 10 ? 80 : result + rescue + 80 + end + + # Called if something goes wrong during the execution. This is used by Foreman::Thor + # internally and should not be used inside your scripts. If something went + # wrong, you can always raise an exception. If you raise a Foreman::Thor::Error, it + # will be rescued and wrapped in the method below. + # + def error(statement) + stderr.puts statement + end + + # Apply color to the given string with optional bold. Disabled in the + # Foreman::Thor::Shell::Basic class. + # + def set_color(string, *) #:nodoc: + string + end + + protected + + def prepare_message(message, *color) + spaces = " " * padding + spaces + set_color(message.to_s, *color) + end + + def can_display_colors? + false + end + + def lookup_color(color) + return color unless color.is_a?(Symbol) + self.class.const_get(color.to_s.upcase) + end + + def stdout + $stdout + end + + def stderr + $stderr + end + + def is?(value) #:nodoc: + value = value.to_s + + if value.size == 1 + /\A#{value}\z/i + else + /\A(#{value}|#{value[0, 1]})\z/i + end + end + + def file_collision_help #:nodoc: + <<-HELP + Y - yes, overwrite + n - no, do not overwrite + a - all, overwrite this and all others + q - quit, abort + d - diff, show the differences between the old and the new + h - help, show this help + HELP + end + + def show_diff(destination, content) #:nodoc: + diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" + + Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| + temp.write content + temp.rewind + system %(#{diff_cmd} "#{destination}" "#{temp.path}") + end + end + + def quiet? #:nodoc: + mute? || (base && base.options[:quiet]) + end + + # Calculate the dynamic width of the terminal + def dynamic_width + @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) + end + + def dynamic_width_stty + `stty size 2>/dev/null`.split[1].to_i + end + + def dynamic_width_tput + `tput cols 2>/dev/null`.to_i + end + + def unix? + RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def truncate(string, width) + as_unicode do + chars = string.chars.to_a + if chars.length <= width + chars.join + else + chars[0, width - 3].join + "..." + end + end + end + + if "".respond_to?(:encode) + def as_unicode + yield + end + else + def as_unicode + old = $KCODE + $KCODE = "U" + yield + ensure + $KCODE = old + end + end + + def ask_simply(statement, color, options) + default = options[:default] + message = [statement, ("(#{default})" if default), nil].uniq.join(" ") + message = prepare_message(message, *color) + result = Foreman::Thor::LineEditor.readline(message, options) + + return unless result + + result.strip! + + if default && result == "" + default + else + result + end + end + + def ask_filtered(statement, color, options) + answer_set = options[:limited_to] + correct_answer = nil + until correct_answer + answers = answer_set.join(", ") + answer = ask_simply("#{statement} [#{answers}]", color, options) + correct_answer = answer_set.include?(answer) ? answer : nil + say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer + end + correct_answer + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/color.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/color.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/color.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/color.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,149 @@ +require "foreman/vendor/thor/lib/thor/shell/basic" + +class Foreman::Thor + module Shell + # Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check + # Foreman::Thor::Shell::Basic to see all available methods. + # + class Color < Basic + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + # The start of an ANSI bold sequence. + BOLD = "\e[1m" + + # Set the terminal's foreground ANSI color to black. + BLACK = "\e[30m" + # Set the terminal's foreground ANSI color to red. + RED = "\e[31m" + # Set the terminal's foreground ANSI color to green. + GREEN = "\e[32m" + # Set the terminal's foreground ANSI color to yellow. + YELLOW = "\e[33m" + # Set the terminal's foreground ANSI color to blue. + BLUE = "\e[34m" + # Set the terminal's foreground ANSI color to magenta. + MAGENTA = "\e[35m" + # Set the terminal's foreground ANSI color to cyan. + CYAN = "\e[36m" + # Set the terminal's foreground ANSI color to white. + WHITE = "\e[37m" + + # Set the terminal's background ANSI color to black. + ON_BLACK = "\e[40m" + # Set the terminal's background ANSI color to red. + ON_RED = "\e[41m" + # Set the terminal's background ANSI color to green. + ON_GREEN = "\e[42m" + # Set the terminal's background ANSI color to yellow. + ON_YELLOW = "\e[43m" + # Set the terminal's background ANSI color to blue. + ON_BLUE = "\e[44m" + # Set the terminal's background ANSI color to magenta. + ON_MAGENTA = "\e[45m" + # Set the terminal's background ANSI color to cyan. + ON_CYAN = "\e[46m" + # Set the terminal's background ANSI color to white. + ON_WHITE = "\e[47m" + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + # Pass foreground, background and bold options to this method as + # symbols. + # + # Example: + # + # set_color "Hi!", :red, :on_white, :bold + # + # The available colors are: + # + # :bold + # :black + # :red + # :green + # :yellow + # :blue + # :magenta + # :cyan + # :white + # :on_black + # :on_red + # :on_green + # :on_yellow + # :on_blue + # :on_magenta + # :on_cyan + # :on_white + def set_color(string, *colors) + if colors.compact.empty? || !can_display_colors? + string + elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + ansi_colors = colors.map { |color| lookup_color(color) } + "#{ansi_colors.join}#{string}#{CLEAR}" + else + # The old API was `set_color(color, bold=boolean)`. We + # continue to support the old API because you should never + # break old APIs unnecessarily :P + foreground, bold = colors + foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol) + + bold = bold ? BOLD : "" + "#{bold}#{foreground}#{string}#{CLEAR}" + end + end + + protected + + def can_display_colors? + stdout.tty? + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when "-" + say "- #{diff.old_element.chomp}", :red, true + when "+" + say "+ #{diff.new_element.chomp}", :green, true + when "!" + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require "diff/lcs" + true + rescue LoadError + false + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/html.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/html.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell/html.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/html.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,126 @@ +require "foreman/vendor/thor/lib/thor/shell/basic" + +class Foreman::Thor + module Shell + # Inherit from Foreman::Thor::Shell::Basic and add set_color behavior. Check + # Foreman::Thor::Shell::Basic to see all available methods. + # + class HTML < Basic + # The start of an HTML bold sequence. + BOLD = "font-weight: bold" + + # Set the terminal's foreground HTML color to black. + BLACK = "color: black" + # Set the terminal's foreground HTML color to red. + RED = "color: red" + # Set the terminal's foreground HTML color to green. + GREEN = "color: green" + # Set the terminal's foreground HTML color to yellow. + YELLOW = "color: yellow" + # Set the terminal's foreground HTML color to blue. + BLUE = "color: blue" + # Set the terminal's foreground HTML color to magenta. + MAGENTA = "color: magenta" + # Set the terminal's foreground HTML color to cyan. + CYAN = "color: cyan" + # Set the terminal's foreground HTML color to white. + WHITE = "color: white" + + # Set the terminal's background HTML color to black. + ON_BLACK = "background-color: black" + # Set the terminal's background HTML color to red. + ON_RED = "background-color: red" + # Set the terminal's background HTML color to green. + ON_GREEN = "background-color: green" + # Set the terminal's background HTML color to yellow. + ON_YELLOW = "background-color: yellow" + # Set the terminal's background HTML color to blue. + ON_BLUE = "background-color: blue" + # Set the terminal's background HTML color to magenta. + ON_MAGENTA = "background-color: magenta" + # Set the terminal's background HTML color to cyan. + ON_CYAN = "background-color: cyan" + # Set the terminal's background HTML color to white. + ON_WHITE = "background-color: white" + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + def set_color(string, *colors) + if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } + html_colors = colors.map { |color| lookup_color(color) } + "#{string}" + else + color, bold = colors + html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + styles = [html_color] + styles << BOLD if bold + "#{string}" + end + end + + # Ask something to the user and receives a response. + # + # ==== Example + # ask("What is your name?") + # + # TODO: Implement #ask for Foreman::Thor::Shell::HTML + def ask(statement, color = nil) + raise NotImplementedError, "Implement #ask for Foreman::Thor::Shell::HTML" + end + + protected + + def can_display_colors? + true + end + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) #:nodoc: + if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? + actual = File.binread(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) #:nodoc: + case diff.action + when "-" + say "- #{diff.old_element.chomp}", :red, true + when "+" + say "+ #{diff.new_element.chomp}", :green, true + when "!" + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? #:nodoc: + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require "diff/lcs" + true + rescue LoadError + false + end + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/shell.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,81 @@ +require "rbconfig" + +class Foreman::Thor + module Base + class << self + attr_writer :shell + + # Returns the shell used in all Foreman::Thor classes. If you are in a Unix platform + # it will use a colored log, otherwise it will use a basic one without color. + # + def shell + @shell ||= if ENV["THOR_SHELL"] && !ENV["THOR_SHELL"].empty? + Foreman::Thor::Shell.const_get(ENV["THOR_SHELL"]) + elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"] + Foreman::Thor::Shell::Basic + else + Foreman::Thor::Shell::Color + end + end + end + end + + module Shell + SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] + attr_writer :shell + + autoload :Basic, "foreman/vendor/thor/lib/thor/shell/basic" + autoload :Color, "foreman/vendor/thor/lib/thor/shell/color" + autoload :HTML, "foreman/vendor/thor/lib/thor/shell/html" + + # Add shell to initialize config values. + # + # ==== Configuration + # shell:: An instance of the shell to be used. + # + # ==== Examples + # + # class MyScript < Foreman::Thor + # argument :first, :type => :numeric + # end + # + # MyScript.new [1.0], { :foo => :bar }, :shell => Foreman::Thor::Shell::Basic.new + # + def initialize(args = [], options = {}, config = {}) + super + self.shell = config[:shell] + shell.base ||= self if shell.respond_to?(:base) + end + + # Holds the shell for the given Foreman::Thor instance. If no shell is given, + # it gets a default shell from Foreman::Thor::Base.shell. + def shell + @shell ||= Foreman::Thor::Base.shell.new + end + + # Common methods that are delegated to the shell. + SHELL_DELEGATED_METHODS.each do |method| + module_eval <<-METHOD, __FILE__, __LINE__ + def #{method}(*args,&block) + shell.#{method}(*args,&block) + end + METHOD + end + + # Yields the given block with padding. + def with_padding + shell.padding += 1 + yield + ensure + shell.padding -= 1 + end + + protected + + # Allow shell to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:shell => shell) + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/util.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/util.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/util.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/util.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,268 @@ +require "rbconfig" + +class Foreman::Thor + module Sandbox #:nodoc: + end + + # This module holds several utilities: + # + # 1) Methods to convert thor namespaces to constants and vice-versa. + # + # Foreman::Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" + # + # 2) Loading thor files and sandboxing: + # + # Foreman::Thor::Util.load_thorfile("~/.thor/foo") + # + module Util + class << self + # Receives a namespace and search for it in the Foreman::Thor::Base subclasses. + # + # ==== Parameters + # namespace:: The namespace to search for. + # + def find_by_namespace(namespace) + namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ + Foreman::Thor::Base.subclasses.detect { |klass| klass.namespace == namespace } + end + + # Receives a constant and converts it to a Foreman::Thor namespace. Since Foreman::Thor + # commands can be added to a sandbox, this method is also responsable for + # removing the sandbox namespace. + # + # This method should not be used in general because it's used to deal with + # older versions of Foreman::Thor. On current versions, if you need to get the + # namespace from a class, just call namespace on it. + # + # ==== Parameters + # constant:: The constant to be converted to the thor path. + # + # ==== Returns + # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" + # + def namespace_from_thor_class(constant) + constant = constant.to_s.gsub(/^Foreman::Thor::Sandbox::/, "") + constant = snake_case(constant).squeeze(":") + constant + end + + # Given the contents, evaluate it inside the sandbox and returns the + # namespaces defined in the sandbox. + # + # ==== Parameters + # contents + # + # ==== Returns + # Array[Object] + # + def namespaces_in_content(contents, file = __FILE__) + old_constants = Foreman::Thor::Base.subclasses.dup + Foreman::Thor::Base.subclasses.clear + + load_thorfile(file, contents) + + new_constants = Foreman::Thor::Base.subclasses.dup + Foreman::Thor::Base.subclasses.replace(old_constants) + + new_constants.map!(&:namespace) + new_constants.compact! + new_constants + end + + # Returns the thor classes declared inside the given class. + # + def thor_classes_in(klass) + stringfied_constants = klass.constants.map(&:to_s) + Foreman::Thor::Base.subclasses.select do |subclass| + next unless subclass.name + stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", "")) + end + end + + # Receives a string and convert it to snake case. SnakeCase returns snake_case. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def snake_case(str) + return str.downcase if str =~ /^[A-Z_]+$/ + str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/ + $+.downcase + end + + # Receives a string and convert it to camel case. camel_case returns CamelCase. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def camel_case(str) + return str if str !~ /_/ && str =~ /[A-Z]+.*/ + str.split("_").map(&:capitalize).join + end + + # Receives a namespace and tries to retrieve a Foreman::Thor or Foreman::Thor::Group class + # from it. It first searches for a class using the all the given namespace, + # if it's not found, removes the highest entry and searches for the class + # again. If found, returns the highest entry as the class name. + # + # ==== Examples + # + # class Foo::Bar < Foreman::Thor + # def baz + # end + # end + # + # class Baz::Foo < Foreman::Thor::Group + # end + # + # Foreman::Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command + # Foreman::Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil + # Foreman::Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" + # + # ==== Parameters + # namespace + # + def find_class_and_command_by_namespace(namespace, fallback = true) + if namespace.include?(":") # look for a namespaced command + pieces = namespace.split(":") + command = pieces.pop + klass = Foreman::Thor::Util.find_by_namespace(pieces.join(":")) + end + unless klass # look for a Foreman::Thor::Group with the right name + klass = Foreman::Thor::Util.find_by_namespace(namespace) + command = nil + end + if !klass && fallback # try a command in the default namespace + command = namespace + klass = Foreman::Thor::Util.find_by_namespace("") + end + [klass, command] + end + alias_method :find_class_and_task_by_namespace, :find_class_and_command_by_namespace + + # Receives a path and load the thor file in the path. The file is evaluated + # inside the sandbox to avoid namespacing conflicts. + # + def load_thorfile(path, content = nil, debug = false) + content ||= File.binread(path) + + begin + Foreman::Thor::Sandbox.class_eval(content, path) + rescue StandardError => e + $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") + if debug + $stderr.puts(*e.backtrace) + else + $stderr.puts(e.backtrace.first) + end + end + end + + def user_home + @@user_home ||= if ENV["HOME"] + ENV["HOME"] + elsif ENV["USERPROFILE"] + ENV["USERPROFILE"] + elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) + elsif ENV["APPDATA"] + ENV["APPDATA"] + else + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + end + + # Returns the root where thor files are located, depending on the OS. + # + def thor_root + File.join(user_home, ".thor").tr('\\', "/") + end + + # Returns the files in the thor root. On Windows thor_root will be something + # like this: + # + # C:\Documents and Settings\james\.thor + # + # If we don't #gsub the \ character, Dir.glob will fail. + # + def thor_root_glob + files = Dir["#{escape_globs(thor_root)}/*"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Where to look for Foreman::Thor files. + # + def globs_for(path) + path = escape_globs(path) + ["#{path}/Foreman::Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] + end + + # Return the path to the ruby interpreter taking into account multiple + # installations and windows extensions. + # + def ruby_command + @ruby_command ||= begin + ruby_name = RbConfig::CONFIG["ruby_install_name"] + ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name) + ruby << RbConfig::CONFIG["EXEEXT"] + + # avoid using different name than ruby (on platforms supporting links) + if ruby_name != "ruby" && File.respond_to?(:readlink) + begin + alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby") + alternate_ruby << RbConfig::CONFIG["EXEEXT"] + + # ruby is a symlink + if File.symlink? alternate_ruby + linked_ruby = File.readlink alternate_ruby + + # symlink points to 'ruby_install_name' + ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby + end + rescue NotImplementedError # rubocop:disable HandleExceptions + # just ignore on windows + end + end + + # escape string in case path to ruby executable contain spaces. + ruby.sub!(/.*\s.*/m, '"\&"') + ruby + end + end + + # Returns a string that has had any glob characters escaped. + # The glob characters are `* ? { } [ ]`. + # + # ==== Examples + # + # Foreman::Thor::Util.escape_globs('[apps]') # => '\[apps\]' + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def escape_globs(path) + path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') + end + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/version.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/version.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor/version.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/version.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,3 @@ +class Foreman::Thor + VERSION = "0.19.4" +end diff -Nru ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor.rb ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor.rb --- ruby-foreman-0.85.0/lib/foreman/vendor/thor/lib/thor.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/vendor/thor/lib/thor.rb 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,492 @@ +require "set" +require "foreman/vendor/thor/lib/thor/base" + +class Foreman::Thor + class << self + # Allows for custom "Command" package naming. + # + # === Parameters + # name + # options + # + def package_name(name, _ = {}) + @package_name = name.nil? || name == "" ? nil : name + end + + # Sets the default command when thor is executed without an explicit command to be called. + # + # ==== Parameters + # meth:: name of the default command + # + def default_command(meth = nil) + if meth + @default_command = meth == :none ? "help" : meth.to_s + else + @default_command ||= from_superclass(:default_command, "help") + end + end + alias_method :default_task, :default_command + + # Registers another Foreman::Thor subclass as a command. + # + # ==== Parameters + # klass:: Foreman::Thor subclass to register + # command:: Subcommand name to use + # usage:: Short usage for the subcommand + # description:: Description for the subcommand + def register(klass, subcommand_name, usage, description, options = {}) + if klass <= Foreman::Thor::Group + desc usage, description, options + define_method(subcommand_name) { |*args| invoke(klass, args) } + else + desc usage, description, options + subcommand subcommand_name, klass + end + end + + # Defines the usage and the description of the next command. + # + # ==== Parameters + # usage + # description + # options + # + def desc(usage, description, options = {}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.usage = usage if usage + command.description = description if description + else + @usage = usage + @desc = description + @hide = options[:hide] || false + end + end + + # Defines the long description of the next command. + # + # ==== Parameters + # long description + # + def long_desc(long_description, options = {}) + if options[:for] + command = find_and_refresh_command(options[:for]) + command.long_description = long_description if long_description + else + @long_desc = long_description + end + end + + # Maps an input to a command. If you define: + # + # map "-T" => "list" + # + # Running: + # + # thor -T + # + # Will invoke the list command. + # + # ==== Parameters + # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. + # + def map(mappings = nil) + @map ||= from_superclass(:map, {}) + + if mappings + mappings.each do |key, value| + if key.respond_to?(:each) + key.each { |subkey| @map[subkey] = value } + else + @map[key] = value + end + end + end + + @map + end + + # Declares the options for the next command to be declared. + # + # ==== Parameters + # Hash[Symbol => Object]:: The hash key is the name of the option and the value + # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric + # or :required (string). If you give a value, the type of the value is used. + # + def method_options(options = nil) + @method_options ||= {} + build_options(options, @method_options) if options + @method_options + end + + alias_method :options, :method_options + + # Adds an option to the set of method options. If :for is given as option, + # it allows you to change the options from a previous defined command. + # + # def previous_command + # # magic + # end + # + # method_option :foo => :bar, :for => :previous_command + # + # def next_command + # # magic + # end + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. It cannot be required and have default values. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner - String to show on usage notes. + # :hide - If you want to hide this option from the help. + # + def method_option(name, options = {}) + scope = if options[:for] + find_and_refresh_command(options[:for]).options + else + method_options + end + + build_option(name, options, scope) + end + alias_method :option, :method_option + + def disable_class_options + @disable_class_options = true + end + + # Prints help information for the given command. + # + # ==== Parameters + # shell + # command_name + # + def command_help(shell, command_name) + meth = normalize_command_name(command_name) + command = all_commands[meth] + handle_no_command_error(meth) unless command + + shell.say "Usage:" + shell.say " #{banner(command)}" + shell.say + class_options_help(shell, nil => command.options.values) + if command.long_description + shell.say "Description:" + shell.print_wrapped(command.long_description, :indent => 2) + else + shell.say command.description + end + end + alias_method :task_help, :command_help + + # Prints help information for this class. + # + # ==== Parameters + # shell + # + def help(shell, subcommand = false) + list = printable_commands(true, subcommand) + Foreman::Thor::Util.thor_classes_in(self).each do |klass| + list += klass.printable_commands(false) + end + list.sort! { |a, b| a[0] <=> b[0] } + + if defined?(@package_name) && @package_name + shell.say "#{@package_name} commands:" + else + shell.say "Commands:" + end + + shell.print_table(list, :indent => 2, :truncate => true) + shell.say + class_options_help(shell) + end + + # Returns commands ready to be printed. + def printable_commands(all = true, subcommand = false) + (all ? all_commands : commands).map do |_, command| + next if command.hidden? + item = [] + item << banner(command, false, subcommand) + item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "") + item + end.compact + end + alias_method :printable_tasks, :printable_commands + + def subcommands + @subcommands ||= from_superclass(:subcommands, []) + end + alias_method :subtasks, :subcommands + + def subcommand_classes + @subcommand_classes ||= {} + end + + def subcommand(subcommand, subcommand_class) + subcommands << subcommand.to_s + subcommand_class.subcommand_help subcommand + subcommand_classes[subcommand.to_s] = subcommand_class + + define_method(subcommand) do |*args| + args, opts = Foreman::Thor::Arguments.split(args) + invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}] + invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h") + invoke subcommand_class, *invoke_args + end + end + alias_method :subtask, :subcommand + + # Extend check unknown options to accept a hash of conditions. + # + # === Parameters + # options: A hash containing :only and/or :except keys + def check_unknown_options!(options = {}) + @check_unknown_options ||= {} + options.each do |key, value| + if value + @check_unknown_options[key] = Array(value) + else + @check_unknown_options.delete(key) + end + end + @check_unknown_options + end + + # Overwrite check_unknown_options? to take subcommands and options into account. + def check_unknown_options?(config) #:nodoc: + options = check_unknown_options + return false unless options + + command = config[:current_command] + return true unless command + + name = command.name + + if subcommands.include?(name) + false + elsif options[:except] + !options[:except].include?(name.to_sym) + elsif options[:only] + options[:only].include?(name.to_sym) + else + true + end + end + + # Stop parsing of options as soon as an unknown option or a regular + # argument is encountered. All remaining arguments are passed to the command. + # This is useful if you have a command that can receive arbitrary additional + # options, and where those additional options should not be handled by + # Foreman::Thor. + # + # ==== Example + # + # To better understand how this is useful, let's consider a command that calls + # an external command. A user may want to pass arbitrary options and + # arguments to that command. The command itself also accepts some options, + # which should be handled by Foreman::Thor. + # + # class_option "verbose", :type => :boolean + # stop_on_unknown_option! :exec + # check_unknown_options! :except => :exec + # + # desc "exec", "Run a shell command" + # def exec(*args) + # puts "diagnostic output" if options[:verbose] + # Kernel.exec(*args) + # end + # + # Here +exec+ can be called with +--verbose+ to get diagnostic output, + # e.g.: + # + # $ thor exec --verbose echo foo + # diagnostic output + # foo + # + # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead: + # + # $ thor exec echo --verbose foo + # --verbose foo + # + # ==== Parameters + # Symbol ...:: A list of commands that should be affected. + def stop_on_unknown_option!(*command_names) + stop_on_unknown_option.merge(command_names) + end + + def stop_on_unknown_option?(command) #:nodoc: + command && stop_on_unknown_option.include?(command.name.to_sym) + end + + protected + + def stop_on_unknown_option #:nodoc: + @stop_on_unknown_option ||= Set.new + end + + # The method responsible for dispatching given the args. + def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength + meth ||= retrieve_command_name(given_args) + command = all_commands[normalize_command_name(meth)] + + if !command && config[:invoked_via_subcommand] + # We're a subcommand and our first argument didn't match any of our + # commands. So we put it back and call our default command. + given_args.unshift(meth) + command = all_commands[normalize_command_name(default_command)] + end + + if command + args, opts = Foreman::Thor::Options.split(given_args) + if stop_on_unknown_option?(command) && !args.empty? + # given_args starts with a non-option, so we treat everything as + # ordinary arguments + args.concat opts + opts.clear + end + else + args = given_args + opts = nil + command = dynamic_command_class.new(meth) + end + + opts = given_opts || opts || [] + config[:current_command] = command + config[:command_options] = command.options + + instance = new(args, opts, config) + yield instance if block_given? + args = instance.args + trailing = args[Range.new(arguments.size, -1)] + instance.invoke_command(command, trailing || []) + end + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Foreman::Thor::Runner. It receives + # the command that is going to be invoked and a boolean which indicates if + # the namespace should be displayed as arguments. + # + def banner(command, namespace = nil, subcommand = false) + "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" + end + + def baseclass #:nodoc: + Foreman::Thor + end + + def dynamic_command_class #:nodoc: + Foreman::Thor::DynamicCommand + end + + def create_command(meth) #:nodoc: + @usage ||= nil + @desc ||= nil + @long_desc ||= nil + @disable_class_options ||= nil + + if @usage && @desc + base_class = @hide ? Foreman::Thor::HiddenCommand : Foreman::Thor::Command + commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options) + @usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil + true + elsif all_commands[meth] || meth == "method_missing" + true + else + puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \ + "Call desc if you want this method to be available as command or declare it inside a " \ + "no_commands{} block. Invoked from #{caller[1].inspect}." + false + end + end + alias_method :create_task, :create_command + + def initialize_added #:nodoc: + class_options.merge!(method_options) + @method_options = nil + end + + # Retrieve the command name from given args. + def retrieve_command_name(args) #:nodoc: + meth = args.first.to_s unless args.empty? + args.shift if meth && (map[meth] || meth !~ /^\-/) + end + alias_method :retrieve_task_name, :retrieve_command_name + + # receives a (possibly nil) command name and returns a name that is in + # the commands hash. In addition to normalizing aliases, this logic + # will determine if a shortened command is an unambiguous substring of + # a command or alias. + # + # +normalize_command_name+ also converts names like +animal-prison+ + # into +animal_prison+. + def normalize_command_name(meth) #:nodoc: + return default_command.to_s.tr("-", "_") unless meth + + possibilities = find_command_possibilities(meth) + raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1 + + if possibilities.empty? + meth ||= default_command + elsif map[meth] + meth = map[meth] + else + meth = possibilities.first + end + + meth.to_s.tr("-", "_") # treat foo-bar as foo_bar + end + alias_method :normalize_task_name, :normalize_command_name + + # this is the logic that takes the command name passed in by the user + # and determines whether it is an unambiguous substrings of a command or + # alias name. + def find_command_possibilities(meth) + len = meth.to_s.length + possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort + unique_possibilities = possibilities.map { |k| map[k] || k }.uniq + + if possibilities.include?(meth) + [meth] + elsif unique_possibilities.size == 1 + unique_possibilities + else + possibilities + end + end + alias_method :find_task_possibilities, :find_command_possibilities + + def subcommand_help(cmd) + desc "help [COMMAND]", "Describe subcommands or one specific subcommand" + class_eval " + def help(command = nil, subcommand = true); super; end +" + end + alias_method :subtask_help, :subcommand_help + end + + include Foreman::Thor::Base + + map HELP_MAPPINGS => :help + + desc "help [COMMAND]", "Describe available commands or one specific command" + disable_class_options + def help(command = nil, subcommand = false) + if command + if self.class.subcommands.include? command + self.class.subcommand_classes[command].help(shell, true) + else + self.class.command_help(shell, command) + end + else + self.class.help(shell, subcommand) + end + end +end diff -Nru ruby-foreman-0.85.0/lib/foreman/version.rb ruby-foreman-0.87.2/lib/foreman/version.rb --- ruby-foreman-0.85.0/lib/foreman/version.rb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/lib/foreman/version.rb 2020-12-14 01:40:41.000000000 +0000 @@ -1,5 +1,5 @@ module Foreman - VERSION = "0.85.0" + VERSION = "0.87.2" end diff -Nru ruby-foreman-0.85.0/man/foreman.1 ruby-foreman-0.87.2/man/foreman.1 --- ruby-foreman-0.85.0/man/foreman.1 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/man/foreman.1 2020-12-14 01:40:41.000000000 +0000 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "FOREMAN" "1" "March 2017" "Foreman 0.85.0" "Foreman Manual" +.TH "FOREMAN" "1" "April 2020" "Foreman 0.87.2" "Foreman Manual" . .SH "NAME" \fBforeman\fR \- manage Procfile\-based applications diff -Nru ruby-foreman-0.85.0/README.md ruby-foreman-0.87.2/README.md --- ruby-foreman-0.85.0/README.md 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/README.md 2020-12-14 01:40:41.000000000 +0000 @@ -10,7 +10,7 @@ $ gem install foreman -Ruby users should take care *not* to install foreman in their project's `Gemfile`. +Ruby users should take care *not* to install foreman in their project's `Gemfile`. See this [wiki article](https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman) for more details. ## Getting Started diff -Nru ruby-foreman-0.85.0/spec/foreman/export/systemd_spec.rb ruby-foreman-0.87.2/spec/foreman/export/systemd_spec.rb --- ruby-foreman-0.85.0/spec/foreman/export/systemd_spec.rb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/spec/foreman/export/systemd_spec.rb 2020-12-14 01:40:41.000000000 +0000 @@ -3,7 +3,7 @@ require "foreman/export/systemd" require "tmpdir" -describe Foreman::Export::Systemd, :fakefs do +describe Foreman::Export::Systemd, :fakefs, :aggregate_failures do let(:procfile) { write_procfile("/tmp/app/Procfile") } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } @@ -16,71 +16,101 @@ it "exports to the filesystem" do systemd.export - expect(File.read("/tmp/init/app.target")).to eq(example_export_file("systemd/app.target")) - expect(File.read("/tmp/init/app-alpha.target")).to eq(example_export_file("systemd/app-alpha.target")) - expect(File.read("/tmp/init/app-alpha@.service")).to eq(example_export_file("systemd/app-alpha@.service")) - expect(File.read("/tmp/init/app-bravo.target")).to eq(example_export_file("systemd/app-bravo.target")) - expect(File.read("/tmp/init/app-bravo@.service")).to eq(example_export_file("systemd/app-bravo@.service")) - - expect(File.directory?("/tmp/init/app-alpha.target.wants")).to be_truthy - expect(File.symlink?("/tmp/init/app-alpha.target.wants/app-alpha@5000.service")).to be_truthy - end - - it "cleans up if exporting into an existing dir" do - expect(FileUtils).to receive(:rm).with("/tmp/init/app.target") - - expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha@.service") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.target") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.target.wants/app-alpha@5000.service") - expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-alpha.target.wants") - - expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.target") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo@.service") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.target.wants/app-bravo@5100.service") - expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-bravo.target.wants") - - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.target") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar@.service") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.target.wants/app-foo_bar@5200.service") - expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-foo_bar.target.wants") - - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.target") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar@.service") - expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.target.wants/app-foo-bar@5300.service") - expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-foo-bar.target.wants") + expect(File.read("/tmp/init/app.target")).to eq(example_export_file("systemd/app.target")) + expect(File.read("/tmp/init/app-alpha.1.service")).to eq(example_export_file("systemd/app-alpha.1.service")) + expect(File.read("/tmp/init/app-bravo.1.service")).to eq(example_export_file("systemd/app-bravo.1.service")) + end + context "when systemd export was run using the previous version of systemd export" do + before do + write_file("/tmp/init/app.target") - systemd.export - systemd.export + write_file("/tmp/init/app-alpha@.service") + write_file("/tmp/init/app-alpha.target") + write_file("/tmp/init/app-alpha.target.wants/app-alpha@5000.service") + + write_file("/tmp/init/app-bravo.target") + write_file("/tmp/init/app-bravo@.service") + write_file("/tmp/init/app-bravo.target.wants/app-bravo@5100.service") + + write_file("/tmp/init/app-foo_bar.target") + write_file("/tmp/init/app-foo_bar@.service") + write_file("/tmp/init/app-foo_bar.target.wants/app-foo_bar@5200.service") + + write_file("/tmp/init/app-foo-bar.target") + write_file("/tmp/init/app-foo-bar@.service") + write_file("/tmp/init/app-foo-bar.target.wants/app-foo-bar@5300.service") + end + + it "cleans up service files created by systemd export" do + expect(FileUtils).to receive(:rm).with("/tmp/init/app.target") + + expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha@.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.target") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.target.wants/app-alpha@5000.service") + expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-alpha.target.wants") + + expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.target") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo@.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.target.wants/app-bravo@5100.service") + expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-bravo.target.wants") + + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.target") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar@.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.target.wants/app-foo_bar@5200.service") + expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-foo_bar.target.wants") + + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.target") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar@.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.target.wants/app-foo-bar@5300.service") + expect(FileUtils).to receive(:rm_r).with("/tmp/init/app-foo-bar.target.wants") + + systemd.export + end + end + + context "when systemd export was run using the current version of systemd export" do + before do + systemd.export + end + + it "cleans up service files created by systemd export" do + expect(FileUtils).to receive(:rm).with("/tmp/init/app.target") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.1.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.1.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.1.service") + expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.1.service") + + systemd.export + end end it "includes environment variables" do engine.env['KEY'] = 'some "value"' systemd.export - expect(File.read("/tmp/init/app-alpha@.service")).to match(/KEY=some "value"/) + expect(File.read("/tmp/init/app-alpha.1.service")).to match(/KEY=some "value"/) end it "includes ExecStart line" do engine.env['KEY'] = 'some "value"' systemd.export - expect(File.read("/tmp/init/app-alpha@.service")).to match(/^ExecStart=/) + expect(File.read("/tmp/init/app-alpha.1.service")).to match(/^ExecStart=/) end - context "with a formation" do + context "with a custom formation specified" do let(:formation) { "alpha=2" } - it "exports to the filesystem with concurrency" do + it "exports only those services that are specified in the formation" do systemd.export - expect(File.read("/tmp/init/app.target")).to eq(example_export_file("systemd/app.target")) - expect(File.read("/tmp/init/app-alpha.target")).to eq(example_export_file("systemd/app-alpha.target")) - expect(File.read("/tmp/init/app-alpha@.service")).to eq(example_export_file("systemd/app-alpha@.service")) - expect(File.read("/tmp/init/app-bravo.target")).to eq(example_export_file("systemd/app-bravo.target")) - expect(File.read("/tmp/init/app-bravo@.service")).to eq(example_export_file("systemd/app-bravo@.service")) + expect(File.read("/tmp/init/app.target")).to include("Wants=app-alpha.1.service app-alpha.2.service\n") + expect(File.read("/tmp/init/app-alpha.1.service")).to eq(example_export_file("systemd/app-alpha.1.service")) + expect(File.read("/tmp/init/app-alpha.2.service")).to eq(example_export_file("systemd/app-alpha.2.service")) + expect(File.exist?("/tmp/init/app-bravo.1.service")).to be_falsey end end - context "with alternate templates" do + context "with alternate template directory specified" do let(:template) { "/tmp/alternate" } let(:options) { { :app => "app", :template => template } } @@ -89,25 +119,37 @@ File.open("#{template}/master.target.erb", "w") { |f| f.puts "alternate_template" } end - it "can export with alternate template files" do + it "uses template files found in the alternate directory" do systemd.export expect(File.read("/tmp/init/app.target")).to eq("alternate_template\n") end - end - context "with alternate templates from home dir" do + context "with alternate templates in the user home directory" do + before do + FileUtils.mkdir_p File.expand_path("~/.foreman/templates/systemd") + File.open(File.expand_path("~/.foreman/templates/systemd/master.target.erb"), "w") do |file| + file.puts "home_dir_template" + end + end + + it "uses template files found in the alternate directory" do + systemd.export + expect(File.read("/tmp/init/app.target")).to eq("alternate_template\n") + end + end + end + context "with alternate templates in the user home directory" do before do FileUtils.mkdir_p File.expand_path("~/.foreman/templates/systemd") File.open(File.expand_path("~/.foreman/templates/systemd/master.target.erb"), "w") do |file| - file.puts "default_alternate_template" + file.puts "home_dir_template" end end - it "can export with alternate template files" do + it "uses template files found in the user home directory" do systemd.export - expect(File.read("/tmp/init/app.target")).to eq("default_alternate_template\n") + expect(File.read("/tmp/init/app.target")).to eq("home_dir_template\n") end end - end diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha.1.service ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha.1.service --- ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha.1.service 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha.1.service 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,18 @@ +[Unit] +PartOf=app.target +StopWhenUnneeded=yes + +[Service] +User=app +WorkingDirectory=/tmp/app +Environment=PORT=5000 +Environment=PS=alpha.1 +ExecStart=/bin/bash -lc 'exec -a "app-alpha.1" ./alpha' +Restart=always +RestartSec=14s +StandardInput=null +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=%n +KillMode=mixed +TimeoutStopSec=5 diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha.2.service ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha.2.service --- ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha.2.service 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha.2.service 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,18 @@ +[Unit] +PartOf=app.target +StopWhenUnneeded=yes + +[Service] +User=app +WorkingDirectory=/tmp/app +Environment=PORT=5001 +Environment=PS=alpha.2 +ExecStart=/bin/bash -lc 'exec -a "app-alpha.2" ./alpha' +Restart=always +RestartSec=14s +StandardInput=null +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=%n +KillMode=mixed +TimeoutStopSec=5 diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha@.service ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha@.service --- ruby-foreman-0.85.0/spec/resources/export/systemd/app-alpha@.service 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app-alpha@.service 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -[Unit] -PartOf=app-alpha.target - -[Service] -User=app -WorkingDirectory=/tmp/app -Environment=PORT=%i -ExecStart=/bin/bash -lc 'exec ./alpha' -Restart=always -StandardInput=null -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=%n -KillMode=mixed -TimeoutStopSec=5 diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app-bravo.1.service ruby-foreman-0.87.2/spec/resources/export/systemd/app-bravo.1.service --- ruby-foreman-0.85.0/spec/resources/export/systemd/app-bravo.1.service 1970-01-01 00:00:00.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app-bravo.1.service 2020-12-14 01:40:41.000000000 +0000 @@ -0,0 +1,18 @@ +[Unit] +PartOf=app.target +StopWhenUnneeded=yes + +[Service] +User=app +WorkingDirectory=/tmp/app +Environment=PORT=5100 +Environment=PS=bravo.1 +ExecStart=/bin/bash -lc 'exec -a "app-bravo.1" ./bravo' +Restart=always +RestartSec=14s +StandardInput=null +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=%n +KillMode=mixed +TimeoutStopSec=5 diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app-bravo@.service ruby-foreman-0.87.2/spec/resources/export/systemd/app-bravo@.service --- ruby-foreman-0.85.0/spec/resources/export/systemd/app-bravo@.service 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app-bravo@.service 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -[Unit] -PartOf=app-bravo.target - -[Service] -User=app -WorkingDirectory=/tmp/app -Environment=PORT=%i -ExecStart=/bin/bash -lc 'exec ./bravo' -Restart=always -StandardInput=null -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=%n -KillMode=mixed -TimeoutStopSec=5 diff -Nru ruby-foreman-0.85.0/spec/resources/export/systemd/app.target ruby-foreman-0.87.2/spec/resources/export/systemd/app.target --- ruby-foreman-0.85.0/spec/resources/export/systemd/app.target 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/spec/resources/export/systemd/app.target 2020-12-14 01:40:41.000000000 +0000 @@ -1,5 +1,5 @@ [Unit] -Wants=app-alpha.target app-bravo.target app-foo_bar.target app-foo-bar.target +Wants=app-alpha.1.service app-bravo.1.service app-foo_bar.1.service app-foo-bar.1.service [Install] WantedBy=multi-user.target diff -Nru ruby-foreman-0.85.0/spec/spec_helper.rb ruby-foreman-0.87.2/spec/spec_helper.rb --- ruby-foreman-0.85.0/spec/spec_helper.rb 2018-06-19 05:29:04.000000000 +0000 +++ ruby-foreman-0.87.2/spec/spec_helper.rb 2020-12-14 01:40:41.000000000 +0000 @@ -105,7 +105,7 @@ def write_file(file) FileUtils.mkdir_p(File.dirname(file)) File.open(file, 'w') do |f| - yield(f) + yield(f) if block_given? end end