birr 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +202 -0
- data/README.md +164 -0
- data/Rakefile +41 -0
- data/bin/birr +35 -0
- data/lib/birr.rb +5 -0
- data/lib/megam/app.rb +190 -0
- data/lib/megam/birr.rb +176 -0
- data/lib/megam/birr_options.rb +59 -0
- data/lib/megam/cmd_verb.rb +48 -0
- data/lib/megam/config.rb +56 -0
- data/lib/megam/core/exceptions.rb +32 -0
- data/lib/megam/core/file_cache.rb +12 -0
- data/lib/megam/core/log.rb +34 -0
- data/lib/megam/core/text.rb +121 -0
- data/lib/megam/install.rb +125 -0
- data/lib/megam/transferarea.rb +41 -0
- data/lib/megam/version.rb +6 -0
- data/lib/megam/workarea.rb +96 -0
- data/lib/megam/workarea_loader.rb +83 -0
- data/spec/dump_spec.rb +18 -0
- metadata +298 -0
data/lib/birr.rb
ADDED
data/lib/megam/app.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
#
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'pp'
|
17
|
+
require 'megam/config'
|
18
|
+
require 'megam/birr'
|
19
|
+
require 'mixlib/cli'
|
20
|
+
require 'tmpdir'
|
21
|
+
require 'rbconfig'
|
22
|
+
|
23
|
+
class Megam::App
|
24
|
+
include Mixlib::CLI
|
25
|
+
#include(a_module) is called inside a class, and adds module methods as instance methods.
|
26
|
+
#extend(a_module) adds all module methods to the instance it is called on.
|
27
|
+
|
28
|
+
NO_COMMAND_GIVEN = "You need to pass a command with option (e.g., birr -i <install_path>)\n"
|
29
|
+
|
30
|
+
banner "Usage: birr (options)"
|
31
|
+
|
32
|
+
option :install_file,
|
33
|
+
:short => "-i INSTALL",
|
34
|
+
:long => "--install INSTALL",
|
35
|
+
:required => true,
|
36
|
+
:description => "The installation file path to use",
|
37
|
+
:proc => lambda { |path| File.expand_path(path, Dir.pwd) }
|
38
|
+
|
39
|
+
verbosity_level = 0
|
40
|
+
option :verbosity,
|
41
|
+
:short => '-V',
|
42
|
+
:long => '--verbose',
|
43
|
+
:description => "More verbose output. Use twice for max verbosity",
|
44
|
+
:proc => Proc.new { verbosity_level += 1},
|
45
|
+
:default => 0
|
46
|
+
|
47
|
+
option :help,
|
48
|
+
:short => "-h",
|
49
|
+
:long => "--help",
|
50
|
+
:description => "Show this message",
|
51
|
+
:on => :tail,
|
52
|
+
:boolean => true,
|
53
|
+
:show_options => true,
|
54
|
+
:exit => 0
|
55
|
+
|
56
|
+
option :yes,
|
57
|
+
:short => "-y",
|
58
|
+
:long => "--yes",
|
59
|
+
:description => "Say yes to all prompts for confirmation"
|
60
|
+
|
61
|
+
option :version,
|
62
|
+
:short => "-v",
|
63
|
+
:long => "--version",
|
64
|
+
:description => "Show birr version",
|
65
|
+
:boolean => true,
|
66
|
+
:proc => lambda {|v| puts "Birr: #{::Megam::VERSION}"},
|
67
|
+
:exit => 0
|
68
|
+
|
69
|
+
#attr_accessors are setters/getters in ruby. The arguments are filtered and available for use
|
70
|
+
#to subclasses.
|
71
|
+
attr_accessor :name_args
|
72
|
+
|
73
|
+
attr_accessor :text
|
74
|
+
def initialize
|
75
|
+
super # The super calls the mixlib cli.
|
76
|
+
|
77
|
+
##Traps are being set for the following when an application starts.
|
78
|
+
##SIGHUP 1 Term Hangup detected on controlling terminal
|
79
|
+
## or death of controlling process
|
80
|
+
##SIGINT 2 Term Interrupt from keyboard
|
81
|
+
##SIGQUIT 3 Core Quit from keyboard
|
82
|
+
trap("TERM") do
|
83
|
+
Megam::App.fatal!("SIGTERM received, stopping", 1)
|
84
|
+
end
|
85
|
+
|
86
|
+
trap("INT") do
|
87
|
+
Megam::App.fatal!("SIGINT received, stopping", 2)
|
88
|
+
end
|
89
|
+
|
90
|
+
trap("QUIT") do
|
91
|
+
Megam::Log.info("SIGQUIT received, call stack:\n " + caller.join("\n "))
|
92
|
+
end
|
93
|
+
|
94
|
+
@text ||= Megam::Text.new(STDOUT, STDERR, STDIN, {})
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
# Run the "birr app". Let it roam and stay by our side.[Go birr..Does it remind of the Hutch adv.].
|
99
|
+
# The first thing run does is it parses the options. Once the first level of parsing is done,
|
100
|
+
# ie the help, no_command, sub_command entry is verified it proceeds to call
|
101
|
+
# Megam_Birr with the user entered options and arguments (ARGV)
|
102
|
+
def run
|
103
|
+
Mixlib::Log::Formatter.show_time = false
|
104
|
+
validate_and_parse_options
|
105
|
+
Megam::Birr.new.run(@named_args, config)
|
106
|
+
exit 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_options(args)
|
110
|
+
super
|
111
|
+
rescue OptionParser::InvalidOption => e
|
112
|
+
puts "Error: " + e.to_s
|
113
|
+
puts self.opt_parser
|
114
|
+
exit(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
##A few private helper methods being used by app itself.
|
118
|
+
##If you run an application for ever, you might pool all the executions and gather the stacktrace.
|
119
|
+
private
|
120
|
+
|
121
|
+
# A check is performed to see if an option is entered, help or version
|
122
|
+
def validate_and_parse_options
|
123
|
+
# Checking ARGV validity *before* parse_options because parse_options
|
124
|
+
# mangles ARGV in some situations
|
125
|
+
if no_command_given?
|
126
|
+
print_help_and_exit(1, NO_COMMAND_GIVEN)
|
127
|
+
elsif (want_help? || want_version?)
|
128
|
+
print_help_and_exit
|
129
|
+
else
|
130
|
+
@named_args = parse_options(ARGV)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
def no_subcommand_given?
|
136
|
+
ARGV[0] =~ /^-/
|
137
|
+
end
|
138
|
+
|
139
|
+
def no_command_given?
|
140
|
+
ARGV.empty?
|
141
|
+
end
|
142
|
+
|
143
|
+
def want_help?
|
144
|
+
ARGV[0] =~ /^(--help|-h)$/
|
145
|
+
end
|
146
|
+
|
147
|
+
def want_version?
|
148
|
+
ARGV[0] =~ /^(--version|-v)$/
|
149
|
+
end
|
150
|
+
|
151
|
+
# Print the help message with the exit code. If no command is given, then a fatal message is printed.
|
152
|
+
# The options are parsed by calling the parse_options present in the mixlib cli as extended by the app.
|
153
|
+
# A error gets caught and results in an ugly stack trace, which probably needs to be shown in an elegant way.
|
154
|
+
# The stacK should be logged using the debug_stacktrace method in the app class.
|
155
|
+
def print_help_and_exit(exitcode=1, fatal_message=nil)
|
156
|
+
Megam::Log.error(fatal_message) if fatal_message
|
157
|
+
parse_options(ARGV)
|
158
|
+
exit exitcode
|
159
|
+
end
|
160
|
+
|
161
|
+
class << self
|
162
|
+
#The exception in ruby carries the class, message and the trace.
|
163
|
+
#http://www.ruby-doc.org/core-2.0/Exception.html
|
164
|
+
def debug_stacktrace(e)
|
165
|
+
message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
166
|
+
megam_stacktrace_out = "Generated at #{Time.now.to_s}\n"
|
167
|
+
megam_stacktrace_out += message
|
168
|
+
|
169
|
+
#after the message is formulated in the variable megam_stack_trace_out, its
|
170
|
+
#stored in a file named megam-stacktrace.out
|
171
|
+
Megam::FileCache.store("megam-stacktrace.out", megam_stacktrace_out)
|
172
|
+
|
173
|
+
##The same error is logged in the log file saying, go look at megam-stacktrace.out for error.
|
174
|
+
Megam::Log.fatal("Stacktrace dumped to #{Megam::FileCache.load("megam-stacktrace.out", false)}")
|
175
|
+
Megam::Log.debug(message)
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
# Log a fatal error message to both STDERR and the Logger, exit the application
|
180
|
+
def fatal!(msg, err = -1)
|
181
|
+
Megam::Log.fatal(msg)
|
182
|
+
Process.exit err
|
183
|
+
end
|
184
|
+
|
185
|
+
def exit!(msg, err = -1)
|
186
|
+
Megam::Log.debug(msg)
|
187
|
+
Process.exit err
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
data/lib/megam/birr.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
|
16
|
+
require 'pp'
|
17
|
+
require 'megam/core/text'
|
18
|
+
|
19
|
+
#Main entry for the command line, after validating the class, run the install_file
|
20
|
+
class Megam::Birr
|
21
|
+
|
22
|
+
attr_accessor :text
|
23
|
+
attr_accessor :config
|
24
|
+
#text is used to print stuff in the terminal (message, log, info, warn etc.)
|
25
|
+
def self.text
|
26
|
+
@text ||= Megam::Text.new(STDOUT, STDERR, STDIN, {})
|
27
|
+
end
|
28
|
+
|
29
|
+
# I don't think we need this method. Will remove it later.
|
30
|
+
# We need to just call text.msg
|
31
|
+
def self.msg(msg="")
|
32
|
+
text.msg(msg)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a new instance of the current class configured for the given
|
36
|
+
# arguments and options
|
37
|
+
def initialize()
|
38
|
+
end
|
39
|
+
|
40
|
+
# Run Birr for the given +args+ (ARGV), adding +options+ to the list of
|
41
|
+
# CLI options that the subcommand knows how to handle.
|
42
|
+
# ===Arguments
|
43
|
+
# args::: usually ARGV
|
44
|
+
# options::: A Mixlib::CLI option parser hash. These +options+ are how
|
45
|
+
# subcommands know about global Birr CLI options
|
46
|
+
def run(args=[], config={})
|
47
|
+
@config = config.dup
|
48
|
+
@text ||= Megam::Text.new(STDOUT, STDERR, STDIN, config)
|
49
|
+
# configure your Birr.
|
50
|
+
configure_birr
|
51
|
+
end
|
52
|
+
|
53
|
+
# configure meggy, to startwith locate the config file under .meggy/Birr.rb
|
54
|
+
# Once located, read the Birr.rb config file. parse them, and report any ruby syntax errors.
|
55
|
+
# if not merge then inside Meggy::Config object.
|
56
|
+
def configure_birr
|
57
|
+
# look for a .birr/birr.rb for configuration. Not used currently.
|
58
|
+
unless config[:install_file]
|
59
|
+
locate_install_file
|
60
|
+
end
|
61
|
+
|
62
|
+
# Now load the installation file provided as input {-i} parm.
|
63
|
+
if config[:install_file]
|
64
|
+
@text.info "Using #{config[:install_file]}"
|
65
|
+
apply_computed_config
|
66
|
+
read_config_file(config[:install_file])
|
67
|
+
else
|
68
|
+
text.warn("No birr configuration file found")
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def locate_install_file
|
74
|
+
# Look for $HOME/.meggy/birr.rb, this aint' supported currently
|
75
|
+
# the idea is to allow configuration of the options used here.
|
76
|
+
if ENV['HOME']
|
77
|
+
user_config_file = File.expand_path(File.join(ENV['HOME'], '.birr', 'birr.rb'))
|
78
|
+
end
|
79
|
+
|
80
|
+
if File.exist?(user_config_file)
|
81
|
+
config[:install_file] = user_config_file
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Catch-all method that does any massaging needed for various config
|
86
|
+
# components, such as expanding file paths and converting verbosity level
|
87
|
+
# into log level.
|
88
|
+
def apply_computed_config
|
89
|
+
|
90
|
+
case config[:verbosity]
|
91
|
+
when 0, nil
|
92
|
+
config[:log_level] = :error
|
93
|
+
when 1
|
94
|
+
config[:log_level] = :info
|
95
|
+
else
|
96
|
+
config[:log_level] = :debug
|
97
|
+
end
|
98
|
+
|
99
|
+
Mixlib::Log::Formatter.show_time = false
|
100
|
+
Megam::Log.init(config[:log_location] || STDOUT)
|
101
|
+
Megam::Log.level(config[:log_level] || :error)
|
102
|
+
|
103
|
+
config.each do |key, val|
|
104
|
+
Megam::Config[key] = val
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
# load the content as provided in -i {installation_file}
|
110
|
+
# ruby errors get reported or else the file is executed.
|
111
|
+
def read_config_file(file)
|
112
|
+
self.instance_eval(IO.read(file), file, 1)
|
113
|
+
rescue SyntaxError => e
|
114
|
+
@text.error "You have invalid ruby syntax in your config file #{file}"
|
115
|
+
@text.info(text.color(e.message, :red))
|
116
|
+
if file_line = e.message[/#{Regexp.escape(file)}:[\d]+/]
|
117
|
+
line = file_line[/:([\d]+)$/, 1].to_i
|
118
|
+
highlight_config_error(file, line)
|
119
|
+
end
|
120
|
+
exit 1
|
121
|
+
rescue Exception => e
|
122
|
+
@text.error "You have an error in your config file #{file}"
|
123
|
+
@text.info "#{e.class.name}: #{e.message}"
|
124
|
+
filtered_trace = e.backtrace.grep(/#{Regexp.escape(file)}/)
|
125
|
+
filtered_trace.each {|line| text.msg(" " + text.color(line, :red))}
|
126
|
+
if !filtered_trace.empty?
|
127
|
+
line_nr = filtered_trace.first[/#{Regexp.escape(file)}:([\d]+)/, 1]
|
128
|
+
highlight_config_error(file, line_nr.to_i)
|
129
|
+
end
|
130
|
+
|
131
|
+
exit 1
|
132
|
+
end
|
133
|
+
|
134
|
+
#ERROR: You have invalid ruby syntax in your config file /home/ram/.meggy/Birr.rb
|
135
|
+
#/home/ram/.meggy/Birr.rb:9: syntax error, unexpected '='
|
136
|
+
#Birr[:username] > = "admin"
|
137
|
+
# ^
|
138
|
+
# # /home/ram/.meggy/Birr.rb
|
139
|
+
# 8: meggy_server_url 'http://localhost:6167'
|
140
|
+
# 9: Birr[:username] > = "admin"
|
141
|
+
# 10: Birr[:password] = "team4dog"
|
142
|
+
# Line 9 is marked in red, and the 3rd line where the error is show is highlighted in red.
|
143
|
+
#This is in case of a ruby parse error.
|
144
|
+
def highlight_config_error(file, line)
|
145
|
+
config_file_lines = []
|
146
|
+
|
147
|
+
# A file line is split into the line number (index) and the line content.
|
148
|
+
# The line number is converted to string (to_s), right justified 3 characters with a colon, and its trimmed (chomp)
|
149
|
+
|
150
|
+
IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"}
|
151
|
+
# mark the appropriate line with a red color, if its just one line, then mark the zeroth line.
|
152
|
+
# if not get the range (deducting 2), and mark the second line.
|
153
|
+
if line == 1
|
154
|
+
lines = config_file_lines[0..3]
|
155
|
+
lines[0] = text.color(lines[0], :red)
|
156
|
+
else
|
157
|
+
lines = config_file_lines[Range.new(line - 2, line)]
|
158
|
+
lines[1] = text.color(lines[1], :red)
|
159
|
+
end
|
160
|
+
text.msg ""
|
161
|
+
# print the name of the file in white
|
162
|
+
text.msg text.color(" # #{file}", :white)
|
163
|
+
# print the rest of the line.
|
164
|
+
lines.each {|l| text.msg(l)}
|
165
|
+
text.msg ""
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def self.working_directory
|
173
|
+
ENV['PWD'] || Dir.pwd
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#The birr options class takes the block in the initialize method.
|
2
|
+
#it uses the custom attr_accessors, namely (setter, varargs_setters) to
|
3
|
+
#inject the value by defining methods with the dsl defined ones.
|
4
|
+
#
|
5
|
+
class Megam
|
6
|
+
class BirrOptions
|
7
|
+
def self.setter(*method_names)
|
8
|
+
method_names.each do |name|
|
9
|
+
send :define_method, name do |data|
|
10
|
+
instance_variable_set "@#{name}".to_sym, data
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.varargs_setter(*method_names)
|
16
|
+
method_names.each do |name|
|
17
|
+
send :define_method, name do |*data|
|
18
|
+
instance_variable_set "@#{name}".to_sym, data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
setter :sudo, :start_time, :tarball
|
24
|
+
varargs_setter :directory, :command
|
25
|
+
attr_reader :commands, :sudo
|
26
|
+
|
27
|
+
def initialize(&block)
|
28
|
+
# defaults
|
29
|
+
@tarball = nil
|
30
|
+
@sudo = false
|
31
|
+
@directory = []
|
32
|
+
@command = []
|
33
|
+
@commands = []
|
34
|
+
@start_time = Time.now
|
35
|
+
instance_eval(&block)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def commands
|
40
|
+
@commands = @command.flatten if @command
|
41
|
+
end
|
42
|
+
|
43
|
+
def sudo?
|
44
|
+
@sudo
|
45
|
+
end
|
46
|
+
|
47
|
+
def tarball_file
|
48
|
+
@tarball_file ||= @tarball
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
tmps = ""
|
53
|
+
tmps << "sudo :" + @sudo.to_s + "\n"
|
54
|
+
tmps << "dir :" + @directory.to_s + "\n"
|
55
|
+
tmps << "cmd :" + commands.to_s + "\n"
|
56
|
+
tmps << "strt :" + @start_time.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
require "megam/core/text"
|
16
|
+
require "megam/birr"
|
17
|
+
|
18
|
+
class Megam::CmdVerb
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
end
|
22
|
+
|
23
|
+
# cp -r -s <from directory> <to directory>
|
24
|
+
def self.cp(opts={})
|
25
|
+
cp =""
|
26
|
+
cp << "sudo " if opts[:sudo]
|
27
|
+
cp << "cp"
|
28
|
+
cp << " -r " if opts[:recursive]
|
29
|
+
# cp << " -u " if opts[:copy_on_new] #copy only when the SOURCE file is newer than
|
30
|
+
cp << opts[:from_dir] if opts[:from_dir] or raise Megam::Exceptions::FileNotFound
|
31
|
+
cp << " "
|
32
|
+
cp << opts[:to_dir] if opts[:to_dir] or raise Megam::Exceptions::FileNotFound
|
33
|
+
cp
|
34
|
+
end
|
35
|
+
|
36
|
+
#gunzip -c foo.tar.gz | tar xvf -
|
37
|
+
def self.untar(opts={})
|
38
|
+
untar = "gunzip -c "
|
39
|
+
untar << opts[:tar_file] if opts[:tar_file]
|
40
|
+
untar << " | tar xvf - -C "
|
41
|
+
untar << opts[:to_dir] if opts[:to_dir]
|
42
|
+
untar
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"cmdverb -> [supports cp, untar *only]"
|
47
|
+
end
|
48
|
+
end
|