rubycut-babushka 0.10.6
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.
- 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
|