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,67 @@
|
|
|
1
|
+
module Babushka
|
|
2
|
+
class Renderable
|
|
3
|
+
include ShellHelpers
|
|
4
|
+
include RunHelpers
|
|
5
|
+
|
|
6
|
+
SEAL_REGEXP = /# Generated by babushka-[\d\.]+ at [a-zA-Z\d\-\:\s+]+, from [0-9a-f]{40}+\. [0-9a-f]{40}/
|
|
7
|
+
|
|
8
|
+
attr_reader :path
|
|
9
|
+
def initialize path
|
|
10
|
+
@path = path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render source, opts = {}
|
|
14
|
+
shell("cat > '#{path}'",
|
|
15
|
+
:input => inkan_output_for(source, opts),
|
|
16
|
+
:sudo => opts[:sudo]
|
|
17
|
+
).tap {|result|
|
|
18
|
+
if result
|
|
19
|
+
sudo "chmod #{opts[:perms]} '#{path}'" if opts[:perms]
|
|
20
|
+
end
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def exists?
|
|
25
|
+
path.p.exists?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def clean?
|
|
29
|
+
Inkan.legitimate? path
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def from? source
|
|
33
|
+
exists? && source_sha == sha_of(source)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def inkan_output_for source, opts = {}
|
|
39
|
+
Inkan.render {|inkan|
|
|
40
|
+
inkan.credit = "Generated #{_by_babushka}, from #{sha_of(source)}"
|
|
41
|
+
inkan.comment = opts[:comment] if opts[:comment]
|
|
42
|
+
inkan.comment_suffix = opts[:comment_suffix] if opts[:comment_suffix]
|
|
43
|
+
inkan.print render_erb(source, opts[:context])
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def render_erb source, custom_context
|
|
48
|
+
require 'erb'
|
|
49
|
+
(custom_context || self).instance_eval {
|
|
50
|
+
ERB.new(source.p.read).result(binding)
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def sha_of source
|
|
55
|
+
require 'digest/sha1'
|
|
56
|
+
raise "Source doesn't exist: #{source.p}" unless source.p.exists?
|
|
57
|
+
Digest::SHA1.hexdigest(source.p.read)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def source_sha
|
|
61
|
+
File.open(path.p) {|f|
|
|
62
|
+
first_line = f.gets
|
|
63
|
+
first_line[/\A#!/] ? f.gets : first_line
|
|
64
|
+
}.scan(/, from ([0-9a-f]{40})\./).flatten.first
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
module Babushka
|
|
2
|
+
class ResourceError < StandardError
|
|
3
|
+
end
|
|
4
|
+
class Resource
|
|
5
|
+
include LogHelpers
|
|
6
|
+
extend LogHelpers
|
|
7
|
+
include ShellHelpers
|
|
8
|
+
extend ShellHelpers
|
|
9
|
+
include PathHelpers
|
|
10
|
+
extend PathHelpers
|
|
11
|
+
|
|
12
|
+
def self.get url, &block
|
|
13
|
+
filename = URI.unescape(url.to_s).p.basename
|
|
14
|
+
if filename.to_s.blank?
|
|
15
|
+
log_error "Not a valid URL to download: #{url}"
|
|
16
|
+
else
|
|
17
|
+
download_path = in_download_dir {|path|
|
|
18
|
+
downloaded_file = download(url, filename)
|
|
19
|
+
path / downloaded_file if downloaded_file
|
|
20
|
+
}
|
|
21
|
+
block.call download_path unless download_path.nil?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.extract url, &block
|
|
26
|
+
get url do |download_path|
|
|
27
|
+
in_build_dir {
|
|
28
|
+
Resource.for(download_path).extract(&block)
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.download url, filename = url.to_s.p.basename
|
|
34
|
+
if filename.p.exists? && !filename.p.empty?
|
|
35
|
+
log_ok "Already downloaded #{filename}."
|
|
36
|
+
filename
|
|
37
|
+
elsif (result = shell(%Q{curl -I -X GET "#{url}"})).nil?
|
|
38
|
+
log_error "Couldn't download #{url}: `curl` exited with non-zero status."
|
|
39
|
+
else
|
|
40
|
+
response_code = result.val_for(/HTTP\/1\.\d/) # not present for ftp://, etc.
|
|
41
|
+
if response_code && response_code[/^[23]/].nil?
|
|
42
|
+
log_error "Couldn't download #{url}: #{response_code}."
|
|
43
|
+
elsif !(location = result.val_for('Location')).nil?
|
|
44
|
+
log "Following redirect from #{url}"
|
|
45
|
+
download URI.escape(location), location.p.basename
|
|
46
|
+
else
|
|
47
|
+
success = log_block "Downloading #{url}" do
|
|
48
|
+
shell %Q{curl -# -o "#{filename}.tmp" "#{url}" && mv -f "#{filename}.tmp" "#{filename}"}, :progress => /[\d\.]+%/
|
|
49
|
+
end
|
|
50
|
+
filename if success
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.detect_type_by_extension path
|
|
56
|
+
TYPES.keys.detect {|key|
|
|
57
|
+
TYPES[key][:exts].any? {|extension|
|
|
58
|
+
path.has_extension? extension
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.detect_type_by_contents path
|
|
64
|
+
TYPES.keys.detect {|key|
|
|
65
|
+
shell("file '#{path}'")[TYPES[key][:file_match]]
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.type path
|
|
70
|
+
detect_type_by_extension(path) || detect_type_by_contents(path)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
TYPES = {
|
|
74
|
+
:deb => {:file_match => 'Debian binary package', :exts => %w[deb]},
|
|
75
|
+
:pkg => {:file_match => 'xar archive', :exts => %w[pkg]},
|
|
76
|
+
:tar => {:file_match => 'tar archive', :exts => %w[tar]},
|
|
77
|
+
:gzip => {:file_match => 'gzip compressed data', :exts => %w[tgz tar.gz]},
|
|
78
|
+
:bzip2 => {:file_match => 'bzip2 compressed data', :exts => %w[tbz2 tar.bz2]},
|
|
79
|
+
:zip => {:file_match => 'Zip archive data', :exts => %w[zip]},
|
|
80
|
+
:dmg => {:file_match => 'VAX COFF executable not stripped', :exts => %w[dmg]}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
attr_reader :path, :name
|
|
84
|
+
|
|
85
|
+
def initialize path, opts = {}
|
|
86
|
+
@path = path.p
|
|
87
|
+
@name = TYPES[type][:exts].inject(filename) {|fn,t| fn.gsub(/\.#{t}$/, '') }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def filename
|
|
91
|
+
path.basename.to_s
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def type
|
|
95
|
+
self.class.type path
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def supported?
|
|
99
|
+
!type.nil?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def extract &block
|
|
103
|
+
cd(archive_prefix, :create => true) { process_extract(&block) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def process_extract &block
|
|
107
|
+
shell("mkdir -p '#{name}'") and
|
|
108
|
+
cd(name) {
|
|
109
|
+
unless log_shell("Extracting #{filename}", extract_command)
|
|
110
|
+
log_error "Couldn't extract #{path} - probably a bad download."
|
|
111
|
+
else
|
|
112
|
+
cd(content_subdir) {
|
|
113
|
+
block.nil? or block.call(self)
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def content_subdir
|
|
120
|
+
identity_dirs.reject {|dir|
|
|
121
|
+
%w[app pkg bundle tmbundle prefPane].map {|i|
|
|
122
|
+
/\.#{i}$/
|
|
123
|
+
}.any? {|dont_descend|
|
|
124
|
+
dir[dont_descend]
|
|
125
|
+
}
|
|
126
|
+
}.first
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def identity_dirs
|
|
130
|
+
everything = Dir.glob('*')
|
|
131
|
+
if everything.length == 1 && File.directory?(everything.first)
|
|
132
|
+
everything
|
|
133
|
+
else
|
|
134
|
+
Dir.glob('*/').map {|dir| dir.chomp('/') }.select {|dir|
|
|
135
|
+
dir.downcase.gsub(/[ \-_\.]/, '') == name.downcase.gsub(/[ \-_\.]/, '')
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def archive_prefix
|
|
141
|
+
BuildPrefix
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class FileResource < Resource
|
|
146
|
+
def extract &block
|
|
147
|
+
in_download_dir {
|
|
148
|
+
block.call(self)
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class TarResource < Resource
|
|
154
|
+
def extract_command
|
|
155
|
+
"tar -#{extract_option(type)}xf '#{path}'"
|
|
156
|
+
end
|
|
157
|
+
def extract_option type
|
|
158
|
+
{
|
|
159
|
+
:tar => '',
|
|
160
|
+
:gzip => 'z',
|
|
161
|
+
:bzip2 => 'j'
|
|
162
|
+
}[type]
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class ZipResource < Resource
|
|
167
|
+
def extract_command
|
|
168
|
+
"unzip -o '#{path}'"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class DmgResource < Resource
|
|
173
|
+
def extract &block
|
|
174
|
+
in_download_dir {
|
|
175
|
+
output = log_shell "Attaching #{filename}", "hdiutil attach '#{filename.p.basename}'"
|
|
176
|
+
if output.nil?
|
|
177
|
+
log_error "Couldn't mount #{filename.p}."
|
|
178
|
+
elsif (path = mountpoint_for(output)).nil?
|
|
179
|
+
raise "Couldn't find where `hdiutil` mounted #{filename.p}."
|
|
180
|
+
else
|
|
181
|
+
cd(path) {
|
|
182
|
+
block.call(self)
|
|
183
|
+
}.tap {
|
|
184
|
+
log_shell "Detaching #{filename}", "hdiutil detach '#{path}'"
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
}
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def mountpoint_for output
|
|
191
|
+
output.scan(/\s+(\/Volumes\/[^\n]+)/).flatten.first
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class Resource
|
|
196
|
+
CLASSES = {
|
|
197
|
+
:deb => FileResource,
|
|
198
|
+
:pkg => FileResource,
|
|
199
|
+
:tar => TarResource,
|
|
200
|
+
:gzip => TarResource,
|
|
201
|
+
:bzip2 => TarResource,
|
|
202
|
+
:zip => ZipResource,
|
|
203
|
+
:dmg => DmgResource
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
def self.for path, opts = {}
|
|
207
|
+
path = path.p
|
|
208
|
+
filename = path.basename.to_s
|
|
209
|
+
raise ResourceError, "The archive #{filename} does not exist." unless path.exists?
|
|
210
|
+
klass = CLASSES[type(path)]
|
|
211
|
+
raise ResourceError, "Don't know how to extract #{filename}." if klass.nil?
|
|
212
|
+
klass.new(path, opts)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Babushka
|
|
2
|
+
class RunReporter
|
|
3
|
+
class << self
|
|
4
|
+
include LogHelpers
|
|
5
|
+
|
|
6
|
+
def queue dep, result, reportable
|
|
7
|
+
if dep.dep_source.type != :public
|
|
8
|
+
debug "Not reporting #{dep.contextual_name}, since it's not in a public source."
|
|
9
|
+
else
|
|
10
|
+
queue_report dep, (reportable ? 'error' : (result ? 'ok' : 'fail'))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def post_reports
|
|
15
|
+
require 'net/http'
|
|
16
|
+
|
|
17
|
+
while Base.task.running? && (report = most_recent_report)
|
|
18
|
+
post_report report
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def post_report report
|
|
26
|
+
submit_report_to_webservice(report.p.read).tap {|result|
|
|
27
|
+
report.p.rm if result
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
require 'net/http'
|
|
32
|
+
def submit_report_to_webservice data
|
|
33
|
+
Net::HTTP.start('babushka.me') {|http|
|
|
34
|
+
http.open_timeout = http.read_timeout = 5
|
|
35
|
+
http.post '/runs.json', data
|
|
36
|
+
}.is_a?(Net::HTTPSuccess)
|
|
37
|
+
rescue Errno::ECONNREFUSED, SocketError
|
|
38
|
+
log_error "Couldn't connect to the babushka webservice." unless Base.task.running?
|
|
39
|
+
rescue Timeout::Error, Errno::ETIMEDOUT
|
|
40
|
+
debug "Timeout while submitting run report."
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def most_recent_report
|
|
44
|
+
ReportPrefix.p.glob('*').sort.last
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def queue_report dep, result
|
|
48
|
+
ReportPrefix.p.mkdir
|
|
49
|
+
(ReportPrefix / Time.now.to_f).open('w') {|f|
|
|
50
|
+
f << run_report_for(dep, result).to_http_params
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def run_report_for dep, result
|
|
55
|
+
Base.task.task_info(dep, result)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module Babushka
|
|
2
|
+
class Shell
|
|
3
|
+
include LogHelpers
|
|
4
|
+
|
|
5
|
+
class ShellCommandFailed < StandardError
|
|
6
|
+
attr_reader :cmd, :stdout, :stderr
|
|
7
|
+
def initialize cmd, stdout, stderr
|
|
8
|
+
@cmd, @stdout, @stderr = cmd, stdout, stderr
|
|
9
|
+
message = if stderr.empty?
|
|
10
|
+
"Shell command failed: '#{cmd.join(' ')}'"
|
|
11
|
+
else
|
|
12
|
+
"Shell command failed: '#{cmd.join(' ')}':\n#{stderr}"
|
|
13
|
+
end
|
|
14
|
+
super message
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :cmd, :opts, :env, :result, :stdout, :stderr
|
|
19
|
+
|
|
20
|
+
def initialize *cmd
|
|
21
|
+
@opts = cmd.extract_options!
|
|
22
|
+
raise "You can't use :spinner and :progress together in Babushka::Shell." if opts[:spinner] && opts[:progress]
|
|
23
|
+
@env = cmd.first.is_a?(Hash) ? cmd.shift : {}
|
|
24
|
+
@cmd = cmd
|
|
25
|
+
@progress = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ok?; result == 0 end
|
|
29
|
+
|
|
30
|
+
def run &block
|
|
31
|
+
@stdout, @stderr = '', ''
|
|
32
|
+
@result = invoke
|
|
33
|
+
print "#{" " * (@progress.length + 1)}#{"\b" * (@progress.length + 1)}" unless @progress.nil?
|
|
34
|
+
|
|
35
|
+
if block_given?
|
|
36
|
+
yield(self)
|
|
37
|
+
elsif ok?
|
|
38
|
+
stdout.chomp
|
|
39
|
+
else
|
|
40
|
+
raise ShellCommandFailed.new(cmd, stdout, stderr)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def invoke
|
|
47
|
+
debug "$ #{@cmd.join(' ')}".colorize('grey')
|
|
48
|
+
Babushka::Open3.popen3 @cmd, popen_opts do |stdin,stdout,stderr,thread|
|
|
49
|
+
unless @opts[:input].nil?
|
|
50
|
+
stdin << @opts[:input]
|
|
51
|
+
stdin.close
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
spinner_offset = -1
|
|
55
|
+
should_spin = @opts[:spinner] && !Base.task.opt(:debug)
|
|
56
|
+
|
|
57
|
+
# For very short-running commands, check for output in a tight loop.
|
|
58
|
+
# The sleep below would at least halve the speed of quick #shell calls.
|
|
59
|
+
# This means really quick calls (e.g. `whoami`, `pwd`, etc) aren't
|
|
60
|
+
# delayed, but the CPU is only pegged for a fraction of a second on
|
|
61
|
+
# slower calls (e.g. `gem env`, `make`, etc).
|
|
62
|
+
1_000.times { break if stdout.ready_for_read? || stderr.ready_for_read? }
|
|
63
|
+
|
|
64
|
+
loop {
|
|
65
|
+
read_from stdout, @stdout do
|
|
66
|
+
print " #{%w[| / - \\][spinner_offset = ((spinner_offset + 1) % 4)]}\b\b" if should_spin
|
|
67
|
+
end
|
|
68
|
+
read_from stderr, @stderr, :stderr
|
|
69
|
+
|
|
70
|
+
if stdout.closed? && stderr.closed?
|
|
71
|
+
break
|
|
72
|
+
else
|
|
73
|
+
# We sleep here because otherwise babushka itself would peg the CPU
|
|
74
|
+
# while waiting for output from long-running shell commands.
|
|
75
|
+
sleep 0.05
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def read_from io, buf, log_as = nil
|
|
82
|
+
while !io.closed? && io.ready_for_read?
|
|
83
|
+
output = nil
|
|
84
|
+
# Only try reading up to a backspace if we're looking for progress output.
|
|
85
|
+
output = io.gets("\r") if @opts[:progress]
|
|
86
|
+
output = io.gets if output.nil?
|
|
87
|
+
|
|
88
|
+
if output.nil?
|
|
89
|
+
io.close
|
|
90
|
+
else
|
|
91
|
+
debug output.chomp, :log => @opts[:log], :as => log_as
|
|
92
|
+
buf << output
|
|
93
|
+
if @opts[:progress] && (@progress = output[@opts[:progress]])
|
|
94
|
+
print " #{@progress}#{"\b" * (@progress.length + 1)}"
|
|
95
|
+
end
|
|
96
|
+
yield if block_given?
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def popen_opts
|
|
102
|
+
{}.tap {|opts|
|
|
103
|
+
opts[:chdir] = @opts[:cd].p.to_s if @opts[:cd]
|
|
104
|
+
opts[:env] = @env if @env
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|