reaper-man 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/CONTRIBUTING.md +25 -0
- data/LICENSE +13 -0
- data/README.md +55 -0
- data/bin/reaper-man +83 -0
- data/lib/reaper-man/command/package/add.rb +24 -0
- data/lib/reaper-man/command/package/remove.rb +24 -0
- data/lib/reaper-man/command/package.rb +12 -0
- data/lib/reaper-man/command/repository/generate.rb +28 -0
- data/lib/reaper-man/command/repository.rb +11 -0
- data/lib/reaper-man/command/sign.rb +21 -0
- data/lib/reaper-man/command.rb +11 -0
- data/lib/reaper-man/error.rb +20 -0
- data/lib/reaper-man/generator/apt.rb +117 -0
- data/lib/reaper-man/generator/rubygems.rb +91 -0
- data/lib/reaper-man/generator/yum.rb +10 -0
- data/lib/reaper-man/generator.rb +98 -0
- data/lib/reaper-man/package_list/deb.rb +150 -0
- data/lib/reaper-man/package_list/gem.rb +80 -0
- data/lib/reaper-man/package_list/rpm.rb +11 -0
- data/lib/reaper-man/package_list.rb +127 -0
- data/lib/reaper-man/signer/deb.rb +47 -0
- data/lib/reaper-man/signer/rubygems.rb +17 -0
- data/lib/reaper-man/signer.rb +62 -0
- data/lib/reaper-man/util-scripts/auto-debsigs +30 -0
- data/lib/reaper-man/utils/process.rb +114 -0
- data/lib/reaper-man/utils.rb +27 -0
- data/lib/reaper-man.rb +13 -0
- data/reaper-man.gemspec +17 -0
- metadata +78 -5
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
|
3
|
+
module ReaperMan
|
4
|
+
class PackageList
|
5
|
+
class Processor
|
6
|
+
# Package list process for debian packages
|
7
|
+
class Deb < Processor
|
8
|
+
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :origin
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :dist
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :component
|
15
|
+
# @return [Array<String>] architectures
|
16
|
+
attr_reader :all_map
|
17
|
+
# @return [String] prefix for package file location
|
18
|
+
attr_reader :package_root
|
19
|
+
# @return [String] namespace for packages
|
20
|
+
attr_reader :package_bucket
|
21
|
+
|
22
|
+
# default package root prefix
|
23
|
+
DEFAULT_ROOT = 'pool'
|
24
|
+
# default namespace for packages
|
25
|
+
DEFAULT_BUCKET = 'public'
|
26
|
+
# default architectures to define
|
27
|
+
DEFAULT_ALL_MAP = ['amd64', 'i386']
|
28
|
+
|
29
|
+
# Create new instance
|
30
|
+
#
|
31
|
+
# @param args [Hash]
|
32
|
+
# @option args [String] :origin
|
33
|
+
# @option args [String] :codename
|
34
|
+
# @option args [String] :component
|
35
|
+
# @option args [String] :package_root
|
36
|
+
# @option args [String] :package_bucket
|
37
|
+
# @option args [Array<String>] :all_map
|
38
|
+
def initialize(args={})
|
39
|
+
@origin = args[:origin].to_s
|
40
|
+
@dist = args[:codename].to_s
|
41
|
+
@component = args[:component].to_s
|
42
|
+
@package_root = args.fetch(:package_root, DEFAULT_ROOT)
|
43
|
+
@package_bucket = args.fetch(:package_bucket, DEFAULT_BUCKET)
|
44
|
+
if(dist.empty? || component.empty?)
|
45
|
+
raise 'Both `codename` and `component` must contain valid values'
|
46
|
+
end
|
47
|
+
@all_map = args.fetch(:all_map, DEFAULT_ALL_MAP)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add a package to the list
|
51
|
+
#
|
52
|
+
# @param conf [Hash]
|
53
|
+
# @param package [String] path to package
|
54
|
+
def add(hash, package)
|
55
|
+
info = extract_fields(package)
|
56
|
+
info.merge!(generate_checksums(package))
|
57
|
+
filenames = inject_package(hash, info, package)
|
58
|
+
filenames
|
59
|
+
end
|
60
|
+
|
61
|
+
# Remove package from the list
|
62
|
+
#
|
63
|
+
# @param conf [Hash] configuration hash
|
64
|
+
# @param package_name [String] name
|
65
|
+
# @param version [String]
|
66
|
+
def remove(hash, package_name, version, args={})
|
67
|
+
hash = hash.to_smash
|
68
|
+
arch = [args.fetch(:arch, all_map)].flatten.compact
|
69
|
+
deleted = false
|
70
|
+
arch.each do |arch_name|
|
71
|
+
arch_name = "binary-#{arch_name}"
|
72
|
+
if(hash.get(:apt, origin, dist, :components, component, arch_name, package_name))
|
73
|
+
if(version)
|
74
|
+
deleted = hash[:apt][origin][dist][:components][component][arch_name][package_name].delete(version)
|
75
|
+
else
|
76
|
+
deleted = hash[:apt][origin][dist][:components][component][arch_name].delete(package_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
!!deleted
|
81
|
+
end
|
82
|
+
|
83
|
+
# Extract package metadata
|
84
|
+
#
|
85
|
+
# @param package [String] path to package
|
86
|
+
# @return [Hash]
|
87
|
+
def extract_fields(package)
|
88
|
+
content = shellout("dpkg-deb -f '#{package}'")
|
89
|
+
Smash[content.stdout.scan(/([^\s][^:]+):\s+(([^\n]|\n\s)+)/).map{|a| a.slice(0,2)}]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Insert package information into package list
|
93
|
+
#
|
94
|
+
# @param hash [Hash] package list contents
|
95
|
+
# @param info [Hash] package information
|
96
|
+
# @param package [String] path to package file
|
97
|
+
# @return [Array<String>] package paths within package list contents
|
98
|
+
def inject_package(hash, info, package)
|
99
|
+
arch = info['Architecture']
|
100
|
+
arch = arch == 'all' ? all_map : [arch]
|
101
|
+
arch.map do |arch|
|
102
|
+
package_file_name = File.join(
|
103
|
+
package_root, package_bucket, origin,
|
104
|
+
dist, component, "binary-#{arch}",
|
105
|
+
File.basename(package)
|
106
|
+
)
|
107
|
+
hash.deep_merge!(
|
108
|
+
'apt' => {
|
109
|
+
origin => {
|
110
|
+
dist => {
|
111
|
+
'components' => {
|
112
|
+
component => {
|
113
|
+
"binary-#{arch}" => {
|
114
|
+
info['Package'] => {
|
115
|
+
info['Version'] => info.merge!(
|
116
|
+
'Filename' => package_file_name,
|
117
|
+
'Size' => File.size(package)
|
118
|
+
)
|
119
|
+
}
|
120
|
+
},
|
121
|
+
"binary-i386" => {
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
)
|
129
|
+
package_file_name
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Generate required checksums for given package
|
134
|
+
#
|
135
|
+
# @param package [String] path to package file
|
136
|
+
# @return [Hash] checksums
|
137
|
+
def generate_checksums(package)
|
138
|
+
File.open(package, 'r') do |pkg|
|
139
|
+
{
|
140
|
+
'MD5sum' => checksum(pkg.rewind && pkg, :md5),
|
141
|
+
'SHA1' => checksum(pkg.rewind && pkg, :sha1),
|
142
|
+
'SHA256' => checksum(pkg.rewind && pkg, :sha256)
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
require 'rubygems/package'
|
3
|
+
|
4
|
+
module ReaperMan
|
5
|
+
class PackageList
|
6
|
+
class Processor
|
7
|
+
class Gem < Processor
|
8
|
+
|
9
|
+
# Add a package to the list
|
10
|
+
#
|
11
|
+
# @param conf [Hash]
|
12
|
+
# @param package [String] path to package
|
13
|
+
def add(hash, package)
|
14
|
+
info = extract_fields(package)
|
15
|
+
filenames = inject_package(hash, info, package)
|
16
|
+
filenames
|
17
|
+
end
|
18
|
+
|
19
|
+
# Remove package from the list
|
20
|
+
#
|
21
|
+
# @param conf [Hash] configuration hash
|
22
|
+
# @param package_name [String] name
|
23
|
+
# @param version [String]
|
24
|
+
def remove(hash, package_name, version, args={})
|
25
|
+
deleted = false
|
26
|
+
if(hash['rubygems'][package_name])
|
27
|
+
if(version)
|
28
|
+
deleted = hash['rubygems'][package_name].delete(version)
|
29
|
+
else
|
30
|
+
deleted = hash['rubygems'].delete(package_name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
!!deleted
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extract package metadata
|
37
|
+
#
|
38
|
+
# @param package [String] path to package
|
39
|
+
# @return [Hash]
|
40
|
+
def extract_fields(package)
|
41
|
+
spec = ::Gem::Package.open(File.open(package)){|pack| pack.metadata}
|
42
|
+
fields = Smash[
|
43
|
+
spec.to_yaml_properties.map do |var_name|
|
44
|
+
[var_name.to_s.tr('@', ''), spec.instance_variable_get(var_name)]
|
45
|
+
end
|
46
|
+
]
|
47
|
+
fields['dependencies'] = fields['dependencies'].map do |dep|
|
48
|
+
[dep.name, dep.requirement.to_s]
|
49
|
+
end
|
50
|
+
fields
|
51
|
+
end
|
52
|
+
|
53
|
+
# Insert package information into package list
|
54
|
+
#
|
55
|
+
# @param hash [Hash] package list contents
|
56
|
+
# @param info [Hash] package information
|
57
|
+
# @param package [String] path to package file
|
58
|
+
# @return [Array<String>] package paths within package list contents
|
59
|
+
def inject_package(hash, info, package)
|
60
|
+
package_path = File.join(
|
61
|
+
'rubygems', 'gems', "#{info['name']}-#{info['version']}.gem"
|
62
|
+
)
|
63
|
+
classification = info['version'].prerelease? ? 'prerelease' : 'release'
|
64
|
+
info['version'] = info['version'].version
|
65
|
+
hash.deep_merge!(
|
66
|
+
'rubygem' => {
|
67
|
+
classification => {
|
68
|
+
info['name'] => {
|
69
|
+
info['version'].to_s => info.merge('package_path' => package_path)
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
)
|
74
|
+
package_path
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'reaper-man'
|
3
|
+
|
4
|
+
module ReaperMan
|
5
|
+
# Package list for repository
|
6
|
+
class PackageList
|
7
|
+
# Package list modification processor
|
8
|
+
class Processor
|
9
|
+
autoload :Rpm, 'reaper-man/package_list/rpm'
|
10
|
+
autoload :Deb, 'reaper-man/package_list/deb'
|
11
|
+
autoload :Gem, 'reaper-man/package_list/gem'
|
12
|
+
|
13
|
+
include Utils::Process
|
14
|
+
include Utils::Checksum
|
15
|
+
|
16
|
+
# Add a package to the list
|
17
|
+
#
|
18
|
+
# @param conf [Hash]
|
19
|
+
# @param package [String] path to package
|
20
|
+
def add(conf, package)
|
21
|
+
raise NoMethodError.new 'Not implemented'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Remove package from the list
|
25
|
+
#
|
26
|
+
# @param conf [Hash] configuration hash
|
27
|
+
# @param package_name [String] name
|
28
|
+
# @param version [String]
|
29
|
+
def remove(conf, package_name, version=nil)
|
30
|
+
raise NoMethodError.new 'Not implemented'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [String] path to list file
|
35
|
+
attr_reader :path
|
36
|
+
# @return [Hash] configuration
|
37
|
+
attr_reader :options
|
38
|
+
# @return [Time] package list mtime
|
39
|
+
attr_reader :init_mtime
|
40
|
+
# @return [Hash] content of package list
|
41
|
+
attr_reader :content
|
42
|
+
|
43
|
+
# Create new instance
|
44
|
+
#
|
45
|
+
# @param path [String] path to package list
|
46
|
+
# @param args [Hash] configuration
|
47
|
+
def initialize(path, args={})
|
48
|
+
@path = path
|
49
|
+
@options = args.dup
|
50
|
+
@content = Smash.new
|
51
|
+
init_list!
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add package to package list file
|
55
|
+
#
|
56
|
+
# @param package [Array<String>] path to package file
|
57
|
+
def add_package(package)
|
58
|
+
[package_handler(File.extname(package).tr('.', '')).add(content, package)].flatten.compact
|
59
|
+
end
|
60
|
+
|
61
|
+
# Remove package from the package list file
|
62
|
+
#
|
63
|
+
# @param package [String] name of package
|
64
|
+
# @param version [String] version of file
|
65
|
+
def remove_package(package, version=nil)
|
66
|
+
ext = File.extname(package).tr('.', '')
|
67
|
+
if(ext.empty?)
|
68
|
+
ext = %w(deb) # rpm)
|
69
|
+
else
|
70
|
+
ext = [ext]
|
71
|
+
end
|
72
|
+
ext.each do |ext_name|
|
73
|
+
package_handler(ext_name).remove(content, package, version)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [String] serialized content
|
78
|
+
def serialize
|
79
|
+
MultiJson.dump(content)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Write contents to package list file
|
83
|
+
#
|
84
|
+
# @return [Integer] number of bytes written
|
85
|
+
def write!
|
86
|
+
new_file = !File.exists?(path)
|
87
|
+
File.open(path, File::CREAT|File::RDWR) do |file|
|
88
|
+
file.flock(File::LOCK_EX)
|
89
|
+
if(!new_file && init_mtime != file.mtime)
|
90
|
+
file.rewind
|
91
|
+
content.deep_merge!(
|
92
|
+
MultiJson.load(
|
93
|
+
file.read
|
94
|
+
)
|
95
|
+
)
|
96
|
+
file.rewind
|
97
|
+
end
|
98
|
+
pos = file.write MultiJson.dump(content, :pretty => true)
|
99
|
+
file.truncate(pos)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# @return [Processor] processor for give package type
|
106
|
+
def package_handler(pkg_ext)
|
107
|
+
Processor.const_get(pkg_ext.capitalize).new(options)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Initialize the package list file
|
111
|
+
#
|
112
|
+
# @return [Hash] loaded file contents
|
113
|
+
def init_list!
|
114
|
+
write! unless File.exist?(path)
|
115
|
+
@init_mtime = File.mtime(path)
|
116
|
+
content.deep_merge!(
|
117
|
+
MultiJson.load(
|
118
|
+
File.open(path, 'r') do |file|
|
119
|
+
file.flock(File::LOCK_SH)
|
120
|
+
file.read
|
121
|
+
end
|
122
|
+
)
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
|
3
|
+
module ReaperMan
|
4
|
+
class Signer
|
5
|
+
# Signing methods for deb files
|
6
|
+
module Deb
|
7
|
+
|
8
|
+
# command to use for file signing
|
9
|
+
SIGN_COMMAND = File.join(
|
10
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..')),
|
11
|
+
'util-scripts/auto-debsigs'
|
12
|
+
)
|
13
|
+
|
14
|
+
# Sign given files
|
15
|
+
#
|
16
|
+
# @param pkgs [String] list of file paths
|
17
|
+
# @return [TrueClass]
|
18
|
+
def package(*pkgs)
|
19
|
+
pkgs = valid_packages(*pkgs)
|
20
|
+
pkgs.each_slice(sign_chunk_size) do |pkgs|
|
21
|
+
if(key_password)
|
22
|
+
shellout(
|
23
|
+
"#{SIGN_COMMAND} #{sign_type} #{key_id} #{pkgs.join(' ')}",
|
24
|
+
:environment => {
|
25
|
+
'REAPER_KEY_PASSWORD' => key_password
|
26
|
+
}
|
27
|
+
)
|
28
|
+
else
|
29
|
+
shellout(%w(debsigs --sign="#{sign_type}" --default-key="#{key_id}" #{pkgs.join(' ')}))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Filter only valid paths for signing (.deb extensions)
|
36
|
+
#
|
37
|
+
# @param pkgs [String] list of file paths
|
38
|
+
# @return [Array<String>]
|
39
|
+
def valid_packages(*pkgs)
|
40
|
+
pkgs.find_all do |pkg|
|
41
|
+
File.extname(pkg) == '.deb'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
|
3
|
+
module ReaperMan
|
4
|
+
# File signer
|
5
|
+
class Signer
|
6
|
+
|
7
|
+
autoload :Rpm, 'reaper-man/signer/rpm'
|
8
|
+
autoload :Deb, 'reaper-man/signer/deb'
|
9
|
+
autoload :Rubygems, 'reaper-man/signer/rubygems'
|
10
|
+
|
11
|
+
include Utils::Process
|
12
|
+
|
13
|
+
attr_reader :key_id
|
14
|
+
attr_reader :sign_chunk_size
|
15
|
+
attr_reader :sign_type
|
16
|
+
attr_reader :package_system
|
17
|
+
attr_reader :key_password
|
18
|
+
|
19
|
+
# Create new instance
|
20
|
+
#
|
21
|
+
# @param args [Hash]
|
22
|
+
# @option args [String] :signing_key
|
23
|
+
# @option args [String] :signing_chunk_size (defaults to 1)
|
24
|
+
# @option args [String] :signing_type (defaults to 'origin')
|
25
|
+
# @option args [String] :key_password (defaults to `ENV['REAPER_KEY_PASSWORD']`)
|
26
|
+
# @option args [String] :package_system
|
27
|
+
def initialize(args={})
|
28
|
+
args = args.to_smash
|
29
|
+
@key_id = args[:signing_key]
|
30
|
+
@sign_chunk_size = args.fetch(:signing_chunk_size, 1)
|
31
|
+
@sign_type = args.fetch(:signing_type, 'origin')
|
32
|
+
@key_password = args.fetch(:key_password, ENV['REAPER_KEY_PASSWORD'])
|
33
|
+
@package_system = args[:package_system]
|
34
|
+
case package_system.to_sym
|
35
|
+
when :deb, :apt
|
36
|
+
extend Deb
|
37
|
+
when :rpm, :yum
|
38
|
+
extend Rpm
|
39
|
+
when :gem, :rubygems
|
40
|
+
extend Rubygems
|
41
|
+
else
|
42
|
+
raise TypeError.new "Unknown packaging type requested (#{package_system})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sign the file
|
47
|
+
#
|
48
|
+
# @param src [String] path to source file
|
49
|
+
# @param dst [String] path for destination file
|
50
|
+
# @return [String] destination file path
|
51
|
+
def file(src, dst=nil)
|
52
|
+
opts = ['--detach-sign', '--armor']
|
53
|
+
dst ||= src.sub(/#{Regexp.escape(File.extname(src))}$/, '.gpg')
|
54
|
+
opts << "--output '#{dst}'"
|
55
|
+
cmd = (['gpg'] + opts + [src]).join(' ')
|
56
|
+
shellout!(cmd)
|
57
|
+
dst
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/expect -f
|
2
|
+
|
3
|
+
log_user 0
|
4
|
+
|
5
|
+
set timeout 3
|
6
|
+
|
7
|
+
set signtype [lindex $argv 0]
|
8
|
+
set signkey [lindex $argv 1]
|
9
|
+
set package [join [lrange $argv 2 [llength $argv]] " "]
|
10
|
+
|
11
|
+
spawn /bin/bash
|
12
|
+
expect {
|
13
|
+
-re ".*\$ *$" {
|
14
|
+
send "/usr/bin/debsigs --sign=\"$signtype\" --default-key=\"$signkey\" $package\r"
|
15
|
+
}
|
16
|
+
timeout { exit 1 }
|
17
|
+
}
|
18
|
+
expect {
|
19
|
+
-re {Enter passphrase: *} {
|
20
|
+
send "$env(REAPER_KEY_PASSWORD)\r"
|
21
|
+
}
|
22
|
+
timeout { exit 2 }
|
23
|
+
}
|
24
|
+
expect {
|
25
|
+
-re {\$ *} {
|
26
|
+
send "exit $?\rexit $?\r"
|
27
|
+
}
|
28
|
+
timeout { exit 3 }
|
29
|
+
}
|
30
|
+
expect eof
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'reaper-man'
|
2
|
+
require 'childprocess'
|
3
|
+
require 'shellwords'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
module ReaperMan
|
7
|
+
module Utils
|
8
|
+
# Shellout helper
|
9
|
+
module Process
|
10
|
+
|
11
|
+
# NOTE: This is extracted from the elecksee gem and some
|
12
|
+
# features removed that are not required here. Should be
|
13
|
+
# wrapped up into standalone gem so it's more reusable.
|
14
|
+
|
15
|
+
class CommandFailed < StandardError
|
16
|
+
attr_accessor :original, :result
|
17
|
+
def initialize(orig, result=nil)
|
18
|
+
@original = orig
|
19
|
+
@result = result
|
20
|
+
super(orig.to_s)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Timeout < CommandFailed
|
25
|
+
end
|
26
|
+
|
27
|
+
class CommandResult
|
28
|
+
attr_reader :original, :stdout, :stderr
|
29
|
+
def initialize(result)
|
30
|
+
@original = result
|
31
|
+
if(result.class.ancestors.map(&:to_s).include?('ChildProcess::AbstractProcess'))
|
32
|
+
extract_childprocess
|
33
|
+
elsif(result.class.to_s == 'Mixlib::ShellOut')
|
34
|
+
extract_shellout
|
35
|
+
else
|
36
|
+
raise TypeError.new("Unknown process result type received: #{result.class}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_childprocess
|
41
|
+
original.io.stdout.rewind
|
42
|
+
original.io.stderr.rewind
|
43
|
+
@stdout = original.io.stdout.read
|
44
|
+
@stderr = original.io.stderr.read
|
45
|
+
original.io.stdout.delete
|
46
|
+
original.io.stderr.delete
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_shellout
|
50
|
+
@stdout = original.stdout
|
51
|
+
@stderr = original.stderr
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Simple helper to shell out
|
56
|
+
def shellout(cmd, args={})
|
57
|
+
result = nil
|
58
|
+
if(defined?(Mixlib))
|
59
|
+
cmd_type = :mixlib_shellout
|
60
|
+
else
|
61
|
+
cmd_type = :childprocess
|
62
|
+
end
|
63
|
+
com_block = nil
|
64
|
+
case cmd_type
|
65
|
+
when :childprocess
|
66
|
+
com_block = lambda{ child_process_command(cmd, args) }
|
67
|
+
when :mixlib_shellout
|
68
|
+
require 'mixlib/shellout'
|
69
|
+
com_block = lambda{ mixlib_shellout_command(cmd, args) }
|
70
|
+
else
|
71
|
+
raise ArgumentError.new("Unknown shellout helper provided: #{cmd_type}")
|
72
|
+
end
|
73
|
+
result = defined?(Bundler) ? Bundler.with_clean_env{ com_block.call } : com_block.call
|
74
|
+
result == false ? false : CommandResult.new(result)
|
75
|
+
end
|
76
|
+
|
77
|
+
def child_process_command(cmd, args)
|
78
|
+
s_out = Tempfile.new('stdout')
|
79
|
+
s_err = Tempfile.new('stderr')
|
80
|
+
s_out.sync
|
81
|
+
s_err.sync
|
82
|
+
c_proc = ChildProcess.build(*Shellwords.split(cmd))
|
83
|
+
c_proc.environment.merge(args.fetch(:environment, {}))
|
84
|
+
c_proc.io.stdout = s_out
|
85
|
+
c_proc.io.stderr = s_err
|
86
|
+
c_proc.start
|
87
|
+
begin
|
88
|
+
c_proc.poll_for_exit(args[:timeout] || 10)
|
89
|
+
rescue ChildProcess::TimeoutError
|
90
|
+
c_proc.stop
|
91
|
+
ensure
|
92
|
+
raise CommandFailed.new("Command failed: #{cmd}", CommandResult.new(c_proc)) if c_proc.crashed?
|
93
|
+
end
|
94
|
+
c_proc
|
95
|
+
end
|
96
|
+
|
97
|
+
def mixlib_shellout_command(cmd, args)
|
98
|
+
shlout = nil
|
99
|
+
begin
|
100
|
+
shlout = Mixlib::ShellOut.new(cmd,
|
101
|
+
:timeout => args[:timeout] || 10,
|
102
|
+
:environment => args.fetch(:environment, {})
|
103
|
+
)
|
104
|
+
shlout.run_command
|
105
|
+
shlout.error!
|
106
|
+
shlout
|
107
|
+
rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout => e
|
108
|
+
raise CommandFailed.new(e, CommandResult.new(shlout))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'digest/sha2'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
require 'reaper-man'
|
6
|
+
|
7
|
+
module ReaperMan
|
8
|
+
# Helper utilities
|
9
|
+
module Utils
|
10
|
+
|
11
|
+
autoload :Process, 'reaper-man/utils/process'
|
12
|
+
|
13
|
+
# Checksum helper
|
14
|
+
module Checksum
|
15
|
+
|
16
|
+
def checksum(io, type)
|
17
|
+
digest = Digest.const_get(type.to_s.upcase).new
|
18
|
+
while(data = io.read(2048))
|
19
|
+
digest << data
|
20
|
+
end
|
21
|
+
digest.hexdigest
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/reaper-man.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module ReaperMan
|
4
|
+
autoload :Cli, 'reaper-man/cli'
|
5
|
+
autoload :Command, 'reaper-man/command'
|
6
|
+
autoload :Config, 'reaper-man/config'
|
7
|
+
autoload :Error, 'reaper-man/error'
|
8
|
+
autoload :Generator, 'reaper-man/generator'
|
9
|
+
autoload :PackageList, 'reaper-man/package_list'
|
10
|
+
autoload :Signer, 'reaper-man/signer'
|
11
|
+
autoload :Version, 'reaper-man/version'
|
12
|
+
autoload :Utils, 'reaper-man/utils'
|
13
|
+
end
|