propro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|