rubycut-babushka 0.10.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +31 -0
- data/README.markdown +246 -0
- data/Rakefile +26 -0
- data/bin/babushka +11 -0
- data/deps/babushka.rb +101 -0
- data/deps/dev.rb +12 -0
- data/deps/fhs.rb +31 -0
- data/deps/git.rb +29 -0
- data/deps/homebrew.rb +30 -0
- data/deps/os_x.rb +33 -0
- data/deps/packages.rb +22 -0
- data/deps/pkg_managers.rb +110 -0
- data/deps/ruby.rb +23 -0
- data/deps/rubygems.rb +24 -0
- data/deps/system.rb +10 -0
- data/deps/templates/app.rb +68 -0
- data/deps/templates/external.rb +12 -0
- data/deps/templates/installer.rb +31 -0
- data/deps/templates/managed.rb +105 -0
- data/deps/templates/ppa.rb +24 -0
- data/deps/templates/src.rb +42 -0
- data/deps/templates/tmbundle.rb +15 -0
- data/lib/babushka.rb +28 -0
- data/lib/babushka/accepts_block_for.rb +72 -0
- data/lib/babushka/accepts_list_for.rb +49 -0
- data/lib/babushka/accepts_value_for.rb +24 -0
- data/lib/babushka/base.rb +78 -0
- data/lib/babushka/bug_reporter.rb +55 -0
- data/lib/babushka/cmdline.rb +133 -0
- data/lib/babushka/cmdline/handler.rb +41 -0
- data/lib/babushka/cmdline/helpers.rb +127 -0
- data/lib/babushka/cmdline/parser.rb +69 -0
- data/lib/babushka/colorizer.rb +59 -0
- data/lib/babushka/core_patches/array.rb +171 -0
- data/lib/babushka/core_patches/blank.rb +22 -0
- data/lib/babushka/core_patches/bytes.rb +52 -0
- data/lib/babushka/core_patches/hash.rb +107 -0
- data/lib/babushka/core_patches/hashish.rb +14 -0
- data/lib/babushka/core_patches/integer.rb +25 -0
- data/lib/babushka/core_patches/io.rb +8 -0
- data/lib/babushka/core_patches/numeric.rb +16 -0
- data/lib/babushka/core_patches/object.rb +27 -0
- data/lib/babushka/core_patches/string.rb +116 -0
- data/lib/babushka/core_patches/symbol.rb +12 -0
- data/lib/babushka/core_patches/try.rb +15 -0
- data/lib/babushka/core_patches/uri.rb +24 -0
- data/lib/babushka/dep.rb +470 -0
- data/lib/babushka/dep_context.rb +18 -0
- data/lib/babushka/dep_definer.rb +115 -0
- data/lib/babushka/dep_pool.rb +49 -0
- data/lib/babushka/dep_runner.rb +85 -0
- data/lib/babushka/dsl.rb +26 -0
- data/lib/babushka/git_repo.rb +185 -0
- data/lib/babushka/helpers/git_helpers.rb +32 -0
- data/lib/babushka/helpers/log_helpers.rb +176 -0
- data/lib/babushka/helpers/path_helpers.rb +34 -0
- data/lib/babushka/helpers/run_helpers.rb +145 -0
- data/lib/babushka/helpers/shell_helpers.rb +229 -0
- data/lib/babushka/helpers/suggest_helpers.rb +16 -0
- data/lib/babushka/helpers/uri_helpers.rb +36 -0
- data/lib/babushka/ip.rb +160 -0
- data/lib/babushka/lambda_chooser.rb +40 -0
- data/lib/babushka/levenshtein.rb +125 -0
- data/lib/babushka/meta_dep.rb +65 -0
- data/lib/babushka/meta_dep_context.rb +15 -0
- data/lib/babushka/parameter.rb +143 -0
- data/lib/babushka/pkg_helper.rb +81 -0
- data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
- data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
- data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
- data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
- data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
- data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
- data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
- data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
- data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
- data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
- data/lib/babushka/popen.rb +40 -0
- data/lib/babushka/prompt.rb +176 -0
- data/lib/babushka/renderable.rb +67 -0
- data/lib/babushka/resource.rb +215 -0
- data/lib/babushka/run_reporter.rb +60 -0
- data/lib/babushka/shell.rb +108 -0
- data/lib/babushka/source.rb +216 -0
- data/lib/babushka/source_pool.rb +146 -0
- data/lib/babushka/system_definitions.rb +97 -0
- data/lib/babushka/system_profile.rb +210 -0
- data/lib/babushka/task.rb +142 -0
- data/lib/babushka/vars.rb +108 -0
- data/lib/babushka/version_of.rb +65 -0
- data/lib/babushka/version_str.rb +57 -0
- data/lib/babushka/xml_string.rb +28 -0
- data/lib/components.rb +82 -0
- data/lib/fancypath/fancypath.rb +200 -0
- data/lib/inkan/inkan.rb +76 -0
- data/spec/acceptance/acceptance.rb +43 -0
- data/spec/acceptance_helper.rb +113 -0
- data/spec/archives/Blah.app.zip +0 -0
- data/spec/archives/archive.tar +0 -0
- data/spec/archives/archive.tar.bz2 +0 -0
- data/spec/archives/archive.tar.gz +0 -0
- data/spec/archives/archive.tbz2 +0 -0
- data/spec/archives/archive.tgz +0 -0
- data/spec/archives/archive.zip +0 -0
- data/spec/archives/content.txt +5 -0
- data/spec/archives/invalid_archive +5 -0
- data/spec/archives/nested archive/content.txt +5 -0
- data/spec/archives/nested_archive.tar +0 -0
- data/spec/archives/really_a_gzip.zip +0 -0
- data/spec/archives/test-0.3.1.tgz +0 -0
- data/spec/archives/tgz_archive +0 -0
- data/spec/archives/zip_without_extension +0 -0
- data/spec/babushka/accepts_for_spec.rb +174 -0
- data/spec/babushka/accepts_for_support.rb +72 -0
- data/spec/babushka/cmdline/console_spec.rb +11 -0
- data/spec/babushka/cmdline/help_spec.rb +61 -0
- data/spec/babushka/cmdline/version_spec.rb +10 -0
- data/spec/babushka/core_patches_spec.rb +171 -0
- data/spec/babushka/dep_context_spec.rb +58 -0
- data/spec/babushka/dep_definer_spec.rb +152 -0
- data/spec/babushka/dep_definer_support.rb +36 -0
- data/spec/babushka/dep_spec.rb +567 -0
- data/spec/babushka/dep_support.rb +29 -0
- data/spec/babushka/deps_spec.rb +113 -0
- data/spec/babushka/gem_helper_spec.rb +90 -0
- data/spec/babushka/git_repo_spec.rb +396 -0
- data/spec/babushka/ip_spec.rb +131 -0
- data/spec/babushka/lambda_chooser_spec.rb +115 -0
- data/spec/babushka/meta_dep_definer_spec.rb +127 -0
- data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
- data/spec/babushka/parameter_spec.rb +135 -0
- data/spec/babushka/path_helpers_spec.rb +102 -0
- data/spec/babushka/prompt_spec.rb +188 -0
- data/spec/babushka/renderable_spec.rb +100 -0
- data/spec/babushka/resource_spec.rb +141 -0
- data/spec/babushka/run_helpers_spec.rb +26 -0
- data/spec/babushka/shell_helpers_spec.rb +244 -0
- data/spec/babushka/shell_spec.rb +19 -0
- data/spec/babushka/source_pool_spec.rb +320 -0
- data/spec/babushka/source_pool_support.rb +31 -0
- data/spec/babushka/source_spec.rb +382 -0
- data/spec/babushka/source_support.rb +17 -0
- data/spec/babushka/system_profile_spec.rb +61 -0
- data/spec/babushka/task_spec.rb +141 -0
- data/spec/babushka/uri_spec.rb +13 -0
- data/spec/babushka/vars_spec.rb +59 -0
- data/spec/babushka/version_of_spec.rb +110 -0
- data/spec/babushka/version_str_spec.rb +130 -0
- data/spec/babushka/version_str_support.rb +37 -0
- data/spec/babushka/xml_string_spec.rb +98 -0
- data/spec/deps/bad/broken.rb +7 -0
- data/spec/deps/bad/working.rb +3 -0
- data/spec/deps/good/meta.rb +14 -0
- data/spec/deps/good/test.rb +11 -0
- data/spec/deps/outer/deps.rb +19 -0
- data/spec/deps/outer/more deps.rb +11 -0
- data/spec/deps/params/params.rb +10 -0
- data/spec/fancypath/fancypath_spec.rb +272 -0
- data/spec/fancypath_support.rb +10 -0
- data/spec/inkan/inkan_spec.rb +217 -0
- data/spec/renderable/different_example.conf.erb +4 -0
- data/spec/renderable/example.conf.erb +3 -0
- data/spec/renderable/example.sh +6 -0
- data/spec/renderable/with_binding.conf.erb +4 -0
- data/spec/renderable/xml_example.conf.erb +8 -0
- data/spec/repos/remote.git.tgz +0 -0
- data/spec/spec_helper.rb +87 -0
- metadata +238 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Babushka
|
2
|
+
module GitHelpers
|
3
|
+
def git uri, opts = {}, &block
|
4
|
+
repo = GitRepo.new(opts[:to] || (BuildPrefix / File.basename(uri.to_s).chomp('.git')))
|
5
|
+
|
6
|
+
if git_update(uri, repo)
|
7
|
+
repo.root.touch # so we can tell when it was last updated
|
8
|
+
block.nil? || cd(repo.path, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def git_update uri, repo
|
15
|
+
if !repo.exists?
|
16
|
+
repo.clone! uri
|
17
|
+
else
|
18
|
+
log_block "Updating #{uri}" do
|
19
|
+
if repo.repo_shell('git fetch origin').nil?
|
20
|
+
log_error " Couldn't fetch,", :newline => false
|
21
|
+
elsif !repo.behind?
|
22
|
+
log " Already up-to-date at #{repo.current_head.colorize('yellow')},", :newline => false
|
23
|
+
true
|
24
|
+
else
|
25
|
+
log " #{repo.current_head.colorize('yellow')}..#{repo.repo_shell("git rev-parse --short origin/#{repo.current_branch}").colorize('yellow')} (#{repo.repo_shell("git log -1 --pretty=format:%s origin/#{repo.current_branch}").chomp('.')}),", :newline => false
|
26
|
+
repo.reset_hard! "origin/#{repo.current_branch}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Babushka
|
4
|
+
module LogHelpers
|
5
|
+
# Log +message+ as an error. This is a shortcut for
|
6
|
+
# log(message, :as => :error)
|
7
|
+
def log_error message, opts = {}, &block
|
8
|
+
log message, opts.merge(:as => :error), &block
|
9
|
+
end
|
10
|
+
|
11
|
+
# Log +message+ as a warning. This is a shortcut for
|
12
|
+
# log(message, :as => :warning)
|
13
|
+
def log_warn message, opts = {}, &block
|
14
|
+
log message, opts.merge(:as => :warning), &block
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_verbose message, opts = {}, &block
|
18
|
+
log_error "#{caller.first}: #log_verbose has been deprecated. Instead, just use #log." # deprecated
|
19
|
+
log message, opts, &block
|
20
|
+
end
|
21
|
+
|
22
|
+
# Yield the block, writing a note to the log about it beforehand and
|
23
|
+
# afterwards.
|
24
|
+
#
|
25
|
+
# As an example, suppose we called #log_block as follows:
|
26
|
+
# log_block('Sleeping for a bit') { sleep 10 }
|
27
|
+
#
|
28
|
+
# While the block yields, the log would show
|
29
|
+
# Sleeping for a bit... (without a newline)
|
30
|
+
#
|
31
|
+
# Once the block returns, the log would be completed to show
|
32
|
+
# Sleeping for a bit... done.
|
33
|
+
def log_block message, opts = {}, &block
|
34
|
+
log "#{message}...", :newline => false
|
35
|
+
block.call.tap {|result|
|
36
|
+
log result ? ' done.' : ' failed', :as => (result ? nil : :error), :indentation => false
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Write +message+ to the debug log, prefixed with +TickChar+, returning +true+.
|
41
|
+
#
|
42
|
+
# This is used to report events that have succeeded, or items that are
|
43
|
+
# already working. For example, when the package manager reports that a
|
44
|
+
# package is already installed, that's an 'OK' that babushka can move on to
|
45
|
+
# the next job, and so +log_ok+ is used to report that fact.
|
46
|
+
def log_ok message, opts = {}, &block
|
47
|
+
log message.end_with('.'), opts.merge(:as => :ok), &block
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Write +message+ to the debug log.
|
52
|
+
#
|
53
|
+
# The message will be written to the log in the normal style, but will only
|
54
|
+
# appear on STDOUT if debug logging is enabled.
|
55
|
+
def debug message, opts = {}, &block
|
56
|
+
log message, opts.merge(:debug => !opts[:log]), &block
|
57
|
+
end
|
58
|
+
|
59
|
+
# Write +message+ to the log.
|
60
|
+
#
|
61
|
+
# By default, the log is written to STDOUT, and to ~/.babushka/logs/<dep_name>.
|
62
|
+
# The log in ~/.babushka/logs is always a full debugging log, but STDOUT
|
63
|
+
# only includes debug logging if +--debug+ was supplied on the command
|
64
|
+
# line.
|
65
|
+
#
|
66
|
+
# By default, the message is ended with a newline. You can pass
|
67
|
+
# :newline => false to prevent the newline character being added.
|
68
|
+
#
|
69
|
+
# To specify the message type, you can use :as. There are four custom
|
70
|
+
# types supported:
|
71
|
+
# :ok The message is printed in grey with +TickChar+ prepended, as
|
72
|
+
# used by +log_ok+.
|
73
|
+
# :warning The message is printed in yellow, as used by +log_warn+.
|
74
|
+
# :error The message is printed in red, as used by +log_error+.
|
75
|
+
# :stderr The message (representing STDERR output) is printed in bold,
|
76
|
+
# as used by +Shell+ for debug logging.
|
77
|
+
#
|
78
|
+
# If a block is given, the block is yielded with the indentation level
|
79
|
+
# incremented. Opening and closing braces are printed to the log to represent
|
80
|
+
# the nesting. (This is the logging style used to show the nesting during dep
|
81
|
+
# runs - so please consider other logging styles before using this one, so as
|
82
|
+
# not to visually confuse dep runs with other operations.)
|
83
|
+
def log message, opts = {}, &block
|
84
|
+
# now = Time.now
|
85
|
+
# print "#{now.to_i}.#{now.usec}: ".ljust(20) unless opts[:debug]
|
86
|
+
printable = !opts[:debug] || Base.task.opt(:debug)
|
87
|
+
Logging.print_log Logging.indentation, printable unless opts[:indentation] == false
|
88
|
+
if block_given?
|
89
|
+
Logging.print_log "#{message} {\n".colorize('grey'), printable
|
90
|
+
Logging.indent! if printable
|
91
|
+
yield.tap {|result|
|
92
|
+
Logging.undent! if printable
|
93
|
+
log Logging.closing_log_message(message, result, opts), opts
|
94
|
+
}
|
95
|
+
else
|
96
|
+
message = message.to_s.rstrip.gsub "\n", "\n#{Logging.indentation}"
|
97
|
+
message = "#{Logging::TickChar.colorize('grey')} #{message}" if opts[:as] == :ok
|
98
|
+
message = message.colorize 'red' if opts[:as] == :error
|
99
|
+
message = message.colorize 'yellow' if opts[:as] == :warning
|
100
|
+
message = message.colorize 'bold' if opts[:as] == :stderr
|
101
|
+
message = message.end_with "\n" unless opts[:newline] == false
|
102
|
+
Logging.print_log message, printable
|
103
|
+
$stdout.flush
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Logging
|
110
|
+
extend LogHelpers
|
111
|
+
|
112
|
+
TickChar = '✓'
|
113
|
+
CrossChar = '✗'
|
114
|
+
|
115
|
+
def self.closing_log_message message, result = true, opts = {}
|
116
|
+
message = opts[:closing_status] if opts[:closing_status].is_a?(String)
|
117
|
+
|
118
|
+
if opts[:closing_status] == :status_only
|
119
|
+
'}'.colorize('grey') + ' ' + "#{result ? TickChar : CrossChar}".colorize(result ? 'green' : 'red')
|
120
|
+
elsif opts[:closing_status] == :dry_run
|
121
|
+
'}'.colorize('grey') + ' ' + "#{result ? TickChar : '~'} #{message}".colorize(result ? 'green' : 'blue')
|
122
|
+
elsif opts[:closing_status]
|
123
|
+
'}'.colorize('grey') + ' ' + "#{result ? TickChar : CrossChar} #{message}".colorize(result ? 'green' : 'red')
|
124
|
+
else
|
125
|
+
"}".colorize('grey')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.log_table headings, rows
|
130
|
+
all_rows = rows.map {|row|
|
131
|
+
row.map(&:to_s)
|
132
|
+
}.unshift(
|
133
|
+
headings
|
134
|
+
).transpose.map {|col|
|
135
|
+
max_length = col.map(&:length).max
|
136
|
+
col.map {|cell| cell.ljust(max_length) }
|
137
|
+
}.transpose
|
138
|
+
|
139
|
+
[
|
140
|
+
all_rows.first.join(' | '),
|
141
|
+
all_rows.first.map {|i| '-' * i.length }.join('-+-')
|
142
|
+
].concat(
|
143
|
+
all_rows[1..-1].map {|row| row.join(' | ') }
|
144
|
+
).each {|row|
|
145
|
+
log row
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def self.print_log message, printable
|
153
|
+
print message if printable
|
154
|
+
write_to_persistent_log message
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.write_to_persistent_log message
|
158
|
+
Base.task.persistent_log.write message unless Base.task.persistent_log.nil?
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.indentation
|
162
|
+
' ' * indentation_level * 2
|
163
|
+
end
|
164
|
+
def self.indentation_level
|
165
|
+
@indentation_level ||= 0
|
166
|
+
end
|
167
|
+
def self.indent!
|
168
|
+
@indentation_level ||= 0
|
169
|
+
@indentation_level += 1
|
170
|
+
end
|
171
|
+
def self.undent!
|
172
|
+
@indentation_level ||= 0
|
173
|
+
@indentation_level -= 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Babushka
|
2
|
+
module PathHelpers
|
3
|
+
def cd dir, opts = {}, &block
|
4
|
+
if dir.nil?
|
5
|
+
yield Dir.pwd.p
|
6
|
+
else
|
7
|
+
path = dir.p
|
8
|
+
shell("mkdir -p '#{path}'", :sudo => opts[:sudo]) if opts[:create] unless path.exists?
|
9
|
+
if Dir.pwd == path
|
10
|
+
yield path
|
11
|
+
else
|
12
|
+
Dir.chdir path do
|
13
|
+
debug "in dir #{dir} (#{path})" do
|
14
|
+
yield path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def in_dir dir, opts = {}, &block
|
22
|
+
log_error "#{caller.first}: #in_dir has been renamed to #cd." # deprecated
|
23
|
+
cd dir, opts, &block
|
24
|
+
end
|
25
|
+
|
26
|
+
def in_build_dir path = '', &block
|
27
|
+
cd Babushka::BuildPrefix / path, :create => true, &block
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_download_dir path = '', &block
|
31
|
+
cd Babushka::DownloadPrefix / path, :create => true, &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Babushka
|
2
|
+
module RunHelpers
|
3
|
+
include LogHelpers
|
4
|
+
include ShellHelpers
|
5
|
+
include PathHelpers
|
6
|
+
|
7
|
+
def hostname
|
8
|
+
shell 'hostname -f'
|
9
|
+
end
|
10
|
+
|
11
|
+
def rake cmd, &block
|
12
|
+
sudo "rake #{cmd} RAILS_ENV=#{var :app_env}", :as => var(:username), &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def bundle_rake cmd, &block
|
16
|
+
cd var(:rails_root) do
|
17
|
+
shell "bundle exec rake #{cmd} --trace RAILS_ENV=#{var :app_env}", :as => var(:username), :log => true, &block
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_file file_name, method_name
|
22
|
+
File.send(method_name, file_name).tap {|result|
|
23
|
+
log_error "#{file_name} failed #{method_name.to_s.sub(/[?!]$/, '')} check." unless result
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def grep pattern, file
|
28
|
+
if (path = file.p).exists?
|
29
|
+
output = if pattern.is_a? String
|
30
|
+
path.readlines.select {|l| l[pattern] }
|
31
|
+
elsif pattern.is_a? Regexp
|
32
|
+
path.readlines.grep pattern
|
33
|
+
end
|
34
|
+
output unless output.blank?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def change_line line, replacement, filename
|
39
|
+
path = filename.p
|
40
|
+
|
41
|
+
log "Patching #{path}"
|
42
|
+
shell "cat > #{path}", :as => path.owner, :input => path.readlines.map {|l|
|
43
|
+
l.gsub(/^(\s*)(#{Regexp.escape(line)})/, "\\1# #{edited_by_babushka}\n\\1# was: \\2\n\\1#{replacement}")
|
44
|
+
}.join("")
|
45
|
+
end
|
46
|
+
|
47
|
+
def insert_into_file insert_before, path, lines, opts = {}
|
48
|
+
opts.defaults! :comment_char => '#', :insert_after => nil
|
49
|
+
nlines = lines.split("\n").length
|
50
|
+
before, after = path.p.readlines.cut {|l| l.strip == insert_before.strip }
|
51
|
+
|
52
|
+
log "Patching #{path}"
|
53
|
+
if after.empty? || (opts[:insert_after] && before.last.strip != opts[:insert_after].strip)
|
54
|
+
log_error "Couldn't find the spot to write to in #{path}."
|
55
|
+
else
|
56
|
+
shell "cat > #{path}", :as => path.owner, :sudo => !File.writable?(path), :input => [
|
57
|
+
before,
|
58
|
+
added_by_babushka(nlines).start_with(opts[:comment_char] + ' ').end_with("\n"),
|
59
|
+
lines.end_with("\n"),
|
60
|
+
after
|
61
|
+
].join
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def change_with_sed keyword, from, to, file
|
66
|
+
# Remove the incorrect setting if it's there
|
67
|
+
shell("#{sed} -ri 's/^#{keyword}\s+#{from}//' #{file}", :sudo => !File.writable?(file))
|
68
|
+
# Add the correct setting unless it's already there
|
69
|
+
grep(/^#{keyword}\s+#{to}/, file) or shell("echo '#{keyword} #{to}' >> #{file}", :sudo => !File.writable?(file))
|
70
|
+
end
|
71
|
+
|
72
|
+
def sed
|
73
|
+
Base.host.linux? ? 'sed' : 'gsed'
|
74
|
+
end
|
75
|
+
|
76
|
+
def append_to_file text, file, opts = {}
|
77
|
+
text = text.to_s
|
78
|
+
shell %Q{echo "\n# #{added_by_babushka(text.split("\n").length)}\n#{text.gsub('"', '\"')}" >> #{file}}, opts
|
79
|
+
end
|
80
|
+
|
81
|
+
def _by_babushka
|
82
|
+
"by babushka-#{VERSION} at #{Time.now}"
|
83
|
+
end
|
84
|
+
def edited_by_babushka
|
85
|
+
"This line edited #{_by_babushka}"
|
86
|
+
end
|
87
|
+
def added_by_babushka nlines
|
88
|
+
if nlines == 1
|
89
|
+
"This line added #{_by_babushka}"
|
90
|
+
else
|
91
|
+
"These #{nlines} lines added #{_by_babushka}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def babushka_config? path
|
96
|
+
if !path.p.exists?
|
97
|
+
unmet "the config hasn't been generated yet"
|
98
|
+
elsif !grep(/Generated by babushka/, path)
|
99
|
+
unmet "the config needs to be regenerated"
|
100
|
+
else
|
101
|
+
true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def yaml path
|
106
|
+
require 'yaml'
|
107
|
+
YAML.load_file path.p
|
108
|
+
end
|
109
|
+
|
110
|
+
def render_erb erb, opts = {}
|
111
|
+
if (path = erb_path_for(erb)).nil?
|
112
|
+
log_error "If you use #render_erb within a dynamically defined dep, you have to give the full path to the erb template."
|
113
|
+
elsif !File.exists?(path) && !opts[:optional]
|
114
|
+
log_error "Couldn't find erb to render at #{path}."
|
115
|
+
elsif File.exists?(path)
|
116
|
+
Renderable.new(opts[:to]).render(path, opts.merge(:context => self)).tap {|result|
|
117
|
+
if result
|
118
|
+
log "Rendered #{opts[:to]}."
|
119
|
+
else
|
120
|
+
log_error "Couldn't render #{opts[:to]}."
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def erb_path_for erb
|
127
|
+
if erb.to_s.starts_with? '/'
|
128
|
+
erb # absolute path
|
129
|
+
elsif load_path
|
130
|
+
File.dirname(load_path) / erb # directory this dep is in, plus relative path
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def log_and_open message, url
|
135
|
+
log "#{message} Hit Enter to open the download page.", :newline => false
|
136
|
+
read_from_prompt ' '
|
137
|
+
shell "open #{url}"
|
138
|
+
end
|
139
|
+
|
140
|
+
def mysql cmd, username = 'root', include_password = true
|
141
|
+
password_segment = "--password='#{var :db_password}'" if include_password
|
142
|
+
shell "echo \"#{cmd.gsub('"', '\"').end_with(';')}\" | mysql -u #{username} #{password_segment}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
module Babushka
|
2
|
+
module ShellHelpers
|
3
|
+
include LogHelpers
|
4
|
+
|
5
|
+
# Run +cmd+.
|
6
|
+
#
|
7
|
+
# If the command succeeds (i.e. returns 0), its output will be returned
|
8
|
+
# with a trailing newline stripped, if there was one. If the command fails
|
9
|
+
# (i.e. returns a non-zero value), nil will be returned.
|
10
|
+
#
|
11
|
+
# If a block is given, it will be yielded once the command has run, with a
|
12
|
+
# Babushka::Shell object as its sole argument. Details of the shell command
|
13
|
+
# are contained in this object - see the methods +cmd+, +ok?+, +result+,
|
14
|
+
# +stdout+, and +stderr+.
|
15
|
+
#
|
16
|
+
# Several options can be provided to alter #shell's behaviour.
|
17
|
+
# <tt>:sudo => true</tt> runs the the command as root. If the command
|
18
|
+
# contains piping or redirection, a 'sudo su' variant will be used
|
19
|
+
# instead so that the pipe receiver or redirect targets are also
|
20
|
+
# included in the sudo.
|
21
|
+
# <tt>:as => 'user'</tt> causes sudo to run as the specified user instead
|
22
|
+
# of root.
|
23
|
+
# <tt>:sudo => 'user'</tt> is a shortcut that has the same effect as
|
24
|
+
# <tt>:sudo => true, :as => 'user'</tt>
|
25
|
+
# <tt>:cd</tt> specifies the directory in which the command should run.
|
26
|
+
# If the path doesn't exist or isn't a directory, an error is raised
|
27
|
+
# unless the <tt>:create</tt> option is also set.
|
28
|
+
# To achieve the directory change, the command is rewritten to change
|
29
|
+
# directory first: `cd #{dir} && #{cmd}`.
|
30
|
+
# <tt>:create</tt> causes the directory specified by the <tt>:cd</tt>
|
31
|
+
# option to be created if it doesn't already exist.
|
32
|
+
# <tt>:input</tt> can be used to supply input for the shell command. It
|
33
|
+
# be any object that can be written to an IO with <tt>io << obj</tt>.
|
34
|
+
# When passed, it will be written to the command's stdin pipe before
|
35
|
+
# any output is read.
|
36
|
+
# <tt>:spinner => true</tt> When this option is passed, a /-\| spinner
|
37
|
+
# is printed to stdout, and advanced whenever a line is read on the
|
38
|
+
# command's stdout or stderr pipes. This is useful for monitoring the
|
39
|
+
# progress of a long-running command, like a build or an installer.
|
40
|
+
def shell *cmd, &block
|
41
|
+
shell!(*cmd, &block)
|
42
|
+
rescue Shell::ShellCommandFailed => e
|
43
|
+
if cmd.extract_options[:log]
|
44
|
+
# Don't log the error if the command already logged
|
45
|
+
elsif e.stdout.empty? && e.stderr.empty?
|
46
|
+
log "$ #{e.cmd.join(' ')}".colorize('grey') + ' ' + "#{Logging::CrossChar} shell command failed".colorize('red')
|
47
|
+
else
|
48
|
+
log "$ #{e.cmd.join(' ')}", :closing_status => 'shell command failed' do
|
49
|
+
log_error(e.stderr.empty? ? e.stdout : e.stderr)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Run +cmd+, returning true if its exit code was 0.
|
55
|
+
#
|
56
|
+
# This is useful to run shell commands whose output isn't important,
|
57
|
+
# but whose exit code is. Unlike +#shell+, which logs the output of shell
|
58
|
+
# commands that exit with non-zero status, +#shell?+ runs silently.
|
59
|
+
#
|
60
|
+
# The idea is that +#shell+ is for when you're interested in the command's
|
61
|
+
# output, and +#shell?+ is for when you're interested in the exit status.
|
62
|
+
def shell? *cmd
|
63
|
+
shell(*cmd) {|s| s.stdout.chomp if s.ok? }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Run +cmd+ via #shell, raising an exception if it doesn't exit
|
67
|
+
# with success.
|
68
|
+
def shell! *cmd, &block
|
69
|
+
opts = cmd.extract_options!
|
70
|
+
cmd = cmd.first if cmd.map(&:class) == [Array]
|
71
|
+
|
72
|
+
if opts[:dir] # deprecated
|
73
|
+
log_error "#{caller.first}: #shell's :dir option has been renamed to :cd."
|
74
|
+
opts[:cd] = opts[:dir]
|
75
|
+
end
|
76
|
+
if opts[:cd]
|
77
|
+
if !opts[:cd].p.exists?
|
78
|
+
if opts[:create]
|
79
|
+
opts[:cd].p.mkdir
|
80
|
+
else
|
81
|
+
raise Errno::ENOENT, opts[:cd]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
shell_method = (opts[:as] || opts[:sudo]) ? :sudo : :shell_cmd
|
86
|
+
send shell_method, *cmd.dup.push(opts), &block
|
87
|
+
end
|
88
|
+
|
89
|
+
# This method is a shortcut for accessing the results of a shell command
|
90
|
+
# without using a block. The method itself returns the shell object that
|
91
|
+
# is yielded to the block by +#shell+.
|
92
|
+
# As an example, this shell command:
|
93
|
+
# shell('grep rails Gemfile') {|shell| shell.stdout }.empty?
|
94
|
+
# can be simplified to this:
|
95
|
+
# raw_shell('grep rails Gemfile').stdout.empty?
|
96
|
+
def raw_shell *cmd
|
97
|
+
shell(*cmd) {|s| s }
|
98
|
+
end
|
99
|
+
|
100
|
+
def failable_shell *cmd
|
101
|
+
log_error "#failable_shell has been renamed to #raw_shell." # deprecated
|
102
|
+
raw_shell(*cmd)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Run +cmd+ in a separate interactive shell. This is useful for running
|
106
|
+
# commands that depend on something shell-related that was changed during
|
107
|
+
# this run, like changing the user's shell. It's also useful for running
|
108
|
+
# commands that are only valid on an interactive shell, like rvm-related
|
109
|
+
# commands.
|
110
|
+
# TODO: specs.
|
111
|
+
def login_shell cmd, opts = {}, &block
|
112
|
+
if shell('echo $SHELL').p.basename == 'zsh'
|
113
|
+
shell %Q{zsh -i -c "#{cmd.gsub('"', '\"')}"}, opts, &block
|
114
|
+
else
|
115
|
+
shell %Q{bash -l -c "#{cmd.gsub('"', '\"')}"}, opts, &block
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Run +cmd+ via `sudo`, bypassing it if possible (i.e. if we're running as
|
120
|
+
# root already, or as the user that was requested).
|
121
|
+
#
|
122
|
+
# The return behaviour and block handling of +#sudo+ are identical to that
|
123
|
+
# of +#shell+. In fact, +#sudo+ constructs a sudo command, and then uses
|
124
|
+
# +#shell+ internally to run the command.
|
125
|
+
#
|
126
|
+
# All the options that can be passed to +#shell+ are valid for +#sudo+ as
|
127
|
+
# well. The :sudo and :as options can be ommitted, though, which will cause
|
128
|
+
# the command to be run as root. Hence, this sudo call:
|
129
|
+
# sudo('ls')
|
130
|
+
# is equivalent to these two shell calls:
|
131
|
+
# shell('ls', :sudo => true)
|
132
|
+
# shell('ls', :as => 'root')
|
133
|
+
#
|
134
|
+
# In the same manner, this sudo call:
|
135
|
+
# sudo('ls', :as => 'ben')
|
136
|
+
# is equivalent to these two shell calls:
|
137
|
+
# shell('ls', :sudo => 'ben')
|
138
|
+
# shell('ls', :as => 'ben')
|
139
|
+
def sudo *cmd, &block
|
140
|
+
opts = cmd.extract_options!
|
141
|
+
env = cmd.first.is_a?(Hash) ? cmd.shift : {}
|
142
|
+
|
143
|
+
if cmd.map(&:class) != [String]
|
144
|
+
raise ArgumentError, "#sudo commands have to be passed as a single string, not splatted strings or an array, since the `sudo` is composed from strings."
|
145
|
+
end
|
146
|
+
|
147
|
+
as = opts[:as] || (opts[:sudo].is_a?(String) ? opts[:sudo] : 'root')
|
148
|
+
cmd = cmd.last
|
149
|
+
|
150
|
+
sudo_cmd = if current_username == as
|
151
|
+
cmd # Don't sudo if we're already running as the specified user.
|
152
|
+
else
|
153
|
+
if opts[:su] || cmd[' |'] || cmd[' >']
|
154
|
+
"sudo su - #{as} -c \"#{cmd.gsub('"', '\"')}\""
|
155
|
+
else
|
156
|
+
"sudo -u #{as} #{cmd}"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
shell [env, sudo_cmd], opts.discard(:as, :sudo, :su), &block
|
161
|
+
end
|
162
|
+
|
163
|
+
# This method returns the full path to the specified command in the PATH,
|
164
|
+
# if that command appears anywhere in the PATH. If it doesn't, nil is
|
165
|
+
# returned.
|
166
|
+
#
|
167
|
+
# For example, on a stock OS X machine:
|
168
|
+
# which('ruby') #=> "/usr/bin/ruby"
|
169
|
+
# which('babushka') #=> nil
|
170
|
+
#
|
171
|
+
# This is roughly equivalent to using `which` or `type` on the shell.
|
172
|
+
# However, because those commands' behaviour and ouptut vary across
|
173
|
+
# platforms and shells, we instead use the logic in #cmd_dir.
|
174
|
+
def which cmd_name
|
175
|
+
matching_dir = cmd_dir(cmd_name)
|
176
|
+
File.join(matching_dir, cmd_name.to_s) unless matching_dir.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
# Return the directory from which the specified command would run if
|
180
|
+
# invoked via the PATH. If the command doesn't appear in the PATH, nil is
|
181
|
+
# returned.
|
182
|
+
#
|
183
|
+
# For example, on a stock OS X machine:
|
184
|
+
# cmd_dir('ruby') #=> "/usr/bin"
|
185
|
+
# cmd_dir('babushka') #=> nil
|
186
|
+
#
|
187
|
+
# This is a direct implementation because the behaviour and output of
|
188
|
+
# `which` and `type` vary across different platforms and shells. It's
|
189
|
+
# also faster to not shell out.
|
190
|
+
def cmd_dir cmd_name
|
191
|
+
ENV['PATH'].split(':').detect {|path|
|
192
|
+
File.executable? File.join(path, cmd_name.to_s)
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
# Run a shell command, logging before and after using #log_block, and using
|
197
|
+
# a spinner while the command runs.
|
198
|
+
# The first argument, +message+, is the message to print before running the
|
199
|
+
# command, and the remaining arguments are identical to those of #shell.
|
200
|
+
#
|
201
|
+
# As an example, suppose we called #log_shell as follows:
|
202
|
+
# log_shell('Sleeping for a bit', 'sleep 10')
|
203
|
+
#
|
204
|
+
# While the command runs, the log would show
|
205
|
+
# Sleeping for a bit... (without a newline)
|
206
|
+
#
|
207
|
+
# The command runs with a /-\| spinner that animates each time a line of
|
208
|
+
# output is emitted by the command. Once the command terminates, the log
|
209
|
+
# would be completed to show
|
210
|
+
# Sleeping for a bit... done.
|
211
|
+
def log_shell message, *cmd, &block
|
212
|
+
opts = cmd.extract_options!
|
213
|
+
log_block message do
|
214
|
+
shell *cmd.dup.push(opts.merge(:spinner => true)), &block
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
def shell_cmd *cmd, &block
|
221
|
+
Shell.new(*cmd).run(&block)
|
222
|
+
end
|
223
|
+
|
224
|
+
def current_username
|
225
|
+
require 'etc'
|
226
|
+
Etc.getpwuid(Process.euid).name
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|