propro 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/LICENSE +339 -0
- data/README.md +134 -0
- data/Rakefile +9 -0
- data/bin/propro +6 -0
- data/examples/vagrant.propro +41 -0
- data/examples/vps_webserver.propro +51 -0
- data/ext/bash/app/nginx.sh +9 -0
- data/ext/bash/app/node.sh +5 -0
- data/ext/bash/app/pg.sh +5 -0
- data/ext/bash/app/puma/nginx.sh +58 -0
- data/ext/bash/app/puma.sh +64 -0
- data/ext/bash/app/rvm.sh +7 -0
- data/ext/bash/app/sidekiq.sh +69 -0
- data/ext/bash/app.sh +75 -0
- data/ext/bash/db/pg.sh +47 -0
- data/ext/bash/db/redis.sh +20 -0
- data/ext/bash/lib/extras.sh +11 -0
- data/ext/bash/lib/nginx.sh +233 -0
- data/ext/bash/lib/node.sh +28 -0
- data/ext/bash/lib/pg.sh +44 -0
- data/ext/bash/lib/propro.sh +104 -0
- data/ext/bash/lib/redis.sh +59 -0
- data/ext/bash/lib/rvm.sh +21 -0
- data/ext/bash/lib/system.sh +57 -0
- data/ext/bash/lib/ubuntu.sh +175 -0
- data/ext/bash/vagrant/nginx.sh +31 -0
- data/ext/bash/vagrant/node.sh +5 -0
- data/ext/bash/vagrant/pg.sh +12 -0
- data/ext/bash/vagrant/redis.sh +5 -0
- data/ext/bash/vagrant/rvm.sh +6 -0
- data/ext/bash/vagrant/system.sh +26 -0
- data/ext/bash/vagrant.sh +3 -0
- data/ext/bash/vps/system.sh +156 -0
- data/lib/propro/cli/templates/init.tt +21 -0
- data/lib/propro/cli.rb +125 -0
- data/lib/propro/command.rb +17 -0
- data/lib/propro/export.rb +119 -0
- data/lib/propro/option.rb +36 -0
- data/lib/propro/package.rb +68 -0
- data/lib/propro/script.rb +95 -0
- data/lib/propro/source.rb +86 -0
- data/lib/propro/version.rb +3 -0
- data/lib/propro.rb +57 -0
- data/propro.gemspec +27 -0
- data/test/export_spec.rb +88 -0
- data/test/minitest_helper.rb +6 -0
- data/test/option_spec.rb +34 -0
- metadata +167 -0
data/lib/propro/cli.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Propro
|
4
|
+
class CLI < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
INIT_TEMPLATES = {
|
8
|
+
db: {
|
9
|
+
paths: %w[ vps db ],
|
10
|
+
desc: 'backend database server'
|
11
|
+
},
|
12
|
+
app: {
|
13
|
+
paths: %w[ vps app ],
|
14
|
+
desc: 'frontend application server'
|
15
|
+
},
|
16
|
+
web: {
|
17
|
+
paths: %w[ vps app db ],
|
18
|
+
desc: 'standalone web server'
|
19
|
+
},
|
20
|
+
vagrant: {
|
21
|
+
paths: %w[ vagrant ],
|
22
|
+
desc: 'standalone Vagrant development VM'
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
def self.source_root
|
27
|
+
File.join(File.dirname(__FILE__), 'cli/templates')
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'init NAME', 'Creates a Propro script with the file name NAME'
|
31
|
+
option :template, aliases: '-t', enum: %w[ db app web vagrant ], default: 'web'
|
32
|
+
def init(outname = nil)
|
33
|
+
key = options[:template].to_sym
|
34
|
+
outfile = absolute_path(outname || "#{key}.propro")
|
35
|
+
type = INIT_TEMPLATES[key]
|
36
|
+
@paths = type[:paths]
|
37
|
+
@desc = type[:desc]
|
38
|
+
@sources = Package.sources_for_paths('lib', *@paths)
|
39
|
+
template 'init.tt', outfile
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'build INPUT', 'Takes a Propro script INPUT and generates a Bash provisioner OUTPUT'
|
43
|
+
option :output, aliases: '-o', banner: '<output file name>'
|
44
|
+
def build(input)
|
45
|
+
infile = absolute_path(input)
|
46
|
+
script = Script.load(input).to_bash
|
47
|
+
if (output = options[:output])
|
48
|
+
File.write(absolute_path(output), script)
|
49
|
+
else
|
50
|
+
STDOUT << script
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'deploy SCRIPT', 'Builds a Propro script and then executes it remotely'
|
55
|
+
option :server, aliases: '-s', banner: '<server address>'
|
56
|
+
option :password, aliases: '-p', banner: '<server password>'
|
57
|
+
option :user, aliases: '-u', banner: '<server user>', default: 'root'
|
58
|
+
def deploy(script_path)
|
59
|
+
require 'net/ssh'
|
60
|
+
require 'net/scp'
|
61
|
+
require 'io/console'
|
62
|
+
|
63
|
+
puts Propro.color_banner
|
64
|
+
puts
|
65
|
+
|
66
|
+
script = Script.load(script_path)
|
67
|
+
address = (options[:server] || script.get_server)
|
68
|
+
password = (options[:password] || script.get_password || ask_password)
|
69
|
+
user = (options[:user] || script.get_user)
|
70
|
+
remote_home = (user == 'root' ? '/root' : "/home/#{user}")
|
71
|
+
remote_log_path = "#{remote_home}/provision.log"
|
72
|
+
remote_script_path = "#{remote_home}/provision.sh"
|
73
|
+
remote_script_url = address + remote_script_path
|
74
|
+
|
75
|
+
say_event 'build', script_path
|
76
|
+
script_data = StringIO.new(script.to_bash)
|
77
|
+
|
78
|
+
raise ArgumentError, 'no server address has been provided' if !address
|
79
|
+
raise ArgumentError, 'no server password has been provided' if !password
|
80
|
+
|
81
|
+
say_event 'connect', "#{user}@#{address}"
|
82
|
+
Net::SSH.start(address, user, password: password) do |session|
|
83
|
+
say_event 'upload', "#{script_path} -> #{remote_script_url}"
|
84
|
+
session.scp.upload!(script_data, remote_script_path)
|
85
|
+
session.exec!("chmod +x #{remote_script_path}")
|
86
|
+
session.exec!("touch #{remote_log_path}")
|
87
|
+
tail = session.exec("tail -f #{remote_log_path}") do |ch|
|
88
|
+
ch.on_data do |ch, data|
|
89
|
+
STDOUT.write(data)
|
90
|
+
STDOUT.flush
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
sleep 1 # ughhhhhh
|
95
|
+
say_event 'run', remote_script_url
|
96
|
+
puts
|
97
|
+
session.exec(remote_script_path)
|
98
|
+
end
|
99
|
+
rescue IOError # uggghhhhhhhhhh
|
100
|
+
say_event 'done', "#{address} is rebooting"
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def say_event(event, msg)
|
106
|
+
pad = (7 - event.length)
|
107
|
+
label = "#{event.upcase}" + (" " * pad)
|
108
|
+
puts "\e[36m\e[1m#{label}\e[0m #{msg}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def ask_password
|
112
|
+
STDIN.noecho do
|
113
|
+
ask 'password:'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def absolute_path(path)
|
118
|
+
if path[0] == '/'
|
119
|
+
path
|
120
|
+
else
|
121
|
+
File.join(Dir.pwd, path)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Propro
|
2
|
+
class Export
|
3
|
+
TAG_SPECIFY = '@specify'
|
4
|
+
TAG_REQUIRE = '@require'
|
5
|
+
EXPORT_BEGIN = 'export '
|
6
|
+
COMMENT_RANGE = /#(.*)\Z/
|
7
|
+
DQUO = '"'
|
8
|
+
SQUO = '\''
|
9
|
+
EQ = '='
|
10
|
+
ZERO_STRING = ''
|
11
|
+
INTEGER_RE = /\A\-{0,1}[0-9]+\Z/
|
12
|
+
DECIMAL_RE = /\A\-{0,1}[0-9]+\.[0-9]+\Z/
|
13
|
+
SPACE_RE = / /
|
14
|
+
YES = 'yes'
|
15
|
+
NO = 'no'
|
16
|
+
|
17
|
+
def self.parse(line)
|
18
|
+
is_literal = false
|
19
|
+
is_specified = false
|
20
|
+
is_required = false
|
21
|
+
comment = nil
|
22
|
+
line = line.sub(EXPORT_BEGIN, ZERO_STRING)
|
23
|
+
name, value = line.split(EQ, 2)
|
24
|
+
|
25
|
+
if value =~ COMMENT_RANGE
|
26
|
+
metacomment = $1
|
27
|
+
is_specified = true if metacomment.sub!(TAG_SPECIFY, ZERO_STRING)
|
28
|
+
is_required = true if metacomment.sub!(TAG_REQUIRE, ZERO_STRING)
|
29
|
+
metacomment.strip!
|
30
|
+
|
31
|
+
if metacomment != ZERO_STRING
|
32
|
+
comment = metacomment
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
value.sub!(COMMENT_RANGE, ZERO_STRING)
|
37
|
+
value.strip!
|
38
|
+
|
39
|
+
case value[0]
|
40
|
+
when DQUO
|
41
|
+
value[0] = ZERO_STRING
|
42
|
+
value[-1] = ZERO_STRING
|
43
|
+
when SQUO
|
44
|
+
is_literal = true
|
45
|
+
value[0] = ZERO_STRING
|
46
|
+
value[-1] = ZERO_STRING
|
47
|
+
end
|
48
|
+
|
49
|
+
new name,
|
50
|
+
default: value,
|
51
|
+
is_literal: is_literal,
|
52
|
+
is_specified: is_specified,
|
53
|
+
is_required: is_required,
|
54
|
+
comment: comment
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(name, opts = {})
|
58
|
+
@name = name.to_s.upcase
|
59
|
+
@default = opts[:default]
|
60
|
+
@is_literal = opts[:is_literal]
|
61
|
+
@is_specified = opts[:is_specified]
|
62
|
+
@is_required = opts[:is_required]
|
63
|
+
@comment = opts[:comment]
|
64
|
+
end
|
65
|
+
|
66
|
+
def key
|
67
|
+
@key ||= @name.downcase.to_sym
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_ruby
|
71
|
+
args = []
|
72
|
+
args << key.inspect
|
73
|
+
args << default.inspect
|
74
|
+
args << "lit: true" if @is_literal
|
75
|
+
if @comment
|
76
|
+
"set #{args.join(', ')} # #{@comment}"
|
77
|
+
else
|
78
|
+
"set #{args.join(', ')}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def default
|
83
|
+
cast(@default)
|
84
|
+
end
|
85
|
+
|
86
|
+
def is_literal?
|
87
|
+
@is_literal
|
88
|
+
end
|
89
|
+
|
90
|
+
def is_specified?
|
91
|
+
@is_specified
|
92
|
+
end
|
93
|
+
|
94
|
+
def is_required?
|
95
|
+
@is_required
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def cast(val)
|
101
|
+
case val
|
102
|
+
when INTEGER_RE
|
103
|
+
val.to_i
|
104
|
+
when DECIMAL_RE
|
105
|
+
val.to_f
|
106
|
+
when ZERO_STRING
|
107
|
+
nil
|
108
|
+
when SPACE_RE
|
109
|
+
val.split(' ')
|
110
|
+
when YES
|
111
|
+
true
|
112
|
+
when NO
|
113
|
+
false
|
114
|
+
else
|
115
|
+
val
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Propro
|
2
|
+
class Option
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(key, value, opts = {})
|
6
|
+
@key = key.to_s.downcase.to_sym
|
7
|
+
@value = value
|
8
|
+
@is_literal = opts[:lit] ? true : false
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= @key.to_s.upcase
|
13
|
+
end
|
14
|
+
|
15
|
+
def value=(val)
|
16
|
+
@value = val
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
case @value
|
21
|
+
when Array
|
22
|
+
%{"#{@value.join(' ')}"}
|
23
|
+
when true
|
24
|
+
%{"yes"}
|
25
|
+
when false
|
26
|
+
%{"no"}
|
27
|
+
else
|
28
|
+
@is_literal ? %{'#{@value}'} : %{"#{@value}"}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_bash
|
33
|
+
"#{name}=#{value}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Propro
|
2
|
+
module Package
|
3
|
+
EXTRACT_NAME_RE = %r{/ext/bash/([a-z0-9_\-/]+)\.sh}
|
4
|
+
SORTED_NAMES = %w[
|
5
|
+
lib/propro
|
6
|
+
lib/ubuntu
|
7
|
+
lib/system
|
8
|
+
lib/pg
|
9
|
+
lib/rvm
|
10
|
+
lib/nginx
|
11
|
+
lib/node
|
12
|
+
lib/redis
|
13
|
+
vps/system
|
14
|
+
app
|
15
|
+
app/rvm
|
16
|
+
app/pg
|
17
|
+
app/nginx
|
18
|
+
app/sidekiq
|
19
|
+
app/puma
|
20
|
+
app/puma/nginx
|
21
|
+
app/node
|
22
|
+
db/pg
|
23
|
+
db/redis
|
24
|
+
vagrant
|
25
|
+
vagrant/system
|
26
|
+
vagrant/pg
|
27
|
+
vagrant/redis
|
28
|
+
vagrant/rvm
|
29
|
+
vagrant/node
|
30
|
+
vagrant/nginx
|
31
|
+
lib/extras
|
32
|
+
]
|
33
|
+
|
34
|
+
module_function
|
35
|
+
|
36
|
+
def root
|
37
|
+
File.join(Propro.root, 'ext/bash')
|
38
|
+
end
|
39
|
+
|
40
|
+
def source_files
|
41
|
+
@source_files ||= Dir[File.join(root, '**/*.sh')]
|
42
|
+
end
|
43
|
+
|
44
|
+
def sources
|
45
|
+
@sources ||= begin
|
46
|
+
names = SORTED_NAMES.dup
|
47
|
+
source_files.each do |file|
|
48
|
+
name = EXTRACT_NAME_RE.match(file)[1]
|
49
|
+
names.push(name) unless names.include?(name)
|
50
|
+
end
|
51
|
+
names.map { |name| Source.new(name) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def sources_for_path(path)
|
56
|
+
resort! sources.select { |source| /\A#{path}/ =~ source.name }
|
57
|
+
end
|
58
|
+
|
59
|
+
def sources_for_paths(*paths)
|
60
|
+
resort! paths.flatten.map { |path| sources_for_path(path) }.flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
def resort!(ary)
|
64
|
+
ary.sort_by! { |source| SORTED_NAMES.index(source.name) }
|
65
|
+
ary
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Propro
|
2
|
+
class Script
|
3
|
+
def self.load(file)
|
4
|
+
script = new
|
5
|
+
script.load_file(file)
|
6
|
+
script
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@sources = []
|
11
|
+
@options = []
|
12
|
+
@commands = []
|
13
|
+
@server = nil
|
14
|
+
@password = nil
|
15
|
+
source :lib
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_file(file)
|
19
|
+
@file = file
|
20
|
+
@file_name = File.basename(@file)
|
21
|
+
instance_eval(File.read(file))
|
22
|
+
end
|
23
|
+
|
24
|
+
def server(host, opts = {})
|
25
|
+
@server = host
|
26
|
+
@password = opts[:password]
|
27
|
+
@user = opts[:user] || 'root'
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_server
|
31
|
+
@server
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_password
|
35
|
+
@password
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_user
|
39
|
+
@user
|
40
|
+
end
|
41
|
+
|
42
|
+
def source(src)
|
43
|
+
@sources.concat(Package.sources_for_path(src))
|
44
|
+
end
|
45
|
+
|
46
|
+
def set(key, value)
|
47
|
+
@options << Option.new(key, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def provision(*commands)
|
51
|
+
@commands.concat(commands.flatten.map { |c| Command.new(c) })
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_bash
|
55
|
+
<<-SH
|
56
|
+
#!/usr/bin/env bash
|
57
|
+
#{Propro.comment_banner}
|
58
|
+
#
|
59
|
+
# Built from: #{@file_name}
|
60
|
+
|
61
|
+
unset UCF_FORCE_CONFFOLD
|
62
|
+
export UCF_FORCE_CONFFNEW="YES"
|
63
|
+
export DEBIAN_FRONTEND="noninteractive"
|
64
|
+
|
65
|
+
#{sources_bash}
|
66
|
+
|
67
|
+
# Options from: #{@file_name}
|
68
|
+
#{options_bash}
|
69
|
+
|
70
|
+
function main {
|
71
|
+
#{commands_bash}
|
72
|
+
finished
|
73
|
+
reboot-system
|
74
|
+
}
|
75
|
+
|
76
|
+
main
|
77
|
+
|
78
|
+
SH
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def options_bash
|
84
|
+
@options.map(&:to_bash).join("\n").strip
|
85
|
+
end
|
86
|
+
|
87
|
+
def sources_bash
|
88
|
+
@sources.map(&:to_bash).join("\n").strip
|
89
|
+
end
|
90
|
+
|
91
|
+
def commands_bash
|
92
|
+
@commands.map(&:to_bash).join("\n ")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Propro
|
2
|
+
class Source
|
3
|
+
attr_reader :name, :provisioner
|
4
|
+
|
5
|
+
EXPORT_BEGIN = 'export '
|
6
|
+
COMMENT_BEGIN = '#'
|
7
|
+
IS_LIBRARY_BEGIN = 'lib/'
|
8
|
+
FUNC_PROVISION_BEGIN = 'function provision-'
|
9
|
+
FUNC_PROVISION_NAME_RANGE = /\Afunction provision\-([a-z\-]+)/
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
@name = name.to_s
|
13
|
+
@exports = []
|
14
|
+
@can_provision = false
|
15
|
+
@is_library = name.start_with?(IS_LIBRARY_BEGIN)
|
16
|
+
@src = ''
|
17
|
+
load
|
18
|
+
end
|
19
|
+
|
20
|
+
def file_name
|
21
|
+
"#{@name}.sh"
|
22
|
+
end
|
23
|
+
|
24
|
+
def file_path
|
25
|
+
File.join(Propro::Package.root, file_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def load
|
29
|
+
File.open(file_path) do |file|
|
30
|
+
file.each_line { |line| load_line(line) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def can_provision?
|
35
|
+
@can_provision
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_library?
|
39
|
+
@is_library
|
40
|
+
end
|
41
|
+
|
42
|
+
def specified_exports
|
43
|
+
@specified_exports ||= begin
|
44
|
+
exports.select { |e| e.is_required? || e.is_specified? }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def exports
|
49
|
+
@exports.sort { |a, b|
|
50
|
+
case
|
51
|
+
when b.is_required? then 1
|
52
|
+
when b.is_specified? then 0
|
53
|
+
else -1
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_bash
|
59
|
+
<<-SH
|
60
|
+
# Propro package: #{file_name}
|
61
|
+
#{@src}
|
62
|
+
SH
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def load_line(line)
|
68
|
+
case
|
69
|
+
when line.start_with?(COMMENT_BEGIN)
|
70
|
+
# skip comments
|
71
|
+
when line.start_with?(EXPORT_BEGIN)
|
72
|
+
# collect exported variables from bash modules
|
73
|
+
@exports << Export.parse(line)
|
74
|
+
@src << line.sub(EXPORT_BEGIN, '')
|
75
|
+
when line.start_with?(FUNC_PROVISION_BEGIN)
|
76
|
+
@can_provision = true
|
77
|
+
path = line.match(FUNC_PROVISION_NAME_RANGE)[1]
|
78
|
+
@provisioner = path.gsub('-', '/')
|
79
|
+
@src << line
|
80
|
+
else
|
81
|
+
# pass-through
|
82
|
+
@src << line
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/propro.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'propro/version'
|
2
|
+
require 'propro/package'
|
3
|
+
require 'propro/export'
|
4
|
+
require 'propro/source'
|
5
|
+
require 'propro/script'
|
6
|
+
require 'propro/command'
|
7
|
+
require 'propro/option'
|
8
|
+
|
9
|
+
module Propro
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
BANNER = <<'DOC'.chomp
|
13
|
+
____ _________ ____ _________
|
14
|
+
/ __ \/ ___/ __ \/ __ \/ ___/ __ \
|
15
|
+
/ /_/ / / / /_/ / /_/ / / / /_/ /
|
16
|
+
/ .___/_/ \____/ .___/_/ \____/
|
17
|
+
/_/ /_/
|
18
|
+
DOC
|
19
|
+
|
20
|
+
module_function
|
21
|
+
|
22
|
+
def banner
|
23
|
+
BANNER
|
24
|
+
end
|
25
|
+
|
26
|
+
# <3 <3 <3 Lifted from Minitest::Pride <3 <3 <3
|
27
|
+
# https://github.com/seattlerb/minitest/blob/master/lib/minitest/pride_plugin.rb
|
28
|
+
def color_banner
|
29
|
+
@color_banner ||= begin
|
30
|
+
if /^xterm|-256color$/ =~ ENV['TERM']
|
31
|
+
pi3 = Math::PI / 3
|
32
|
+
colors = (0...(6 * 7)).map { |n|
|
33
|
+
n *= 1.0 / 6
|
34
|
+
r = (3 * Math.sin(n ) + 3).to_i
|
35
|
+
g = (3 * Math.sin(n + 2 * pi3) + 3).to_i
|
36
|
+
b = (3 * Math.sin(n + 4 * pi3) + 3).to_i
|
37
|
+
36 * r + 6 * g + b + 16
|
38
|
+
}
|
39
|
+
banner.each_line.map { |line|
|
40
|
+
line.each_char.with_index.map { |chr, i|
|
41
|
+
"\e[38;5;#{colors[i]}m#{chr}\e[0m"
|
42
|
+
}.join
|
43
|
+
}.join
|
44
|
+
else
|
45
|
+
"\e[2m#{banner}\e[0m"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def comment_banner
|
51
|
+
@comment_banner ||= banner.each_line.map { |l| '# ' + l }.join
|
52
|
+
end
|
53
|
+
|
54
|
+
def root
|
55
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
56
|
+
end
|
57
|
+
end
|
data/propro.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'propro/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'propro'
|
8
|
+
spec.version = Propro::VERSION
|
9
|
+
spec.authors = ['Carsten Nielsen']
|
10
|
+
spec.email = ['heycarsten@gmail.com']
|
11
|
+
spec.summary = 'A standalone server provisioning tool'
|
12
|
+
spec.description = 'Propro is a tool for provisioning remote servers.'
|
13
|
+
spec.homepage = 'http://github.com/heycarsten/propro'
|
14
|
+
spec.license = 'GNU v2'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^test\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'thor', '~> 0.18'
|
22
|
+
spec.add_dependency 'net-scp', '~> 1.1'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'minitest'
|
27
|
+
end
|