foreman-0.87.2/0000755000004100000410000000000013765541231013276 5ustar www-datawww-dataforeman-0.87.2/README.md0000644000004100000410000000351313765541231014557 0ustar www-datawww-data# Foreman [![Build Status](https://travis-ci.org/ddollar/foreman.svg?branch=master)](https://travis-ci.org/ddollar/foreman) [![Code Climate](https://codeclimate.com/github/ddollar/foreman.svg)](https://codeclimate.com/github/ddollar/foreman) [![Inline docs](http://inch-ci.org/github/ddollar/foreman.svg?branch=master)](http://inch-ci.org/github/ddollar/foreman) Manage Procfile-based applications ## Installation $ gem install foreman 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 * http://blog.daviddollar.org/2011/05/06/introducing-foreman.html ## Supported Ruby versions See [.travis.yml](.travis.yml) for a list of Ruby versions against which Foreman is tested. ## Documentation * [man page](http://ddollar.github.io/foreman/) * [wiki](https://github.com/ddollar/foreman/wiki) * [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md) ## Ports * [forego](https://github.com/ddollar/forego) - Go * [node-foreman](https://github.com/strongloop/node-foreman) - Node.js * [gaffer](https://github.com/jingweno/gaffer) - Java/JVM * [goreman](https://github.com/mattn/goreman) - Go * [honcho](https://github.com/nickstenning/honcho) - python * [proclet](https://github.com/kazeburo/Proclet) - Perl * [shoreman](https://github.com/chrismytton/shoreman) - shell * [crank](https://github.com/arktisklada/crank) - Crystal * [houseman](https://github.com/fujimura/houseman) - Haskell * [spm](https://github.com/bytegust/spm) - Go ## Authors #### Created and maintained by David Dollar #### Patches contributed by [Contributor List](https://github.com/ddollar/foreman/contributors) ## License Foreman is licensed under the MIT license. See LICENSE for the full license text. foreman-0.87.2/bin/0000755000004100000410000000000013765541231014046 5ustar www-datawww-dataforeman-0.87.2/bin/foreman-runner0000755000004100000410000000116113765541231016731 0ustar www-datawww-data#!/bin/sh # #/ Usage: foreman-runner [-d ] [-p] [...] #/ #/ Run a command with exec, optionally changing directory first set -e error() { echo $@ >&2 exit 1 } usage() { cat $0 | grep '^#/' | cut -c4- exit } read_profile="" while getopts ":hd:p" OPT; do case $OPT in d) cd "$OPTARG" ;; p) read_profile="1" ;; h) usage ;; \?) error "invalid option: -$OPTARG" ;; :) error "option -$OPTARG requires an argument" ;; esac done shift $((OPTIND-1)) [ -z "$1" ] && usage if [ "$read_profile" = "1" ]; then if [ -f .profile ]; then . ./.profile fi fi exec "$@" foreman-0.87.2/bin/foreman0000755000004100000410000000016313765541231015423 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.expand_path("../../lib", __FILE__) require "foreman/cli" Foreman::CLI.start foreman-0.87.2/spec/0000755000004100000410000000000013765541231014230 5ustar www-datawww-dataforeman-0.87.2/spec/helper_spec.rb0000644000004100000410000000070213765541231017045 0ustar www-datawww-datarequire "spec_helper" describe "spec helpers" do describe "#preserving_env" do after { ENV.delete "FOO" } it "should remove added environment vars" do old = ENV["FOO"] preserving_env { ENV["FOO"] = "baz" } expect(ENV["FOO"]).to eq(old) end it "should reset modified environment vars" do ENV["FOO"] = "bar" preserving_env { ENV["FOO"] = "baz"} expect(ENV["FOO"]).to eq("bar") end end end foreman-0.87.2/spec/resources/0000755000004100000410000000000013765541231016242 5ustar www-datawww-dataforeman-0.87.2/spec/resources/Procfile.bad0000644000004100000410000000003113765541231020447 0ustar www-datawww-datagood: sleep 1 bad: false foreman-0.87.2/spec/resources/bin/0000755000004100000410000000000013765541231017012 5ustar www-datawww-dataforeman-0.87.2/spec/resources/bin/test0000755000004100000410000000003113765541231017711 0ustar www-datawww-data#!/bin/sh echo "testing" foreman-0.87.2/spec/resources/bin/echo0000755000004100000410000000002213765541231017650 0ustar www-datawww-data#!/bin/sh echo $* foreman-0.87.2/spec/resources/bin/utf80000755000004100000410000000004413765541231017624 0ustar www-datawww-data#!/usr/bin/env ruby puts "\xff\x03" foreman-0.87.2/spec/resources/bin/env0000755000004100000410000000002713765541231017527 0ustar www-datawww-data#!/bin/bash echo ${!1} foreman-0.87.2/spec/resources/export/0000755000004100000410000000000013765541231017563 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/inittab/0000755000004100000410000000000013765541231021215 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/inittab/inittab.default0000644000004100000410000000076413765541231024224 0ustar www-datawww-data# ----- foreman app processes ----- AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1' AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5100;./bravo >> /var/log/app/bravo-1.log 2>&1' AP03:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5200;./foo_bar >> /var/log/app/foo_bar-1.log 2>&1' AP04:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5300;./foo-bar >> /var/log/app/foo-bar-1.log 2>&1' # ----- end foreman app processes ----- foreman-0.87.2/spec/resources/export/inittab/inittab.concurrency0000644000004100000410000000043413765541231025124 0ustar www-datawww-data# ----- foreman app processes ----- AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1' AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5001;./alpha >> /var/log/app/alpha-2.log 2>&1' # ----- end foreman app processes ----- foreman-0.87.2/spec/resources/export/systemd/0000755000004100000410000000000013765541231021253 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/systemd/app-bravo.1.service0000644000004100000410000000052113765541231024661 0ustar www-datawww-data[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 foreman-0.87.2/spec/resources/export/systemd/app-alpha.target0000644000004100000410000000003113765541231024320 0ustar www-datawww-data[Unit] PartOf=app.target foreman-0.87.2/spec/resources/export/systemd/app.target0000644000004100000410000000020713765541231023242 0ustar www-datawww-data[Unit] Wants=app-alpha.1.service app-bravo.1.service app-foo_bar.1.service app-foo-bar.1.service [Install] WantedBy=multi-user.target foreman-0.87.2/spec/resources/export/systemd/app-alpha.1.service0000644000004100000410000000052113765541231024635 0ustar www-datawww-data[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 foreman-0.87.2/spec/resources/export/systemd/app-bravo.target0000644000004100000410000000003113765541231024344 0ustar www-datawww-data[Unit] PartOf=app.target foreman-0.87.2/spec/resources/export/systemd/app-alpha.2.service0000644000004100000410000000052113765541231024636 0ustar www-datawww-data[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 foreman-0.87.2/spec/resources/export/supervisord/0000755000004100000410000000000013765541231022150 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/supervisord/app-alpha-1.conf0000644000004100000410000000216113765541231025020 0ustar www-datawww-data[program:app-alpha-1] command=./alpha autostart=true autorestart=true stdout_logfile=/var/log/app/alpha-1.log stderr_logfile=/var/log/app/alpha-1.error.log user=app directory=/tmp/app environment=FOO="bar",URL="http://example.com/api?foo=bar&baz=1",PORT="5000" [program:app-bravo-1] command=./bravo autostart=true autorestart=true stdout_logfile=/var/log/app/bravo-1.log stderr_logfile=/var/log/app/bravo-1.error.log user=app directory=/tmp/app environment=FOO="bar",URL="http://example.com/api?foo=bar&baz=1",PORT="5100" [program:app-foo_bar-1] command=./foo_bar autostart=true autorestart=true stdout_logfile=/var/log/app/foo_bar-1.log stderr_logfile=/var/log/app/foo_bar-1.error.log user=app directory=/tmp/app environment=FOO="bar",URL="http://example.com/api?foo=bar&baz=1",PORT="5200" [program:app-foo-bar-1] command=./foo-bar autostart=true autorestart=true stdout_logfile=/var/log/app/foo-bar-1.log stderr_logfile=/var/log/app/foo-bar-1.error.log user=app directory=/tmp/app environment=FOO="bar",URL="http://example.com/api?foo=bar&baz=1",PORT="5300" [group:app] programs=app-alpha-1,app-bravo-1,app-foo_bar-1,app-foo-bar-1 foreman-0.87.2/spec/resources/export/supervisord/app-alpha-2.conf0000644000004100000410000000071713765541231025026 0ustar www-datawww-data[program:app-alpha-1] command=./alpha autostart=true autorestart=true stdout_logfile=/var/log/app/alpha-1.log stderr_logfile=/var/log/app/alpha-1.error.log user=app directory=/tmp/app environment=PORT="5000" [program:app-alpha-2] command=./alpha autostart=true autorestart=true stdout_logfile=/var/log/app/alpha-2.log stderr_logfile=/var/log/app/alpha-2.error.log user=app directory=/tmp/app environment=PORT="5001" [group:app] programs=app-alpha-1,app-alpha-2 foreman-0.87.2/spec/resources/export/runit/0000755000004100000410000000000013765541231020724 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-alpha-2/0000755000004100000410000000000013765541231022726 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-alpha-2/run0000644000004100000410000000013713765541231023456 0ustar www-datawww-data#!/bin/sh cd /tmp/app exec 2>&1 exec chpst -u app -e /tmp/init/app-alpha-2/env ./alpha bar=baz foreman-0.87.2/spec/resources/export/runit/app-alpha-2/log/0000755000004100000410000000000013765541231023507 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-alpha-2/log/run0000644000004100000410000000021213765541231024231 0ustar www-datawww-data#!/bin/sh set -e LOG=/var/log/app/alpha-2 test -d "$LOG" || mkdir -p -m 2750 "$LOG" && chown app "$LOG" exec chpst -u app svlogd "$LOG" foreman-0.87.2/spec/resources/export/runit/app-alpha-1/0000755000004100000410000000000013765541231022725 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-alpha-1/run0000644000004100000410000000013713765541231023455 0ustar www-datawww-data#!/bin/sh cd /tmp/app exec 2>&1 exec chpst -u app -e /tmp/init/app-alpha-1/env ./alpha bar=baz foreman-0.87.2/spec/resources/export/runit/app-alpha-1/log/0000755000004100000410000000000013765541231023506 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-alpha-1/log/run0000644000004100000410000000021213765541231024230 0ustar www-datawww-data#!/bin/sh set -e LOG=/var/log/app/alpha-1 test -d "$LOG" || mkdir -p -m 2750 "$LOG" && chown app "$LOG" exec chpst -u app svlogd "$LOG" foreman-0.87.2/spec/resources/export/runit/app-bravo-1/0000755000004100000410000000000013765541231022751 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-bravo-1/run0000644000004100000410000000012713765541231023500 0ustar www-datawww-data#!/bin/sh cd /tmp/app exec 2>&1 exec chpst -u app -e /tmp/init/app-bravo-1/env ./bravo foreman-0.87.2/spec/resources/export/runit/app-bravo-1/log/0000755000004100000410000000000013765541231023532 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/runit/app-bravo-1/log/run0000644000004100000410000000021213765541231024254 0ustar www-datawww-data#!/bin/sh set -e LOG=/var/log/app/bravo-1 test -d "$LOG" || mkdir -p -m 2750 "$LOG" && chown app "$LOG" exec chpst -u app svlogd "$LOG" foreman-0.87.2/spec/resources/export/upstart/0000755000004100000410000000000013765541231021265 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/upstart/app-alpha-1.conf0000644000004100000410000000017013765541231024133 0ustar www-datawww-datastart on starting app-alpha stop on stopping app-alpha respawn env PORT=5000 setuid app chdir /tmp/app exec ./alpha foreman-0.87.2/spec/resources/export/upstart/app.conf0000644000004100000410000000006213765541231022712 0ustar www-datawww-datastart on runlevel [2345] stop on runlevel [!2345] foreman-0.87.2/spec/resources/export/upstart/app-alpha-2.conf0000644000004100000410000000017013765541231024134 0ustar www-datawww-datastart on starting app-alpha stop on stopping app-alpha respawn env PORT=5001 setuid app chdir /tmp/app exec ./alpha foreman-0.87.2/spec/resources/export/upstart/app-bravo-1.conf0000644000004100000410000000017013765541231024157 0ustar www-datawww-datastart on starting app-bravo stop on stopping app-bravo respawn env PORT=5100 setuid app chdir /tmp/app exec ./bravo foreman-0.87.2/spec/resources/export/upstart/app-alpha.conf0000644000004100000410000000005313765541231023775 0ustar www-datawww-datastart on starting app stop on stopping app foreman-0.87.2/spec/resources/export/upstart/app-bravo.conf0000644000004100000410000000005313765541231024021 0ustar www-datawww-datastart on starting app stop on stopping app foreman-0.87.2/spec/resources/export/launchd/0000755000004100000410000000000013765541231021201 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/launchd/launchd-a.default0000644000004100000410000000143213765541231024403 0ustar www-datawww-data Label app-alpha-1 EnvironmentVariables PORT 5000 ProgramArguments ./alpha KeepAlive RunAtLoad StandardOutPath /var/log/app/app-alpha-1.log StandardErrorPath /var/log/app/app-alpha-1.log UserName app WorkingDirectory /tmp/app foreman-0.87.2/spec/resources/export/launchd/launchd-c.default0000644000004100000410000000147313765541231024412 0ustar www-datawww-data Label app-alpha-1 EnvironmentVariables PORT 5000 ProgramArguments ./alpha charlie KeepAlive RunAtLoad StandardOutPath /var/log/app/app-alpha-1.log StandardErrorPath /var/log/app/app-alpha-1.log UserName app WorkingDirectory /tmp/app foreman-0.87.2/spec/resources/export/launchd/launchd-b.default0000644000004100000410000000143213765541231024404 0ustar www-datawww-data Label app-bravo-1 EnvironmentVariables PORT 5100 ProgramArguments ./bravo KeepAlive RunAtLoad StandardOutPath /var/log/app/app-bravo-1.log StandardErrorPath /var/log/app/app-bravo-1.log UserName app WorkingDirectory /tmp/app foreman-0.87.2/spec/resources/export/daemon/0000755000004100000410000000000013765541231021026 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/daemon/app-alpha-1.conf0000644000004100000410000000036513765541231023702 0ustar www-datawww-datastart on starting app-alpha stop on stopping app-alpha respawn env PORT=5000 exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-1.pid --exec ./alpha >> /var/log/app/app-alpha-1.log 2>&1 foreman-0.87.2/spec/resources/export/daemon/app.conf0000644000004100000410000000030213765541231022450 0ustar www-datawww-datapre-start script bash << "EOF" mkdir -p /var/log/app chown -R app /var/log/app mkdir -p /var/run/app chown -R app /var/run/app EOF end script start on runlevel [2345] stop on runlevel [016] foreman-0.87.2/spec/resources/export/daemon/app-alpha-2.conf0000644000004100000410000000036513765541231023703 0ustar www-datawww-datastart on starting app-alpha stop on stopping app-alpha respawn env PORT=5001 exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-2.pid --exec ./alpha >> /var/log/app/app-alpha-2.log 2>&1 foreman-0.87.2/spec/resources/export/daemon/app-bravo-1.conf0000644000004100000410000000036513765541231023726 0ustar www-datawww-datastart on starting app-bravo stop on stopping app-bravo respawn env PORT=5100 exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-bravo-1.pid --exec ./bravo >> /var/log/app/app-bravo-1.log 2>&1 foreman-0.87.2/spec/resources/export/daemon/app-alpha.conf0000644000004100000410000000005313765541231023536 0ustar www-datawww-datastart on starting app stop on stopping app foreman-0.87.2/spec/resources/export/daemon/app-bravo.conf0000644000004100000410000000005313765541231023562 0ustar www-datawww-datastart on starting app stop on stopping app foreman-0.87.2/spec/resources/export/bluepill/0000755000004100000410000000000013765541231021373 5ustar www-datawww-dataforeman-0.87.2/spec/resources/export/bluepill/app-concurrency.pill0000644000004100000410000000221613765541231025366 0ustar www-datawww-dataBluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app| app.uid = "app" app.gid = "app" app.process("alpha-1") do |process| process.start_command = %Q{./alpha} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5000"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-alpha-1.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-alpha" end app.process("alpha-2") do |process| process.start_command = %Q{./alpha} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5001"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-alpha-2.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-alpha" end end foreman-0.87.2/spec/resources/export/bluepill/app.pill0000644000004100000410000000420413765541231023035 0ustar www-datawww-dataBluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app| app.uid = "app" app.gid = "app" app.process("alpha-1") do |process| process.start_command = %Q{./alpha} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5000"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-alpha-1.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-alpha" end app.process("bravo-1") do |process| process.start_command = %Q{./bravo} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5100"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-bravo-1.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-bravo" end app.process("foo_bar-1") do |process| process.start_command = %Q{./foo_bar} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5200"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-foo_bar-1.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-foo_bar" end app.process("foo-bar-1") do |process| process.start_command = %Q{./foo-bar} process.working_dir = "/tmp/app" process.daemonize = true process.environment = {"PORT"=>"5300"} process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "/var/log/app/app-foo-bar-1.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "app-foo-bar" end end foreman-0.87.2/spec/resources/Procfile0000644000004100000410000000015113765541231017725 0ustar www-datawww-dataecho: bin/echo echoing env: bin/env FOO test: bin/test utf8: bin/utf8 ps: bin/echo PS env var is $PS foreman-0.87.2/spec/foreman_spec.rb0000644000004100000410000000043513765541231017220 0ustar www-datawww-datarequire "spec_helper" require "foreman" describe Foreman do describe "VERSION" do subject { Foreman::VERSION } it { is_expected.to be_a String } end describe "runner" do it "should exist" do expect(File.exists?(Foreman.runner)).to eq(true) end end end foreman-0.87.2/spec/foreman/0000755000004100000410000000000013765541231015657 5ustar www-datawww-dataforeman-0.87.2/spec/foreman/engine_spec.rb0000644000004100000410000000640413765541231020467 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" class Foreman::Engine::Tester < Foreman::Engine attr_reader :buffer def startup @buffer = "" end def output(name, data) @buffer += "#{name}: #{data}" end def shutdown end end describe "Foreman::Engine", :fakefs do subject do write_procfile "Procfile" Foreman::Engine::Tester.new.load_procfile("Procfile") end describe "initialize" do describe "with a Procfile" do before { write_procfile } it "reads the processes" do expect(subject.process("alpha").command).to eq("./alpha") expect(subject.process("bravo").command).to eq("./bravo") end end end describe "start" do it "forks the processes" do expect(subject.process("alpha")).to receive(:run) expect(subject.process("bravo")).to receive(:run) expect(subject).to receive(:watch_for_output) expect(subject).to receive(:wait_for_shutdown_or_child_termination) subject.start end it "handles concurrency" do subject.options[:formation] = "alpha=2" expect(subject.process("alpha")).to receive(:run).twice expect(subject.process("bravo")).to_not receive(:run) expect(subject).to receive(:watch_for_output) expect(subject).to receive(:wait_for_shutdown_or_child_termination) subject.start end end describe "directories" do it "has the directory default relative to the Procfile" do write_procfile "/some/app/Procfile" engine = Foreman::Engine.new.load_procfile("/some/app/Procfile") expect(engine.root).to eq("/some/app") end end describe "environment" do it "should read env files" do write_file("/tmp/env") { |f| f.puts("FOO=baz") } subject.load_env("/tmp/env") expect(subject.env["FOO"]).to eq("baz") end it "should read more than one if specified" do write_file("/tmp/env1") { |f| f.puts("FOO=bar") } write_file("/tmp/env2") { |f| f.puts("BAZ=qux") } subject.load_env "/tmp/env1" subject.load_env "/tmp/env2" expect(subject.env["FOO"]).to eq("bar") expect(subject.env["BAZ"]).to eq("qux") end it "should handle quoted values" do write_file("/tmp/env") do |f| f.puts 'FOO=bar' f.puts 'BAZ="qux"' f.puts "FRED='barney'" f.puts 'OTHER="escaped\"quote"' f.puts 'URL="http://example.com/api?foo=bar&baz=1"' end subject.load_env "/tmp/env" expect(subject.env["FOO"]).to eq("bar") expect(subject.env["BAZ"]).to eq("qux") expect(subject.env["FRED"]).to eq("barney") expect(subject.env["OTHER"]).to eq('escaped"quote') expect(subject.env["URL"]).to eq("http://example.com/api?foo=bar&baz=1") end it "should handle multiline strings" do write_file("/tmp/env") do |f| f.puts 'FOO="bar\nbaz"' end subject.load_env "/tmp/env" expect(subject.env["FOO"]).to eq("bar\nbaz") end it "should fail if specified and doesnt exist" do expect { subject.load_env "/tmp/env" }.to raise_error(Errno::ENOENT) end it "should set port from .env if specified" do write_file("/tmp/env") { |f| f.puts("PORT=9000") } subject.load_env "/tmp/env" expect(subject.send(:base_port)).to eq(9000) end end end foreman-0.87.2/spec/foreman/helpers_spec.rb0000644000004100000410000000106413765541231020661 0ustar www-datawww-datarequire "spec_helper" require "foreman/helpers" describe "Foreman::Helpers" do before do module Foo class Bar; end end end after do Object.send(:remove_const, :Foo) end subject { o = Object.new; o.extend(Foreman::Helpers); o } it "should classify words" do expect(subject.classify("foo")).to eq("Foo") expect(subject.classify("foo-bar")).to eq("FooBar") end it "should constantize words" do expect(subject.constantize("Object")).to eq(Object) expect(subject.constantize("Foo::Bar")).to eq(Foo::Bar) end end foreman-0.87.2/spec/foreman/export/0000755000004100000410000000000013765541231017200 5ustar www-datawww-dataforeman-0.87.2/spec/foreman/export/base_spec.rb0000644000004100000410000000125213765541231021451 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export" describe "Foreman::Export::Base", :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:location) { "/tmp/init" } let(:engine) { Foreman::Engine.new().load_procfile(procfile) } let(:subject) { Foreman::Export::Base.new(location, engine) } it "has a say method for displaying info" do expect(subject).to receive(:puts).with("[foreman export] foo") subject.send(:say, "foo") end it "raises errors as a Foreman::Export::Exception" do expect { subject.send(:error, "foo") }.to raise_error(Foreman::Export::Exception, "foo") end end foreman-0.87.2/spec/foreman/export/inittab_spec.rb0000644000004100000410000000233113765541231022170 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/inittab" require "tmpdir" describe Foreman::Export::Inittab, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:location) { "/tmp/inittab" } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } let(:options) { Hash.new } let(:inittab) { Foreman::Export::Inittab.new(location, engine, options) } before(:each) { load_export_templates_into_fakefs("inittab") } before(:each) { allow(inittab).to receive(:say) } it "exports to the filesystem" do inittab.export expect(File.read("/tmp/inittab")).to eq(example_export_file("inittab/inittab.default")) end context "to stdout" do let(:location) { "-" } it "exports to stdout" do expect(inittab).to receive(:puts).with(example_export_file("inittab/inittab.default")) inittab.export end end context "with concurrency" do let(:formation) { "alpha=2" } it "exports to the filesystem with concurrency" do inittab.export expect(File.read("/tmp/inittab")).to eq(example_export_file("inittab/inittab.concurrency")) end end end foreman-0.87.2/spec/foreman/export/supervisord_spec.rb0000644000004100000410000000250213765541231023123 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/supervisord" require "tmpdir" describe Foreman::Export::Supervisord, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } let(:options) { Hash.new } let(:supervisord) { Foreman::Export::Supervisord.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("supervisord") } before(:each) { allow(supervisord).to receive(:say) } it "exports to the filesystem" do write_env(".env", "FOO"=>"bar", "URL"=>"http://example.com/api?foo=bar&baz=1") supervisord.engine.load_env('.env') supervisord.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("supervisord/app-alpha-1.conf")) end it "cleans up if exporting into an existing dir" do expect(FileUtils).to receive(:rm).with("/tmp/init/app.conf") supervisord.export supervisord.export end context "with concurrency" do let(:formation) { "alpha=2" } it "exports to the filesystem with concurrency" do supervisord.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("supervisord/app-alpha-2.conf")) end end end foreman-0.87.2/spec/foreman/export/runit_spec.rb0000644000004100000410000000346413765541231021707 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/runit" require "tmpdir" describe Foreman::Export::Runit, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') } let(:engine) { Foreman::Engine.new(:formation => "alpha=2,bravo=1").load_procfile(procfile) } let(:options) { Hash.new } let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, options) } before(:each) { load_export_templates_into_fakefs("runit") } before(:each) { allow(runit).to receive(:say) } before(:each) { allow(FakeFS::FileUtils).to receive(:chmod) } it "exports to the filesystem" do engine.env["BAR"] = "baz" runit.export expect(File.read("/tmp/init/app-alpha-1/run")).to eq(example_export_file('runit/app-alpha-1/run')) expect(File.read("/tmp/init/app-alpha-1/log/run")).to eq(example_export_file('runit/app-alpha-1/log/run')) expect(File.read("/tmp/init/app-alpha-1/env/PORT")).to eq("5000\n") expect(File.read("/tmp/init/app-alpha-1/env/BAR")).to eq("baz\n") expect(File.read("/tmp/init/app-alpha-2/run")).to eq(example_export_file('runit/app-alpha-2/run')) expect(File.read("/tmp/init/app-alpha-2/log/run")).to eq(example_export_file('runit/app-alpha-2/log/run')) expect(File.read("/tmp/init/app-alpha-2/env/PORT")).to eq("5001\n") expect(File.read("/tmp/init/app-alpha-2/env/BAR")).to eq("baz\n") expect(File.read("/tmp/init/app-bravo-1/run")).to eq(example_export_file('runit/app-bravo-1/run')) expect(File.read("/tmp/init/app-bravo-1/log/run")).to eq(example_export_file('runit/app-bravo-1/log/run')) expect(File.read("/tmp/init/app-bravo-1/env/PORT")).to eq("5100\n") end it "creates a full path to the export directory" do expect { runit.export }.to_not raise_error end end foreman-0.87.2/spec/foreman/export/upstart_spec.rb0000644000004100000410000001062613765541231022246 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/upstart" require "tmpdir" describe Foreman::Export::Upstart, :fakefs do let(:procfile) { write_procfile("/tmp/app/Procfile") } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } let(:options) { Hash.new } let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("upstart") } before(:each) { allow(upstart).to receive(:say) } it "exports to the filesystem" do upstart.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("upstart/app.conf")) expect(File.read("/tmp/init/app-alpha.conf")).to eq(example_export_file("upstart/app-alpha.conf")) expect(File.read("/tmp/init/app-alpha-1.conf")).to eq(example_export_file("upstart/app-alpha-1.conf")) expect(File.read("/tmp/init/app-bravo.conf")).to eq(example_export_file("upstart/app-bravo.conf")) expect(File.read("/tmp/init/app-bravo-1.conf")).to eq(example_export_file("upstart/app-bravo-1.conf")) end it "cleans up if exporting into an existing dir" do expect(FileUtils).to receive(:rm).with("/tmp/init/app.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar-1.conf") upstart.export upstart.export end it "does not delete exported files for similarly named applications" do FileUtils.mkdir_p "/tmp/init" ["app2", "app2-alpha", "app2-alpha-1"].each do |name| path = "/tmp/init/#{name}.conf" FileUtils.touch(path) expect(FileUtils).to_not receive(:rm).with(path) end upstart.export end it 'does not delete exported files for app which share name prefix' do FileUtils.mkdir_p "/tmp/init" ["app-worker", "app-worker-worker", "app-worker-worker-1"].each do |name| path = "/tmp/init/#{name}.conf" FileUtils.touch(path) expect(FileUtils).to_not receive(:rm).with(path) end upstart.export expect(File.exist?('/tmp/init/app.conf')).to be true expect(File.exist?('/tmp/init/app-worker.conf')).to be true end it "quotes and escapes environment variables" do engine.env['KEY'] = 'd"\|d' upstart.export expect("foobarfoo").to include "bar" expect(File.read("/tmp/init/app-alpha-1.conf")).to match(/KEY='d"\\\|d'/) end context "with a formation" do let(:formation) { "alpha=2" } it "exports to the filesystem with concurrency" do upstart.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("upstart/app.conf")) expect(File.read("/tmp/init/app-alpha.conf")).to eq(example_export_file("upstart/app-alpha.conf")) expect(File.read("/tmp/init/app-alpha-1.conf")).to eq(example_export_file("upstart/app-alpha-1.conf")) expect(File.read("/tmp/init/app-alpha-2.conf")).to eq(example_export_file("upstart/app-alpha-2.conf")) expect(File.exists?("/tmp/init/app-bravo-1.conf")).to eq(false) end end context "with alternate templates" do let(:template) { "/tmp/alternate" } let(:options) { { :app => "app", :template => template } } before do FileUtils.mkdir_p template File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" } end it "can export with alternate template files" do upstart.export expect(File.read("/tmp/init/app.conf")).to eq("alternate_template\n") end end context "with alternate templates from home dir" do before do FileUtils.mkdir_p File.expand_path("~/.foreman/templates/upstart") File.open(File.expand_path("~/.foreman/templates/upstart/master.conf.erb"), "w") do |file| file.puts "default_alternate_template" end end it "can export with alternate template files" do upstart.export expect(File.read("/tmp/init/app.conf")).to eq("default_alternate_template\n") end end end foreman-0.87.2/spec/foreman/export/launchd_spec.rb0000644000004100000410000000217613765541231022163 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/launchd" require "tmpdir" describe Foreman::Export::Launchd, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:options) { Hash.new } let(:engine) { Foreman::Engine.new().load_procfile(procfile) } let(:launchd) { Foreman::Export::Launchd.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("launchd") } before(:each) { allow(launchd).to receive(:say) } it "exports to the filesystem" do launchd.export expect(File.read("/tmp/init/app-alpha-1.plist")).to eq(example_export_file("launchd/launchd-a.default")) expect(File.read("/tmp/init/app-bravo-1.plist")).to eq(example_export_file("launchd/launchd-b.default")) end context "with multiple command arguments" do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", "charlie") } it "splits each command argument" do launchd.export expect(File.read("/tmp/init/app-alpha-1.plist")).to eq(example_export_file("launchd/launchd-c.default")) end end end foreman-0.87.2/spec/foreman/export/bluepill_spec.rb0000644000004100000410000000234513765541231022353 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/bluepill" require "tmpdir" describe Foreman::Export::Bluepill, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } let(:options) { Hash.new } let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("bluepill") } before(:each) { allow(bluepill).to receive(:say) } it "exports to the filesystem" do bluepill.export expect(normalize_space(File.read("/tmp/init/app.pill"))).to eq(normalize_space(example_export_file("bluepill/app.pill"))) end it "cleans up if exporting into an existing dir" do expect(FileUtils).to receive(:rm).with("/tmp/init/app.pill") bluepill.export bluepill.export end context "with a process formation" do let(:formation) { "alpha=2" } it "exports to the filesystem with concurrency" do bluepill.export expect(normalize_space(File.read("/tmp/init/app.pill"))).to eq(normalize_space(example_export_file("bluepill/app-concurrency.pill"))) end end end foreman-0.87.2/spec/foreman/export/systemd_spec.rb0000644000004100000410000001444313765541231022235 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/systemd" require "tmpdir" 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) } let(:options) { Hash.new } let(:systemd) { Foreman::Export::Systemd.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("systemd") } before(:each) { allow(systemd).to receive(:say) } 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.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") 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.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.1.service")).to match(/^ExecStart=/) end context "with a custom formation specified" do let(:formation) { "alpha=2" } it "exports only those services that are specified in the formation" do systemd.export 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 template directory specified" do let(:template) { "/tmp/alternate" } let(:options) { { :app => "app", :template => template } } before do FileUtils.mkdir_p template File.open("#{template}/master.target.erb", "w") { |f| f.puts "alternate_template" } 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 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 "home_dir_template" end end it "uses template files found in the user home directory" do systemd.export expect(File.read("/tmp/init/app.target")).to eq("home_dir_template\n") end end end foreman-0.87.2/spec/foreman/export/daemon_spec.rb0000644000004100000410000000731313765541231022006 0ustar www-datawww-datarequire "spec_helper" require "foreman/engine" require "foreman/export/daemon" require "tmpdir" describe Foreman::Export::Daemon, :fakefs do let(:procfile) { write_procfile("/tmp/app/Procfile") } let(:formation) { nil } let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) } let(:options) { Hash.new } let(:daemon) { Foreman::Export::Daemon.new("/tmp/init", engine, options) } before(:each) { load_export_templates_into_fakefs("daemon") } before(:each) { allow(daemon).to receive(:say) } it "exports to the filesystem" do daemon.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("daemon/app.conf")) expect(File.read("/tmp/init/app-alpha.conf")).to eq(example_export_file("daemon/app-alpha.conf")) expect(File.read("/tmp/init/app-alpha-1.conf")).to eq(example_export_file("daemon/app-alpha-1.conf")) expect(File.read("/tmp/init/app-bravo.conf")).to eq(example_export_file("daemon/app-bravo.conf")) expect(File.read("/tmp/init/app-bravo-1.conf")).to eq(example_export_file("daemon/app-bravo-1.conf")) end it "cleans up if exporting into an existing dir" do expect(FileUtils).to receive(:rm).with("/tmp/init/app.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-alpha-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-bravo-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo-bar-1.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar.conf") expect(FileUtils).to receive(:rm).with("/tmp/init/app-foo_bar-1.conf") daemon.export daemon.export end it "does not delete exported files for similarly named applications" do FileUtils.mkdir_p "/tmp/init" ["app2", "app2-alpha", "app2-alpha-1"].each do |name| path = "/tmp/init/#{name}.conf" FileUtils.touch(path) expect(FileUtils).to_not receive(:rm).with(path) end daemon.export end context "with a formation" do let(:formation) { "alpha=2" } it "exports to the filesystem with concurrency" do daemon.export expect(File.read("/tmp/init/app.conf")).to eq(example_export_file("daemon/app.conf")) expect(File.read("/tmp/init/app-alpha.conf")).to eq(example_export_file("daemon/app-alpha.conf")) expect(File.read("/tmp/init/app-alpha-1.conf")).to eq(example_export_file("daemon/app-alpha-1.conf")) expect(File.read("/tmp/init/app-alpha-2.conf")).to eq(example_export_file("daemon/app-alpha-2.conf")) expect(File.exists?("/tmp/init/app-bravo-1.conf")).to eq(false) end end context "with alternate templates" do let(:template) { "/tmp/alternate" } let(:options) { { :app => "app", :template => template } } before do FileUtils.mkdir_p template File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" } end it "can export with alternate template files" do daemon.export expect(File.read("/tmp/init/app.conf")).to eq("alternate_template\n") end end context "with alternate templates from home dir" do before do FileUtils.mkdir_p File.expand_path("~/.foreman/templates/daemon") File.open(File.expand_path("~/.foreman/templates/daemon/master.conf.erb"), "w") do |file| file.puts "default_alternate_template" end end it "can export with alternate template files" do daemon.export expect(File.read("/tmp/init/app.conf")).to eq("default_alternate_template\n") end end end foreman-0.87.2/spec/foreman/cli_spec.rb0000644000004100000410000000656013765541231017774 0ustar www-datawww-datarequire "spec_helper" require "foreman/cli" describe "Foreman::CLI", :fakefs do subject { Foreman::CLI.new } describe ".foreman" do before { File.open(".foreman", "w") { |f| f.puts "formation: alpha=2" } } it "provides default options" do expect(subject.send(:options)["formation"]).to eq("alpha=2") end it "is overridden by options at the cli" do subject = Foreman::CLI.new([], :formation => "alpha=3") expect(subject.send(:options)["formation"]).to eq("alpha=3") end end describe "start" do describe "when a Procfile doesnt exist", :fakefs do it "displays an error" do mock_error(subject, "Procfile does not exist.") do expect_any_instance_of(Foreman::Engine).to_not receive(:start) subject.start end end end describe "with a valid Procfile" do it "can run a single command" do without_fakefs do output = foreman("start env -f #{resource_path("Procfile")}") expect(output).to match(/env.1/) expect(output).not_to match(/test.1/) end end it "can run all commands" do without_fakefs do output = foreman("start -f #{resource_path("Procfile")} -e #{resource_path(".env")}") expect(output).to match(/echo.1 \| echoing/) expect(output).to match(/env.1 \| bar/) expect(output).to match(/test.1 \| testing/) end end it "sets PS variable with the process name" do without_fakefs do output = foreman("start -f #{resource_path("Procfile")}") expect(output).to match(/ps.1 \| PS env var is ps.1/) end end it "fails if process fails" do output = `bundle exec foreman start -f #{resource_path "Procfile.bad"} && echo success` expect(output).not_to include 'success' end end end describe "check" do it "with a valid Procfile displays the jobs" do write_procfile expect(foreman("check")).to eq("valid procfile detected (alpha, bravo, foo_bar, foo-bar)\n") end it "with a blank Procfile displays an error" do FileUtils.touch "Procfile" expect(foreman("check")).to eq("ERROR: no processes defined\n") end it "without a Procfile displays an error" do expect(foreman("check")).to eq("ERROR: Procfile does not exist.\n") end end describe "run" do it "can run a command" do expect(forked_foreman("run echo 1")).to eq("1\n") end it "doesn't parse options for the command" do expect(forked_foreman("run grep -e FOO #{resource_path(".env")}")).to eq("FOO=bar\n") end it "includes the environment" do expect(forked_foreman("run -e #{resource_path(".env")} #{resource_path("bin/env FOO")}")).to eq("bar\n") end it "can run a command from the Procfile" do expect(forked_foreman("run -f #{resource_path("Procfile")} test")).to eq("testing\n") end it "exits with the same exit code as the command" do expect(fork_and_get_exitstatus("run echo 1")).to eq(0) expect(fork_and_get_exitstatus("run date 'invalid_date'")).to eq(1) end end describe "version" do it "displays gem version" do expect(foreman("version").chomp).to eq(Foreman::VERSION) end it "displays gem version on shortcut command" do expect(foreman("-v").chomp).to eq(Foreman::VERSION) end end end foreman-0.87.2/spec/foreman/export_spec.rb0000644000004100000410000000136413765541231020543 0ustar www-datawww-datarequire "spec_helper" require "foreman/export" describe "Foreman::Export" do subject { Foreman::Export } describe "with a formatter that doesn't declare the appropriate class" do it "prints an error" do expect(subject).to receive(:require).with("foreman/export/invalidformatter") mock_export_error("Unknown export format: invalidformatter (no class Foreman::Export::Invalidformatter).") do subject.formatter("invalidformatter") end end end describe "with an invalid formatter" do it "prints an error" do mock_export_error("Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do subject.formatter("invalidformatter") end end end end foreman-0.87.2/spec/foreman/process_spec.rb0000644000004100000410000000432313765541231020676 0ustar www-datawww-datarequire 'spec_helper' require 'foreman/process' require 'ostruct' require 'timeout' require 'tmpdir' describe Foreman::Process do def run(process, options={}) rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY") process.run(options.merge(:output => wr)) rd.gets end describe "#run" do it "runs the process" do process = Foreman::Process.new(resource_path("bin/test")) expect(run(process)).to eq("testing\n") end it "can set environment" do process = Foreman::Process.new(resource_path("bin/env FOO"), :env => { "FOO" => "bar" }) expect(run(process)).to eq("bar\n") end it "can set per-run environment" do process = Foreman::Process.new(resource_path("bin/env FOO")) expect(run(process, :env => { "FOO" => "bar "})).to eq("bar\n") end it "can handle env vars in the command" do process = Foreman::Process.new(resource_path("bin/echo $FOO"), :env => { "FOO" => "bar" }) expect(run(process)).to eq("bar\n") end it "can handle per-run env vars in the command" do process = Foreman::Process.new(resource_path("bin/echo $FOO")) expect(run(process, :env => { "FOO" => "bar" })).to eq("bar\n") end it "should output utf8 properly" do process = Foreman::Process.new(resource_path("bin/utf8")) expect(run(process)).to eq(Foreman.ruby_18? ? "\xFF\x03\n" : "\xFF\x03\n".force_encoding('binary')) end it "can expand env in the command" do process = Foreman::Process.new("command $FOO $BAR", :env => { "FOO" => "bar" }) expect(process.expanded_command).to eq("command bar $BAR") end it "can expand extra env in the command" do process = Foreman::Process.new("command $FOO $BAR", :env => { "FOO" => "bar" }) expect(process.expanded_command("BAR" => "qux")).to eq("command bar qux") end it "can execute" do expect(Kernel).to receive(:exec).with("bin/command") process = Foreman::Process.new("bin/command") process.exec end it "can execute with env" do expect(Kernel).to receive(:exec).with("bin/command bar") process = Foreman::Process.new("bin/command $FOO") process.exec(:env => { "FOO" => "bar" }) end end end foreman-0.87.2/spec/foreman/procfile_spec.rb0000644000004100000410000000312713765541231021024 0ustar www-datawww-datarequire 'spec_helper' require 'foreman/procfile' require 'pathname' require 'tmpdir' describe Foreman::Procfile, :fakefs do subject { Foreman::Procfile.new } it "can load from a file" do write_procfile subject.load "Procfile" expect(subject["alpha"]).to eq("./alpha") expect(subject["bravo"]).to eq("./bravo") end it "loads a passed-in Procfile" do write_procfile procfile = Foreman::Procfile.new("Procfile") expect(procfile["alpha"]).to eq("./alpha") expect(procfile["bravo"]).to eq("./bravo") expect(procfile["foo-bar"]).to eq("./foo-bar") expect(procfile["foo_bar"]).to eq("./foo_bar") end it 'only creates Procfile entries for lines matching regex' do write_procfile procfile = Foreman::Procfile.new("Procfile") keys = procfile.instance_variable_get(:@entries).map(&:first) expect(keys).to match_array(%w[alpha bravo foo-bar foo_bar]) end it "returns nil when attempting to retrieve an non-existing entry" do write_procfile procfile = Foreman::Procfile.new("Procfile") expect(procfile["unicorn"]).to eq(nil) end it "can have a process appended to it" do subject["charlie"] = "./charlie" expect(subject["charlie"]).to eq("./charlie") end it "can write to a string" do subject["foo"] = "./foo" subject["bar"] = "./bar" expect(subject.to_s).to eq("foo: ./foo\nbar: ./bar") end it "can write to a file" do subject["foo"] = "./foo" subject["bar"] = "./bar" Dir.mkdir('/tmp') subject.save "/tmp/proc" expect(File.read("/tmp/proc")).to eq("foo: ./foo\nbar: ./bar\n") end end foreman-0.87.2/spec/spec_helper.rb0000644000004100000410000001001613765541231017044 0ustar www-datawww-data# it throws following message: # W, [2018-02-06T18:16:06.360071 #72025] WARN -- : This usage of the Code Climate Test Reporter is now deprecated. Since version # 1.0, we now require you to run `SimpleCov` in your test/spec helper, and then # run the provided `codeclimate-test-reporter` binary separately to report your # results to Code Climate. # # More information here: https://github.com/codeclimate/ruby-test-reporter/blob/master/README.md # require "codeclimate-test-reporter" # CodeClimate::TestReporter.start require "simplecov" SimpleCov.start do add_filter "/spec/" end require "rspec" require "timecop" require "pp" require "fakefs/safe" require "fakefs/spec_helpers" $:.unshift File.expand_path("../../lib", __FILE__) def mock_export_error(message) expect { yield }.to raise_error(Foreman::Export::Exception, message) end def mock_error(subject, message) mock_exit do expect(subject).to receive(:puts).with("ERROR: #{message}") yield end end def make_pipe IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY") end def foreman(args) capture_stdout do begin Foreman::CLI.start(args.split(" ")) rescue SystemExit end end end def forked_foreman(args) rd, wr = make_pipe Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr) wr.close rd.read end def fork_and_capture(&blk) rd, wr = make_pipe pid = fork do rd.close wr.sync = true $stdout.reopen wr $stderr.reopen wr blk.call $stdout.flush $stdout.close end wr.close Process.wait pid buffer = "" until rd.eof? buffer += rd.gets end end def fork_and_get_exitstatus(args) pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null") Process.wait(pid) $?.exitstatus end def mock_exit(&block) expect(block).to raise_error(SystemExit) end def write_foreman_config(app) File.open("/etc/foreman/#{app}.conf", "w") do |file| file.puts %{#{app}_processes="alpha bravo"} file.puts %{#{app}_alpha="1"} file.puts %{#{app}_bravo="2"} end end def write_procfile(procfile="Procfile", alpha_env="") FileUtils.mkdir_p(File.dirname(procfile)) File.open(procfile, "w") do |file| file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip file.puts "\n" file.puts "bravo:\t./bravo" file.puts "foo_bar:\t./foo_bar" file.puts "foo-bar:\t./foo-bar" file.puts "# baz:\t./baz" end File.expand_path(procfile) end def write_file(file) FileUtils.mkdir_p(File.dirname(file)) File.open(file, 'w') do |f| yield(f) if block_given? end end def write_env(env=".env", options={"FOO"=>"bar"}) File.open(env, "w") do |file| options.each do |key, val| file.puts "#{key}=#{val}" end end end def without_fakefs FakeFS.deactivate! ret = yield FakeFS.activate! ret end def load_export_templates_into_fakefs(type) without_fakefs do Dir[File.expand_path("../../data/export/#{type}/**/*", __FILE__)].inject({}) do |hash, file| next(hash) if File.directory?(file) hash.update(file => File.read(file)) end end.each do |filename, contents| FileUtils.mkdir_p File.dirname(filename) File.open(filename, "w") do |f| f.puts contents end end end def resource_path(filename) File.expand_path("../resources/#{filename}", __FILE__) end def example_export_file(filename) FakeFS.deactivate! data = File.read(File.expand_path(resource_path("export/#{filename}"), __FILE__)) FakeFS.activate! data end def preserving_env old_env = ENV.to_hash begin yield ensure ENV.clear ENV.update(old_env) end end def normalize_space(s) s.gsub(/\n[\n\s]*/, "\n") end def capture_stdout old_stdout = $stdout.dup rd, wr = make_pipe $stdout = wr yield wr.close rd.read ensure $stdout = old_stdout end RSpec.configure do |config| config.color = true config.order = 'rand' config.include FakeFS::SpecHelpers, :fakefs config.before(:each) do FileUtils.mkdir_p('/tmp') end config.after(:each) do FileUtils.rm_rf('/tmp') end end foreman-0.87.2/foreman.gemspec0000644000004100000410000001622713765541231016302 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: foreman 0.87.2 ruby lib Gem::Specification.new do |s| s.name = "foreman".freeze 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 = "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/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 end foreman-0.87.2/data/0000755000004100000410000000000013765541231014207 5ustar www-datawww-dataforeman-0.87.2/data/export/0000755000004100000410000000000013765541231015530 5ustar www-datawww-dataforeman-0.87.2/data/export/systemd/0000755000004100000410000000000013765541231017220 5ustar www-datawww-dataforeman-0.87.2/data/export/systemd/master.target.erb0000644000004100000410000000012213765541231022465 0ustar www-datawww-data[Unit] Wants=<%= service_names.join(' ') %> [Install] WantedBy=multi-user.target foreman-0.87.2/data/export/systemd/process.service.erb0000644000004100000410000000102513765541231023025 0ustar www-datawww-data[Unit] PartOf=<%= app %>.target StopWhenUnneeded=yes [Service] User=<%= user %> WorkingDirectory=<%= engine.root %> Environment=PORT=<%= port %> Environment=PS=<%= process_name %> <% engine.env.each_pair do |var,env| -%> Environment="<%= var %>=<%= env %>" <% end -%> ExecStart=/bin/bash -lc 'exec -a "<%= app %>-<%= process_name %>" <%= process.command %>' Restart=always RestartSec=14s StandardInput=null StandardOutput=syslog StandardError=syslog SyslogIdentifier=%n KillMode=mixed TimeoutStopSec=<%= engine.options[:timeout] %> foreman-0.87.2/data/export/supervisord/0000755000004100000410000000000013765541231020115 5ustar www-datawww-dataforeman-0.87.2/data/export/supervisord/app.conf.erb0000644000004100000410000000146613765541231022322 0ustar www-datawww-data<% app_names = [] engine.each_process do |name, process| 1.upto(engine.formation[name]) do |num| port = engine.port_for(process, num) full_name = "#{app}-#{name}-#{num}" environment = engine.env.merge("PORT" => port.to_s).map do |key, value| value = shell_quote(value) value = value.gsub('\=', '=') value = value.gsub('\&', '&') value = value.gsub('\?', '?') "#{key}=\"#{value}\"" end app_names << full_name -%> [program:<%= full_name %>] command=<%= process.command %> autostart=true autorestart=true stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log user=<%= user %> directory=<%= engine.root %> environment=<%= environment.join(',') %> <% end end -%> [group:<%= app %>] programs=<%= app_names.join(',') %> foreman-0.87.2/data/export/runit/0000755000004100000410000000000013765541231016671 5ustar www-datawww-dataforeman-0.87.2/data/export/runit/log/0000755000004100000410000000000013765541231017452 5ustar www-datawww-dataforeman-0.87.2/data/export/runit/log/run.erb0000644000004100000410000000024713765541231020753 0ustar www-datawww-data#!/bin/sh set -e LOG=<%= log %>/<%= name %>-<%= num %> test -d "$LOG" || mkdir -p -m 2750 "$LOG" && chown <%= user %> "$LOG" exec chpst -u <%= user %> svlogd "$LOG" foreman-0.87.2/data/export/runit/run.erb0000644000004100000410000000022613765541231020167 0ustar www-datawww-data#!/bin/sh cd <%= engine.root %> exec 2>&1 exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %> foreman-0.87.2/data/export/upstart/0000755000004100000410000000000013765541231017232 5ustar www-datawww-dataforeman-0.87.2/data/export/upstart/process.conf.erb0000644000004100000410000000050513765541231022326 0ustar www-datawww-datastart on starting <%= app %>-<%= name %> stop on stopping <%= app %>-<%= name %> respawn env PORT=<%= port %> <% engine.env.each do |name,value| -%> <% next if name.upcase == "PORT" -%> env <%= name %>='<%= value.gsub(/'/, "'\"'\"'") %>' <% end -%> setuid <%= user %> chdir <%= engine.root %> exec <%= process.command %> foreman-0.87.2/data/export/upstart/process_master.conf.erb0000644000004100000410000000007113765541231023677 0ustar www-datawww-datastart on starting <%= app %> stop on stopping <%= app %> foreman-0.87.2/data/export/upstart/master.conf.erb0000644000004100000410000000006213765541231022141 0ustar www-datawww-datastart on runlevel [2345] stop on runlevel [!2345] foreman-0.87.2/data/export/launchd/0000755000004100000410000000000013765541231017146 5ustar www-datawww-dataforeman-0.87.2/data/export/launchd/launchd.plist.erb0000644000004100000410000000200513765541231022405 0ustar www-datawww-data Label <%= "#{app}-#{name}-#{num}" %> EnvironmentVariables <%- engine.env.merge("PORT" => port).each_pair do |var,env| -%> <%= var %> <%= env %> <%- end -%> ProgramArguments <%- command_args.each do |command| -%> <%= command %> <%- end -%> KeepAlive RunAtLoad StandardOutPath <%= log %>/<%= app %>-<%= name %>-<%=num%>.log StandardErrorPath <%= log %>/<%= app %>-<%= name %>-<%=num%>.log UserName <%= user %> WorkingDirectory <%= engine.root %> foreman-0.87.2/data/export/daemon/0000755000004100000410000000000013765541231016773 5ustar www-datawww-dataforeman-0.87.2/data/export/daemon/process.conf.erb0000644000004100000410000000072313765541231022071 0ustar www-datawww-datastart on starting <%= app %>-<%= name.gsub('_', '-') %> stop on stopping <%= app %>-<%= name.gsub('_', '-') %> respawn env PORT=<%= port %><% engine.env.each_pair do |var, env| %> env <%= var %>=<%= env %><% end %> exec start-stop-daemon --start --chuid <%= user %> --chdir <%= engine.root %> --make-pidfile --pidfile <%= run %>/<%= app %>-<%= name %>-<%= num %>.pid --exec <%= executable %><%= arguments %> >> <%= log %>/<%= app %>-<%= name %>-<%= num %>.log 2>&1 foreman-0.87.2/data/export/daemon/process_master.conf.erb0000644000004100000410000000007113765541231023440 0ustar www-datawww-datastart on starting <%= app %> stop on stopping <%= app %> foreman-0.87.2/data/export/daemon/master.conf.erb0000644000004100000410000000031213765541231021700 0ustar www-datawww-datapre-start script bash << "EOF" mkdir -p <%= log %> chown -R <%= user %> <%= log %> mkdir -p <%= run %> chown -R <%= user %> <%= run %> EOF end script start on runlevel [2345] stop on runlevel [016] foreman-0.87.2/data/export/bluepill/0000755000004100000410000000000013765541231017340 5ustar www-datawww-dataforeman-0.87.2/data/export/bluepill/master.pill.erb0000644000004100000410000000164413765541231022271 0ustar www-datawww-dataBluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app| app.uid = "<%= user %>" app.gid = "<%= user %>" <% engine.each_process do |name, process| %> <% 1.upto(engine.formation[name]) do |num| %> <% port = engine.port_for(process, num) %> app.process("<%= name %>-<%= num %>") do |process| process.start_command = %Q{<%= process.command %>} process.working_dir = "<%= engine.root %>" process.daemonize = true process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %> process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill] process.stop_grace_time = 45.seconds process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log" process.monitor_children do |children| children.stop_command "kill {{PID}}" end process.group = "<%= app %>-<%= name %>" end <% end %> <% end %> end foreman-0.87.2/data/example/0000755000004100000410000000000013765541231015642 5ustar www-datawww-dataforeman-0.87.2/data/example/ticker0000755000004100000410000000035513765541231017054 0ustar www-datawww-data#!/usr/bin/env ruby $stdout.sync = true %w( SIGINT SIGTERM ).each do |signal| trap(signal) do puts "received #{signal} but i'm ignoring it!" end end while true puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}" sleep 1 end foreman-0.87.2/data/example/Procfile0000644000004100000410000000013313765541231017325 0ustar www-datawww-dataticker: ruby ./ticker $PORT error: ruby ./error utf8: ruby ./utf8 spawner: ./spawner foreman-0.87.2/data/example/log/0000755000004100000410000000000013765541231016423 5ustar www-datawww-dataforeman-0.87.2/data/example/log/neverdie.log0000644000004100000410000000010213765541231020720 0ustar www-datawww-datatick tick ./never_die:6:in `sleep': Interrupt from ./never_die:6 foreman-0.87.2/data/example/utf80000755000004100000410000000036413765541231016461 0ustar www-datawww-data#!/usr/bin/env ruby # encoding: BINARY $stdout.sync = true while true puts "\u65e5\u672c\u8a9e\u6587\u5b57\u5217" puts "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628\u041a\u0430\u043b\u0438\u043d\u0430" puts "\xff\x03" sleep 1 end foreman-0.87.2/data/example/spawnee0000755000004100000410000000021613765541231017231 0ustar www-datawww-data#!/bin/sh NAME="$1" sigterm() { echo "$NAME: got sigterm" } #trap sigterm SIGTERM while true; do echo "$NAME: ping $$" sleep 1 done foreman-0.87.2/data/example/Procfile.without_colon0000644000004100000410000000004413765541231022222 0ustar www-datawww-dataticker ./ticker $PORT error ./errorforeman-0.87.2/data/example/error0000755000004100000410000000013113765541231016714 0ustar www-datawww-data#!/usr/bin/env ruby $stdout.sync = true puts "will error in 10s" sleep 5 raise "Dying" foreman-0.87.2/data/example/spawner0000755000004100000410000000007313765541231017247 0ustar www-datawww-data#!/bin/sh ./spawnee A & ./spawnee B & ./spawnee C & wait foreman-0.87.2/man/0000755000004100000410000000000013765541231014051 5ustar www-datawww-dataforeman-0.87.2/man/foreman.10000644000004100000410000001477713765541231015602 0ustar www-datawww-data.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "FOREMAN" "1" "April 2020" "Foreman 0.87.2" "Foreman Manual" . .SH "NAME" \fBforeman\fR \- manage Procfile\-based applications . .SH "SYNOPSIS" \fBforeman start [process]\fR . .br \fBforeman run \fR . .br \fBforeman export [location]\fR . .SH "DESCRIPTION" Foreman is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\. . .SH "RUNNING" \fBforeman start\fR is used to run your application directly from the command line\. . .P If no additional parameters are passed, foreman will run one instance of each type of process defined in your Procfile\. . .P If a parameter is passed, foreman will run one instance of the specified application type\. . .P The following options control how the application is run: . .TP \fB\-m\fR, \fB\-\-formation\fR Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR . .TP \fB\-e\fR, \fB\-\-env\fR Specify one or more \.env files to load . .TP \fB\-f\fR, \fB\-\-procfile\fR Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\. . .TP \fB\-p\fR, \fB\-\-port\fR Specify which port to use as the base for this application\. Should be a multiple of 1000\. . .TP \fB\-t\fR, \fB\-\-timeout\fR Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5\. . .P \fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\. . .SH "EXPORTING" \fBforeman export\fR is used to export your application to another process management format\. . .P A location to export can be passed as an argument\. This argument may be either required or optional depending on the export format\. . .P The following options control how the application is run: . .TP \fB\-a\fR, \fB\-\-app\fR Use this name rather than the application\'s root directory name as the name of the application when exporting\. . .TP \fB\-m\fR, \fB\-\-formation\fR Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR . .TP \fB\-l\fR, \fB\-\-log\fR Specify the directory to place process logs in\. . .TP \fB\-p\fR, \fB\-\-port\fR Specify which port to use as the base for this application\. Should be a multiple of 1000\. . .TP \fB\-t\fR, \fB\-\-template\fR Specify an alternate template to use for creating export files\. See \fIhttps://github\.com/ddollar/foreman/tree/master/data/export\fR for examples\. . .TP \fB\-u\fR, \fB\-\-user\fR Specify the user the application should be run as\. Defaults to the app name . .SH "GLOBAL OPTIONS" These options control all modes of foreman\'s operation\. . .TP \fB\-d\fR, \fB\-\-root\fR Specify an alternate application root\. This defaults to the directory containing the Procfile\. . .TP \fB\-e\fR, \fB\-\-env\fR Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\. . .TP \fB\-f\fR, \fB\-\-procfile\fR Specify an alternate location for the application\'s Procfile\. This file\'s containing directory will be assumed to be the root directory of the application\. . .SH "EXPORT FORMATS" foreman currently supports the following output formats: . .IP "\(bu" 4 bluepill . .IP "\(bu" 4 inittab . .IP "\(bu" 4 launchd . .IP "\(bu" 4 runit . .IP "\(bu" 4 supervisord . .IP "\(bu" 4 systemd . .IP "\(bu" 4 upstart . .IP "" 0 . .SH "INITTAB EXPORT" Will export a chunk of inittab\-compatible configuration: . .IP "" 4 . .nf # \-\-\-\-\- foreman example processes \-\-\-\-\- EX01:4:respawn:/bin/su \- example \-c \'PORT=5000 bundle exec thin start >> /var/log/web\-1\.log 2>&1\' EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >> /var/log/job\-1\.log 2>&1\' # \-\-\-\-\- end foreman example processes \-\-\-\-\- . .fi . .IP "" 0 . .SH "SYSTEMD EXPORT" Will create a series of systemd scripts in the location you specify\. Scripts will be structured to make the following commands valid: . .P \fBsystemctl start appname\.target\fR . .P \fBsystemctl stop appname\-processname\.target\fR . .P \fBsystemctl restart appname\-processname\-3\.service\fR . .SH "UPSTART EXPORT" Will create a series of upstart scripts in the location you specify\. Scripts will be structured to make the following commands valid: . .P \fBstart appname\fR . .P \fBstop appname\-processname\fR . .P \fBrestart appname\-processname\-3\fR . .SH "PROCFILE" A Procfile should contain both a name for the process and the command used to run it\. . .IP "" 4 . .nf web: bundle exec thin start job: bundle exec rake jobs:work . .fi . .IP "" 0 . .P A process name may contain letters, numbers and the underscore character\. You can validate your Procfile format using the \fBcheck\fR command: . .IP "" 4 . .nf $ foreman check . .fi . .IP "" 0 . .P The special environment variables \fB$PORT\fR and \fB$PS\fR are available within the Procfile\. \fB$PORT\fR is the port selected for that process\. \fB$PS\fR is the name of the process for the line\. . .P The \fB$PORT\fR value starts as the base port as specified by \fB\-p\fR, then increments by 100 for each new process line\. Multiple instances of the same process are assigned \fB$PORT\fR values that increment by 1\. . .SH "ENVIRONMENT" If a \fB\.env\fR file exists in the current directory, the default environment will be read from it\. This file should contain key/value pairs, separated by \fB=\fR, with one key/value pair per line\. . .IP "" 4 . .nf FOO=bar BAZ=qux . .fi . .IP "" 0 . .SH "DEFAULT OPTIONS" If a \fB\.foreman\fR file exists in the current directory, default options will be read from it\. This file should be in YAML format with the long option name as keys\. Example: . .IP "" 4 . .nf formation: alpha=0,bravo=1 port: 15000 . .fi . .IP "" 0 . .SH "EXAMPLES" Start one instance of each process type, interleave the output on stdout: . .IP "" 4 . .nf $ foreman start . .fi . .IP "" 0 . .P Export the application in upstart format: . .IP "" 4 . .nf $ foreman export upstart /etc/init . .fi . .IP "" 0 . .P Run one process type from the application defined in a specific Procfile: . .IP "" 4 . .nf $ foreman start alpha \-f ~/myapp/Procfile . .fi . .IP "" 0 . .P Start all processes except the one named worker: . .IP "" 4 . .nf $ foreman start \-m all=1,worker=0 . .fi . .IP "" 0 . .SH "COPYRIGHT" Foreman is Copyright (C) 2010 David Dollar \fIhttp://daviddollar\.org\fR foreman-0.87.2/lib/0000755000004100000410000000000013765541231014044 5ustar www-datawww-dataforeman-0.87.2/lib/foreman.rb0000644000004100000410000000046313765541231016023 0ustar www-datawww-datarequire "foreman/version" module Foreman def self.runner File.expand_path("../../bin/foreman-runner", __FILE__) end def self.ruby_18? defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/ end def self.windows? defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/ end end foreman-0.87.2/lib/foreman/0000755000004100000410000000000013765541231015473 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/version.rb0000644000004100000410000000005213765541231017502 0ustar www-datawww-datamodule Foreman VERSION = "0.87.2" end foreman-0.87.2/lib/foreman/export/0000755000004100000410000000000013765541231017014 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/export/runit.rb0000644000004100000410000000165613765541231020512 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Runit < Foreman::Export::Base ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/ def export super engine.each_process do |name, process| 1.upto(engine.formation[name]) do |num| process_directory = "#{app}-#{name}-#{num}" create_directory process_directory create_directory "#{process_directory}/env" create_directory "#{process_directory}/log" write_template "runit/run.erb", "#{process_directory}/run", binding chmod 0755, "#{process_directory}/run" port = engine.port_for(process, num) engine.env.merge("PORT" => port.to_s).each do |key, value| write_file "#{process_directory}/env/#{key}", value end write_template "runit/log/run.erb", "#{process_directory}/log/run", binding chmod 0755, "#{process_directory}/log/run" end end end end foreman-0.87.2/lib/foreman/export/systemd.rb0000644000004100000410000000160213765541231021030 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Systemd < Foreman::Export::Base def export super Dir["#{location}/#{app}*.target"] .concat(Dir["#{location}/#{app}*.service"]) .concat(Dir["#{location}/#{app}*.target.wants/#{app}*.service"]) .each do |file| clean file end Dir["#{location}/#{app}*.target.wants"].each do |file| clean_dir file end service_names = [] engine.each_process do |name, process| 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 end write_template "systemd/master.target.erb", "#{app}.target", binding end end foreman-0.87.2/lib/foreman/export/bluepill.rb0000644000004100000410000000035513765541231021154 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Bluepill < Foreman::Export::Base def export super clean "#{location}/#{app}.pill" write_template "bluepill/master.pill.erb", "#{app}.pill", binding end end foreman-0.87.2/lib/foreman/export/inittab.rb0000644000004100000410000000213613765541231020775 0ustar www-datawww-datarequire "foreman/export" class Foreman::Export::Inittab < Foreman::Export::Base def export error("Must specify a location") unless location inittab = [] inittab << "# ----- foreman #{app} processes -----" index = 1 engine.each_process do |name, process| 1.upto(engine.formation[name]) do |num| id = app.slice(0, 2).upcase + sprintf("%02d", index) port = engine.port_for(process, num) commands = [] commands << "cd #{engine.root}" commands << "export PORT=#{port}" engine.env.each_pair do |var, env| commands << "export #{var.upcase}=#{shell_quote(env)}" end commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1" inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'" index += 1 end end inittab << "# ----- end foreman #{app} processes -----" inittab = inittab.join("\n") + "\n" if location == "-" puts inittab else say "writing: #{location}" File.open(location, "w") { |file| file.puts inittab } end end end foreman-0.87.2/lib/foreman/export/base.rb0000644000004100000410000001046613765541231020262 0ustar www-datawww-datarequire "foreman/export" require "ostruct" require "pathname" require "shellwords" class Foreman::Export::Base attr_reader :location attr_reader :engine attr_reader :options attr_reader :formation # deprecated attr_reader :port def initialize(location, engine, options={}) @location = location @engine = engine @options = options.dup @formation = engine.formation # deprecated def port Foreman::Export::Base.warn_deprecation! engine.base_port end # deprecated def template Foreman::Export::Base.warn_deprecation! options[:template] end # deprecated def @engine.procfile Foreman::Export::Base.warn_deprecation! @processes.map do |process| OpenStruct.new( :name => @names[process], :process => process ) end end end def export error("Must specify a location") unless location FileUtils.mkdir_p(location) rescue error("Could not create: #{location}") chown user, log chown user, run end def app options[:app] || "app" end def log options[:log] || "/var/log/#{app}" end def run options[:run] || "/var/run/#{app}" end def user options[:user] || app end private ###################################################################### def self.warn_deprecation! @@deprecation_warned ||= false return if @@deprecation_warned puts "WARNING: Using deprecated exporter interface. Please update your exporter" puts "the interface shown in the upstart exporter:" puts puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb" puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb" puts @@deprecation_warned = true end def chown user, dir FileUtils.chown user, nil, dir rescue error("Could not chown #{dir} to #{user}") unless File.writable?(dir) || ! File.exists?(dir) end def error(message) raise Foreman::Export::Exception.new(message) end def say(message) puts "[foreman export] %s" % message end def clean(filename) return unless File.exists?(filename) say "cleaning up: #{filename}" FileUtils.rm(filename) end def clean_dir(dirname) return unless File.exists?(dirname) say "cleaning up directory: #{dirname}" FileUtils.rm_r(dirname) end def shell_quote(value) Shellwords.escape(value) end # deprecated def old_export_template(exporter, file, template_root) if template_root && File.exist?(file_path = File.join(template_root, file)) File.read(file_path) elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file))) File.read(file_path) else File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__)) end end def export_template(name, file=nil, template_root=nil) if file && template_root old_export_template name, file, template_root else name_without_first = name.split("/")[1..-1].join("/") matchers = [] matchers << File.join(options[:template], name_without_first) if options[:template] matchers << File.expand_path("~/.foreman/templates/#{name}") matchers << File.expand_path("../../../../data/export/#{name}", __FILE__) File.read(matchers.detect { |m| File.exists?(m) }) end end def write_template(name, target, binding) compiled = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+ ERB.new(export_template(name), trim_mode: '-').result(binding) else ERB.new(export_template(name), nil, '-').result(binding) end write_file target, compiled end def chmod(mode, file) say "setting #{file} to mode #{mode}" FileUtils.chmod mode, File.join(location, file) end def create_directory(dir) say "creating: #{dir}" FileUtils.mkdir_p(File.join(location, dir)) end def create_symlink(link, target) say "symlinking: #{link} -> #{target}" FileUtils.symlink(target, File.join(location, link)) end def write_file(filename, contents) say "writing: #{filename}" filename = File.join(location, filename) unless Pathname.new(filename).absolute? File.open(filename, "w") do |file| file.puts contents end end end foreman-0.87.2/lib/foreman/export/upstart.rb0000644000004100000410000000202513765541231021042 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Upstart < Foreman::Export::Base def export super master_file = "#{app}.conf" clean File.join(location, master_file) write_template master_template, master_file, binding engine.each_process do |name, process| process_master_file = "#{app}-#{name}.conf" process_file = "#{app}-#{name}-%s.conf" Dir[ File.join(location, process_master_file), File.join(location, process_file % "*") ].each { |f| clean(f) } next if engine.formation[name] < 1 write_template process_master_template, process_master_file, binding 1.upto(engine.formation[name]) do |num| port = engine.port_for(process, num) write_template process_template, process_file % num, binding end end end private def master_template "upstart/master.conf.erb" end def process_master_template "upstart/process_master.conf.erb" end def process_template "upstart/process.conf.erb" end end foreman-0.87.2/lib/foreman/export/supervisord.rb0000644000004100000410000000043113765541231021724 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Supervisord < Foreman::Export::Base def export super Dir["#{location}/#{app}.conf"].each do |file| clean file end write_template "supervisord/app.conf.erb", "#{app}.conf", binding end end foreman-0.87.2/lib/foreman/export/daemon.rb0000644000004100000410000000150613765541231020606 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Daemon < Foreman::Export::Base def export super (Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file| clean file end write_template "daemon/master.conf.erb", "#{app}.conf", binding engine.each_process do |name, process| next if engine.formation[name] < 1 write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding 1.upto(engine.formation[name]) do |num| port = engine.port_for(process, num) arguments = process.command.split(" ") executable = arguments.slice!(0) arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : "" write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding end end end end foreman-0.87.2/lib/foreman/export/launchd.rb0000644000004100000410000000102213765541231020752 0ustar www-datawww-datarequire "erb" require "foreman/export" class Foreman::Export::Launchd < Foreman::Export::Base def export super engine.each_process do |name, process| 1.upto(engine.formation[name]) do |num| port = engine.port_for(process, num) command_args = process.command.split(/\s+/).map{|arg| case arg when "$PORT" then port else arg end } write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding end end end end foreman-0.87.2/lib/foreman/export.rb0000644000004100000410000000177313765541231017351 0ustar www-datawww-datarequire "foreman" require "foreman/helpers" require "pathname" module Foreman::Export extend Foreman::Helpers class Exception < ::Exception; end def self.formatter(format) begin require "foreman/export/#{ format.tr('-', '_') }" classy_format = classify(format) formatter = constantize("Foreman::Export::#{ classy_format }") rescue NameError => ex error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })." rescue LoadError => ex error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')." end end def self.error(message) raise Foreman::Export::Exception.new(message) end end require "foreman/export/base" require "foreman/export/inittab" require "foreman/export/upstart" require "foreman/export/daemon" require "foreman/export/bluepill" require "foreman/export/runit" require "foreman/export/supervisord" require "foreman/export/launchd" require "foreman/export/systemd" foreman-0.87.2/lib/foreman/helpers.rb0000644000004100000410000000244013765541231017462 0ustar www-datawww-datamodule Foreman::Helpers # Copied whole sale from, https://github.com/defunkt/resque/ # Given a word with dashes, returns a camel cased version of it. # # classify('job-name') # => 'JobName' def classify(dashed_word) dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join end # Tries to find a constant with the name specified in the argument string: # # constantize("Module") # => Module # constantize("Test::Unit") # => Test::Unit # # The name is assumed to be the one of a top-level constant, no matter # whether it starts with "::" or not. No lexical context is taken into # account: # # C = 'outside' # module M # C = 'inside' # C # => 'inside' # constantize("C") # => 'outside', same as ::C # end # # NameError is raised when the constant is unknown. def constantize(camel_cased_word) camel_cased_word = camel_cased_word.to_s names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? constant = Object names.each do |name| args = Module.method(:const_get).arity != 1 ? [false] : [] if constant.const_defined?(name, *args) constant = constant.const_get(name) else constant = constant.const_missing(name) end end constant end end foreman-0.87.2/lib/foreman/procfile.rb0000644000004100000410000000356213765541231017631 0ustar www-datawww-datarequire "foreman" # Reads and writes Procfiles # # A valid Procfile entry is captured by this regex: # # /^([A-Za-z0-9_]+):\s*(.+)$/ # # All other lines are ignored. # class Foreman::Procfile # Initialize a Procfile # # @param [String] filename (nil) An optional filename to read from # def initialize(filename=nil) @entries = [] load(filename) if filename end # Yield each +Procfile+ entry in order # def entries @entries.each do |(name, command)| yield name, command end end # Retrieve a +Procfile+ command by name # # @param [String] name The name of the Procfile entry to retrieve # def [](name) if entry = @entries.detect { |n,c| name == n } entry.last end end # Create a +Procfile+ entry # # @param [String] name The name of the +Procfile+ entry to create # @param [String] command The command of the +Procfile+ entry to create # def []=(name, command) delete name @entries << [name, command] end # Remove a +Procfile+ entry # # @param [String] name The name of the +Procfile+ entry to remove # def delete(name) @entries.reject! { |n,c| name == n } end # Load a Procfile from a file # # @param [String] filename The filename of the +Procfile+ to load # def load(filename) @entries.replace parse(filename) end # Save a Procfile to a file # # @param [String] filename Save the +Procfile+ to this file # def save(filename) File.open(filename, 'w') do |file| file.puts self.to_s end end # Get the +Procfile+ as a +String+ # def to_s @entries.map do |name, command| [ name, command ].join(": ") end.join("\n") end private def parse(filename) File.read(filename).gsub("\r\n","\n").split("\n").map do |line| if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/ [$1, $2] end end.compact end end foreman-0.87.2/lib/foreman/engine.rb0000644000004100000410000002773013765541231017276 0ustar www-datawww-datarequire "foreman" require "foreman/env" require "foreman/process" require "foreman/procfile" require "tempfile" require "fileutils" require "thread" class Foreman::Engine # The signals that the engine cares about. # HANDLED_SIGNALS = [ :TERM, :INT, :HUP, :USR1, :USR2 ] attr_reader :env attr_reader :options attr_reader :processes # Create an +Engine+ for running processes # # @param [Hash] options # # @option options [String] :formation (all=1) The process formation to use # @option options [Fixnum] :port (5000) The base port to assign to processes # @option options [String] :root (Dir.pwd) The root directory from which to run processes # def initialize(options={}) @options = options.dup @options[:formation] ||= "all=1" @options[:timeout] ||= 5 @env = {} @mutex = Mutex.new @names = {} @processes = [] @running = {} @readers = {} @shutdown = false # Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html) reader, writer = create_pipe reader.close_on_exec = true if reader.respond_to?(:close_on_exec) writer.close_on_exec = true if writer.respond_to?(:close_on_exec) @selfpipe = { :reader => reader, :writer => writer } # Set up a global signal queue # http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html Thread.main[:signal_queue] = [] end # Start the processes registered to this +Engine+ # def start register_signal_handlers startup spawn_processes watch_for_output sleep 0.1 wait_for_shutdown_or_child_termination shutdown exit(@exitstatus) if @exitstatus end # Set up deferred signal handlers # def register_signal_handlers HANDLED_SIGNALS.each do |sig| if ::Signal.list.include? sig.to_s trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal } end end end # Unregister deferred signal handlers # def restore_default_signal_handlers HANDLED_SIGNALS.each do |sig| trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s end end # Wake the main thread up via the selfpipe when there's a signal # def notice_signal @selfpipe[:writer].write_nonblock( '.' ) rescue Errno::EAGAIN # Ignore writes that would block rescue Errno::EINTR # Retry if another signal arrived while writing retry end # Invoke the real handler for signal +sig+. This shouldn't be called directly # by signal handlers, as it might invoke code which isn't re-entrant. # # @param [Symbol] sig the name of the signal to be handled # def handle_signal(sig) case sig when :TERM handle_term_signal when :INT handle_interrupt when :HUP handle_hangup when *HANDLED_SIGNALS handle_signal_forward(sig) else system "unhandled signal #{sig}" end end # Handle a TERM signal # def handle_term_signal system "SIGTERM received, starting shutdown" @shutdown = true end # Handle an INT signal # def handle_interrupt system "SIGINT received, starting shutdown" @shutdown = true end # Handle a HUP signal # def handle_hangup system "SIGHUP received, starting shutdown" @shutdown = true end def handle_signal_forward(signal) system "#{signal} received, forwarding it to children" kill_children signal end # Register a process to be run by this +Engine+ # # @param [String] name A name for this process # @param [String] command The command to run # @param [Hash] options # # @option options [Hash] :env A custom environment for this process # def register(name, command, options={}) options[:env] ||= env options[:cwd] ||= File.dirname(command.split(" ").first) process = Foreman::Process.new(command, options) @names[process] = name @processes << process end # Clear the processes registered to this +Engine+ # def clear @names = {} @processes = [] end # Register processes by reading a Procfile # # @param [String] filename A Procfile from which to read processes to register # def load_procfile(filename) options[:root] ||= File.dirname(filename) Foreman::Procfile.new(filename).entries do |name, command| register name, command, :cwd => options[:root] end self end # Load a .env file into the +env+ for this +Engine+ # # @param [String] filename A .env file to load into the environment # def load_env(filename) Foreman::Env.new(filename).entries do |name, value| @env[name] = value end end # Send a signal to all processes started by this +Engine+ # # @param [String] signal The signal to send to each process # def kill_children(signal="SIGTERM") if Foreman.windows? @running.each do |pid, (process, index)| system "sending #{signal} to #{name_for(pid)} at pid #{pid}" begin Process.kill(signal, pid) rescue Errno::ESRCH, Errno::EPERM end end else begin pids = @running.keys.compact Process.kill signal, *pids unless pids.empty? rescue Errno::ESRCH, Errno::EPERM end end end # Send a signal to the whole process group. # # @param [String] signal The signal to send # def killall(signal="SIGTERM") if Foreman.windows? kill_children(signal) else begin Process.kill "-#{signal}", Process.pid rescue Errno::ESRCH, Errno::EPERM end end end # Get the process formation # # @returns [Fixnum] The formation count for the specified process # def formation @formation ||= parse_formation(options[:formation]) end # List the available process names # # @returns [Array] A list of process names # def process_names @processes.map { |p| @names[p] } end # Get the +Process+ for a specifid name # # @param [String] name The process name # # @returns [Foreman::Process] The +Process+ for the specified name # def process(name) @names.invert[name] end # Yield each +Process+ in order # def each_process process_names.each do |name| yield name, process(name) end end # Get the root directory for this +Engine+ # # @returns [String] The root directory # def root File.expand_path(options[:root] || Dir.pwd) end # Get the port for a given process and offset # # @param [Foreman::Process] process A +Process+ associated with this engine # @param [Fixnum] instance The instance of the process # # @returns [Fixnum] port The port to use for this instance of this process # def port_for(process, instance, base=nil) if base base + (@processes.index(process.process) * 100) + (instance - 1) else base_port + (@processes.index(process) * 100) + (instance - 1) end end # Get the base port for this foreman instance # # @returns [Fixnum] port The base port # def base_port (options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i end # deprecated def environment env end private ### Engine API ###################################################### def startup raise TypeError, "must use a subclass of Foreman::Engine" end def output(name, data) raise TypeError, "must use a subclass of Foreman::Engine" end def shutdown raise TypeError, "must use a subclass of Foreman::Engine" end ## Helpers ########################################################## def create_pipe IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY") end def name_for(pid) process, index = @running[pid] name_for_index(process, index) end def name_for_index(process, index) [ @names[process], index.to_s ].compact.join(".") end def parse_formation(formation) pairs = formation.to_s.gsub(/\s/, "").split(",") pairs.inject(Hash.new(0)) do |ax, pair| process, amount = pair.split("=") process == "all" ? ax.default = amount.to_i : ax[process] = amount.to_i ax end end def output_with_mutex(name, message) @mutex.synchronize do output name, message end end def system(message) output_with_mutex "system", message end def termination_message_for(status) if status.exited? "exited with code #{status.exitstatus}" elsif status.signaled? "terminated by SIG#{Signal.list.invert[status.termsig]}" else "died a mysterious death" end end def flush_reader(reader) until reader.eof? data = reader.gets output_with_mutex name_for(@readers.key(reader)), data end end ## Engine ########################################################### def spawn_processes @processes.each do |process| 1.upto(formation[@names[process]]) do |n| reader, writer = create_pipe begin pid = process.run(:output => writer, :env => { "PORT" => port_for(process, n).to_s, "PS" => name_for_index(process, n) }) writer.puts "started with pid #{pid}" rescue Errno::ENOENT writer.puts "unknown command: #{process.command}" end @running[pid] = [process, n] @readers[pid] = reader end end end def read_self_pipe @selfpipe[:reader].read_nonblock(11) rescue Errno::EAGAIN, Errno::EINTR, Errno::EBADF, Errno::EWOULDBLOCK # ignore end def handle_signals while sig = Thread.main[:signal_queue].shift self.handle_signal(sig) end end def handle_io(readers) readers.each do |reader| next if reader == @selfpipe[:reader] if reader.eof? @readers.delete_if { |key, value| value == reader } else data = reader.gets output_with_mutex name_for(@readers.invert[reader]), data end end end def watch_for_output Thread.new do begin loop do io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30) read_self_pipe handle_signals handle_io(io ? io.first : []) end rescue Exception => ex puts ex.message puts ex.backtrace end end end def wait_for_shutdown_or_child_termination loop do # Stop if it is time to shut down (asked via a signal) break if @shutdown # Stop if any of the children died break if check_for_termination # Sleep for a moment and do not blow up if any signals are coming our way begin sleep(1) rescue Exception # noop end end # Ok, we have exited from the main loop, time to shut down gracefully terminate_gracefully end def check_for_termination # Check if any of the children have died off pid, status = begin Process.wait2(-1, Process::WNOHANG) rescue Errno::ECHILD return nil end # record the exit status @exitstatus ||= status.exitstatus if status # If no childred have died, nothing to do here return nil unless pid # Log the information about the process that exited output_with_mutex name_for(pid), termination_message_for(status) # Delete it from the list of running processes and return its pid @running.delete(pid) return pid end def terminate_gracefully restore_default_signal_handlers # Tell all children to stop gracefully if Foreman.windows? system "sending SIGKILL to all processes" kill_children "SIGKILL" else system "sending SIGTERM to all processes" kill_children "SIGTERM" end # Wait for all children to stop or until the time comes to kill them all start_time = Time.now while Time.now - start_time <= options[:timeout] return if @running.empty? check_for_termination # Sleep for a moment and do not blow up if more signals are coming our way begin sleep(0.1) rescue Exception # noop end end # Ok, we have no other option than to kill all of our children system "sending SIGKILL to all processes" kill_children "SIGKILL" end end foreman-0.87.2/lib/foreman/vendor/0000755000004100000410000000000013765541231016770 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/0000755000004100000410000000000013765541231017744 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/0000755000004100000410000000000013765541231020512 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor.rb0000644000004100000410000003641613765541231022025 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/0000755000004100000410000000000013765541231021466 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/0000755000004100000410000000000013765541231023276 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb0000644000004100000410000000361513765541231031504 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/ordered_hash.rb0000644000004100000410000000513613765541231026257 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/core_ext/io_binary_read.rb0000644000004100000410000000046413765541231026575 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/0000755000004100000410000000000013765541231022762 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/arguments.rb0000644000004100000410000001062713765541231025322 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/option.rb0000644000004100000410000000761213765541231024625 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/options.rb0000644000004100000410000001346513765541231025013 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser/argument.rb0000644000004100000410000000340413765541231025132 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/runner.rb0000644000004100000410000002367513765541231023341 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor.rb0000644000004100000410000000065713765541231024320 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/version.rb0000644000004100000410000000005513765541231023500 0ustar www-datawww-dataclass Foreman::Thor VERSION = "0.19.4" end foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/group.rb0000644000004100000410000002156113765541231023154 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions.rb0000644000004100000410000002406713765541231023464 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/0000755000004100000410000000000013765541231022575 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/color.rb0000644000004100000410000001125713765541231024246 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/basic.rb0000644000004100000410000003075513765541231024215 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell/html.rb0000644000004100000410000001055713765541231024076 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/base.rb0000644000004100000410000005555013765541231022737 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/shell.rb0000644000004100000410000000444713765541231023133 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/error.rb0000644000004100000410000000165713765541231023155 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/invocation.rb0000644000004100000410000001406713765541231024174 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/rake_compat.rb0000644000004100000410000000414413765541231024303 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/command.rb0000644000004100000410000001100313765541231023424 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/0000755000004100000410000000000013765541231023126 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/file_manipulation.rb0000644000004100000410000002606213765541231027160 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/inject_into_file.rb0000644000004100000410000000574313765541231026770 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_file.rb0000644000004100000410000000604713765541231025724 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/empty_directory.rb0000644000004100000410000001005013765541231026671 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/create_link.rb0000644000004100000410000000344613765541231025742 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/actions/directory.rb0000644000004100000410000000776213765541231025473 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/0000755000004100000410000000000013765541231023763 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/readline.rb0000644000004100000410000000342613765541231026100 0ustar www-datawww-databegin 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/line_editor/basic.rb0000644000004100000410000000103513765541231025370 0ustar www-datawww-dataclass 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 foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/parser.rb0000644000004100000410000000033213765541231023305 0ustar www-datawww-datarequire "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" foreman-0.87.2/lib/foreman/vendor/thor/lib/thor/util.rb0000644000004100000410000002100213765541231022763 0ustar www-datawww-datarequire "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 foreman-0.87.2/lib/foreman/env.rb0000644000004100000410000000124013765541231016605 0ustar www-datawww-datarequire "foreman" class Foreman::Env attr_reader :entries def initialize(filename) @entries = File.read(filename).gsub("\r\n","\n").split("\n").inject({}) do |ax, line| if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/ key = $1 case val = $2 # Remove single quotes when /\A'(.*)'\z/ then ax[key] = $1 # Remove double quotes and unescape string preserving newline characters when /\A"(.*)"\z/ then ax[key] = $1.gsub('\n', "\n").gsub(/\\(.)/, '\1') else ax[key] = val end end ax end end def entries @entries.each do |key, value| yield key, value end end end foreman-0.87.2/lib/foreman/cli.rb0000644000004100000410000001261413765541231016573 0ustar www-datawww-datarequire "foreman" require "foreman/helpers" require "foreman/engine" require "foreman/engine/cli" require "foreman/export" require "foreman/version" require "shellwords" require "yaml" require "foreman/vendor/thor/lib/thor" class Foreman::CLI < Foreman::Thor include Foreman::Helpers map ["-v", "--version"] => :version class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile" class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory" desc "start [PROCESS]", "Start the application (or a specific PROCESS)" method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled" method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env" method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"' method_option :port, :type => :numeric, :aliases => "-p" method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5." method_option :timestamp, :type => :boolean, :default => true, :desc => "Include timestamp in output" class << self # Hackery. Take the run method away from Thor so that we can redefine it. def is_thor_reserved_word?(word, type) return false if word == "run" super end end def start(process=nil) check_procfile! load_environment! engine.load_procfile(procfile) engine.options[:formation] = "#{process}=1" if process engine.start end desc "export FORMAT LOCATION", "Export the application to another process management format" method_option :app, :type => :string, :aliases => "-a" method_option :log, :type => :string, :aliases => "-l" method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/" method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env" method_option :port, :type => :numeric, :aliases => "-p" method_option :user, :type => :string, :aliases => "-u" method_option :template, :type => :string, :aliases => "-t" method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"' method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5." def export(format, location=nil) check_procfile! load_environment! engine.load_procfile(procfile) formatter = Foreman::Export.formatter(format) formatter.new(location, engine, options).export rescue Foreman::Export::Exception => ex error ex.message end desc "check", "Validate your application's Procfile" def check check_procfile! engine.load_procfile(procfile) error "no processes defined" unless engine.processes.length > 0 puts "valid procfile detected (#{engine.process_names.join(', ')})" end desc "run COMMAND [ARGS...]", "Run a command using your application's environment" method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env" stop_on_unknown_option! :run def run(*args) load_environment! if File.file?(procfile) engine.load_procfile(procfile) end pid = fork do begin engine.env.each { |k,v| ENV[k] = v } if args.size == 1 && process = engine.process(args.first) process.exec(:env => engine.env) else exec args.shelljoin end rescue Errno::EACCES error "not executable: #{args.first}" rescue Errno::ENOENT error "command not found: #{args.first}" end end trap("INT") do Process.kill(:INT, pid) end Process.wait(pid) exit $?.exitstatus || 0 rescue Interrupt end desc "version", "Display Foreman gem version" def version puts Foreman::VERSION end no_tasks do def engine @engine ||= begin engine_class = Foreman::Engine::CLI engine = engine_class.new(options) engine end end end private ###################################################################### def error(message) puts "ERROR: #{message}" exit 1 end def check_procfile! error("#{procfile} does not exist.") unless File.file?(procfile) end def load_environment! if options[:env] options[:env].split(",").each do |file| engine.load_env file end else default_env = File.join(engine.root, ".env") engine.load_env default_env if File.file?(default_env) end end def procfile case when options[:procfile] then options[:procfile] when options[:root] then File.expand_path(File.join(options[:root], "Procfile")) else "Procfile" end end def options original_options = super return original_options unless File.file?(".foreman") defaults = ::YAML::load_file(".foreman") || {} Foreman::Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) end end foreman-0.87.2/lib/foreman/process.rb0000644000004100000410000000365613765541231017510 0ustar www-datawww-datarequire "foreman" require "shellwords" class Foreman::Process attr_reader :command attr_reader :env # Create a Process # # @param [String] command The command to run # @param [Hash] options # # @option options [String] :cwd (./) Change to this working directory before executing the process # @option options [Hash] :env ({}) Environment variables to set for this process # def initialize(command, options={}) @command = command @options = options.dup @options[:env] ||= {} end # Get environment-expanded command for a +Process+ # # @param [Hash] custom_env ({}) Environment variables to merge with defaults # # @return [String] The expanded command # def expanded_command(custom_env={}) env = @options[:env].merge(custom_env) expanded_command = command.dup env.each do |key, val| expanded_command.gsub!("$#{key}", val) end expanded_command end # Run a +Process+ # # @param [Hash] options # # @option options :env ({}) Environment variables to set for this execution # @option options :output ($stdout) The output stream # # @returns [Fixnum] pid The +pid+ of the process # def run(options={}) env = @options[:env].merge(options[:env] || {}) output = options[:output] || $stdout runner = "#{Foreman.runner}".shellescape Dir.chdir(cwd) do Process.spawn env, expanded_command(env), :out => output, :err => output end end # Exec a +Process+ # # @param [Hash] options # # @option options :env ({}) Environment variables to set for this execution # # @return Does not return def exec(options={}) env = @options[:env].merge(options[:env] || {}) env.each { |k, v| ENV[k] = v } Dir.chdir(cwd) Kernel.exec expanded_command(env) end # Returns the working directory for this +Process+ # # @returns [String] # def cwd File.expand_path(@options[:cwd] || ".") end end foreman-0.87.2/lib/foreman/engine/0000755000004100000410000000000013765541231016740 5ustar www-datawww-dataforeman-0.87.2/lib/foreman/engine/cli.rb0000644000004100000410000000470313765541231020040 0ustar www-datawww-datarequire "foreman/engine" class Foreman::Engine::CLI < Foreman::Engine module Color ANSI = { :reset => 0, :black => 30, :red => 31, :green => 32, :yellow => 33, :blue => 34, :magenta => 35, :cyan => 36, :white => 37, :bright_black => 30, :bright_red => 31, :bright_green => 32, :bright_yellow => 33, :bright_blue => 34, :bright_magenta => 35, :bright_cyan => 36, :bright_white => 37, } def self.enable(io, force=false) io.extend(self) @@color_force = force end def color? return true if @@color_force return false if Foreman.windows? return false unless self.respond_to?(:isatty) self.isatty && ENV["TERM"] end def color(name) return "" unless color? return "" unless ansi = ANSI[name.to_sym] "\e[#{ansi}m" end end FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow bright_green bright_magenta bright_red bright_blue ) def startup @colors = map_colors proctitle "foreman: master" unless Foreman.windows? Color.enable($stdout, options[:color]) end def output(name, data) data.to_s.lines.map(&:chomp).each do |message| output = "" output += $stdout.color(@colors[name.split(".").first].to_sym) output += "#{Time.now.strftime("%H:%M:%S")} " if options[:timestamp] output += "#{pad_process_name(name)} | " output += $stdout.color(:reset) output += message $stdout.puts output $stdout.flush end rescue Errno::EPIPE terminate_gracefully end def shutdown end private def name_padding @name_padding ||= begin index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1 name_padding = @names.values.map { |n| n.length + index_padding }.sort.last [ 6, name_padding ].max end end def pad_process_name(name) name.ljust(name_padding, " ") end def map_colors colors = Hash.new("white") @names.values.each_with_index do |name, index| colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length] end colors["system"] = "bright_white" colors end def proctitle(title) $0 = title end def termtitle(title) printf("\033]0;#{title}\007") unless Foreman.windows? end end foreman-0.87.2/lib/foreman/distribution.rb0000644000004100000410000000030413765541231020534 0ustar www-datawww-datamodule Foreman module Distribution def self.files Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file| File.file?(file) end end end end