software_smithy 1.1 → 1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/smithy +140 -33
- data/etc/completion/smithy-completion.bash +106 -7
- data/etc/completion/zsh/_smithy +121 -27
- data/etc/templates/formula.rb.erb +11 -0
- data/etc/templates/web/machine_version_table.html.erb +0 -7
- data/etc/templates/web/version_table.html.erb +2 -2
- data/lib/smithy/config.rb +70 -13
- data/lib/smithy/description.rb +4 -4
- data/lib/smithy/download_cache.rb +120 -0
- data/lib/smithy/file_operations.rb +13 -6
- data/lib/smithy/formula.rb +203 -0
- data/lib/smithy/formula_command.rb +198 -0
- data/lib/smithy/helpers.rb +83 -37
- data/lib/smithy/module_file.rb +21 -10
- data/lib/smithy/package.rb +111 -86
- data/lib/smithy.rb +3 -0
- data/lib/smithy_version.rb +1 -1
- data/man/man1/smithy.1 +240 -53
- data/smithy.rdoc +107 -7
- metadata +84 -39
- data/README.rdoc +0 -114
- data/etc/smithyrc +0 -36
@@ -43,6 +43,9 @@ module Smithy
|
|
43
43
|
def notice_exist(file)
|
44
44
|
puts "exists ".rjust(12).color(:blue).bright + file
|
45
45
|
end
|
46
|
+
def notice_using(file)
|
47
|
+
puts "using ".rjust(12).color(:blue).bright + file
|
48
|
+
end
|
46
49
|
def notice_link(file1, file2)
|
47
50
|
puts "link ".rjust(12).bright + file1 + " -> " + file2
|
48
51
|
end
|
@@ -83,7 +86,7 @@ module Smithy
|
|
83
86
|
begin
|
84
87
|
FileUtils.send method, nil, new_group, f, options
|
85
88
|
rescue
|
86
|
-
raise "Could not set group \"#{new_group}\" on \"#{f}\""
|
89
|
+
# raise "Could not set group \"#{new_group}\" on \"#{f}\""
|
87
90
|
end
|
88
91
|
end
|
89
92
|
|
@@ -96,7 +99,7 @@ module Smithy
|
|
96
99
|
else
|
97
100
|
command = "chmod g+w #{f}"
|
98
101
|
unless options[:noop]
|
99
|
-
# Check to see if it's already group
|
102
|
+
# Check to see if it's already group writable
|
100
103
|
# convert the integer to a string in base 8
|
101
104
|
mode = File.stat(f).mode.to_s(8)
|
102
105
|
# check the group bit, convert back to integer
|
@@ -134,7 +137,7 @@ module Smithy
|
|
134
137
|
installed = false
|
135
138
|
|
136
139
|
force = options.try(:[],:force)
|
137
|
-
force = Smithy::Config.global.try(:[], :"force")
|
140
|
+
force = Smithy::Config.global.try(:[], :"force") unless force
|
138
141
|
options.reject!{|k,v| k==:force}
|
139
142
|
|
140
143
|
if File.exists?(dest) && !force
|
@@ -143,8 +146,8 @@ module Smithy
|
|
143
146
|
installed = true
|
144
147
|
else
|
145
148
|
notice_conflict dest
|
146
|
-
overwrite =
|
147
|
-
while overwrite
|
149
|
+
overwrite = "unknown"
|
150
|
+
while overwrite == "unknown" do
|
148
151
|
prompt = Readline.readline(" "*FILE_NOTICE_COLUMNS+"Overwrite? (enter \"h\" for help) [ynsdh] ")
|
149
152
|
case prompt.downcase
|
150
153
|
when "y"
|
@@ -208,6 +211,7 @@ module Smithy
|
|
208
211
|
end
|
209
212
|
|
210
213
|
def render_erb(args = {})
|
214
|
+
args[:options] = {} unless args[:options]
|
211
215
|
options = {:noop => false, :verbose => false}
|
212
216
|
options.merge!(args[:options])
|
213
217
|
erb_filename = args[:erb]
|
@@ -219,7 +223,10 @@ module Smithy
|
|
219
223
|
rendered_file = "#{File.dirname(dest)}/.#{File.basename(dest)}_#{Time.now.to_i}"
|
220
224
|
end
|
221
225
|
|
222
|
-
|
226
|
+
erb_string = args[:erb_string]
|
227
|
+
erb_string = File.read(erb_filename) if erb_string.blank?
|
228
|
+
|
229
|
+
erb = ERB.new(erb_string, nil, "<>")
|
223
230
|
File.open(rendered_file, "w+") do |f|
|
224
231
|
f.write erb.result(args[:binding])
|
225
232
|
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Smithy
|
2
|
+
class Formula
|
3
|
+
attr_accessor :formula_file, :name, :build_name, :prefix, :package, :module_setup
|
4
|
+
|
5
|
+
def self.formula_name
|
6
|
+
self.to_s.underscore.split("/").last.gsub /_formula$/, ""
|
7
|
+
end
|
8
|
+
def formula_name
|
9
|
+
self.class.formula_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(passed_package = nil)
|
13
|
+
@formula_file = __FILE__
|
14
|
+
raise "no install method implemented" unless self.respond_to?(:install)
|
15
|
+
raise "homepage must be specified" if homepage.blank?
|
16
|
+
raise "url must be specified" if url.blank?
|
17
|
+
if passed_package
|
18
|
+
set_package(passed_package)
|
19
|
+
else
|
20
|
+
# guess name and build_name
|
21
|
+
@name = self.formula_name
|
22
|
+
@build_name = operating_system
|
23
|
+
initialize_modules
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# setup module environment by purging and loading only what's needed
|
28
|
+
def initialize_modules
|
29
|
+
@modules = nil # re-evaluate modules block
|
30
|
+
@module_commands = nil # re-evaluate module_commands block
|
31
|
+
@module_setup = ""
|
32
|
+
raise "please specify modules OR modules_command, not both" if modules.present? && module_commands.present?
|
33
|
+
if ENV["MODULESHOME"]
|
34
|
+
@modulecmd = "modulecmd sh"
|
35
|
+
@modulecmd = "#{ENV["MODULESHOME"]}/bin/modulecmd sh" if File.exists?("#{ENV["MODULESHOME"]}/bin/modulecmd")
|
36
|
+
if modules.present?
|
37
|
+
@module_setup << `#{@module_setup} #{@modulecmd} purge 2>/dev/null` << " "
|
38
|
+
raise "modules must return a list of strings" unless modules.is_a? Array
|
39
|
+
@module_setup << `#{@module_setup} #{@modulecmd} load #{modules.join(" ")}` << " "
|
40
|
+
elsif module_commands.present?
|
41
|
+
module_commands.each do |command|
|
42
|
+
@module_setup << `#{@module_setup} #{@modulecmd} #{command}` << " "
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_package(p)
|
49
|
+
@package = p
|
50
|
+
@name = p.name
|
51
|
+
@version = p.version
|
52
|
+
@build_name = p.build_name
|
53
|
+
@prefix = p.prefix
|
54
|
+
initialize_modules
|
55
|
+
end
|
56
|
+
|
57
|
+
# DSL Methods
|
58
|
+
%w{ homepage url md5 sha1 sha2 sha256 modules module_commands depends_on modulefile }.each do |attr|
|
59
|
+
class_eval %Q{
|
60
|
+
def self.#{attr}(value = nil, &block)
|
61
|
+
@#{attr} = block_given? ? block : value unless @#{attr}
|
62
|
+
@#{attr}
|
63
|
+
end
|
64
|
+
def #{attr}
|
65
|
+
@#{attr} = self.class.#{attr}.is_a?(Proc) ? instance_eval(&self.class.#{attr}) : self.class.#{attr} unless @#{attr}
|
66
|
+
@#{attr}
|
67
|
+
end
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# DLS Version Method, can set a version or guess based on the filename
|
72
|
+
def self.version(value = nil)
|
73
|
+
unless @version
|
74
|
+
if value
|
75
|
+
@version = value
|
76
|
+
else
|
77
|
+
@version = url_filename_version_number(url) if url.present?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@version
|
81
|
+
end
|
82
|
+
|
83
|
+
def version
|
84
|
+
@version = self.class.version unless @version
|
85
|
+
@version
|
86
|
+
end
|
87
|
+
|
88
|
+
# DLS Version Method, can set a version or guess based on the filename
|
89
|
+
def self.disable_group_writable(value = true)
|
90
|
+
@disable_group_writable = true
|
91
|
+
@disable_group_writable
|
92
|
+
end
|
93
|
+
|
94
|
+
def group_writable?
|
95
|
+
! self.class.instance_variables.include?(:@disable_group_writable)
|
96
|
+
end
|
97
|
+
|
98
|
+
def run_install
|
99
|
+
check_dependencies if depends_on
|
100
|
+
install
|
101
|
+
notice_success "SUCCESS #{@prefix}"
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_modulefile
|
106
|
+
return false if modulefile.blank?
|
107
|
+
notice "Creating Modulefile for #{package.prefix}"
|
108
|
+
m = ModuleFile.new :package => package
|
109
|
+
FileUtils.mkdir_p(File.dirname(m.module_file))
|
110
|
+
FileOperations.render_erb(:erb_string => modulefile, :binding => m.get_binding, :destination => m.module_file)
|
111
|
+
FileOperations.make_group_writable(m.module_file)
|
112
|
+
FileOperations.set_group(m.module_file, package.group)
|
113
|
+
return true
|
114
|
+
end
|
115
|
+
|
116
|
+
def module_list
|
117
|
+
if ENV['MODULESHOME']
|
118
|
+
notice "module list"
|
119
|
+
Kernel.system @module_setup + "#{@modulecmd} list 2>&1"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def module_is_available?(mod)
|
124
|
+
return false unless @modulecmd
|
125
|
+
|
126
|
+
if `#{@modulecmd} avail #{mod} 2>&1` =~ /#{mod}/
|
127
|
+
true
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def module_environment_variable(mod, var)
|
134
|
+
module_display = `#{@modulecmd} display #{mod} 2>&1`
|
135
|
+
if module_display =~ /(\S+)\s+#{var}\s+(.*)$/
|
136
|
+
return $2
|
137
|
+
else
|
138
|
+
return ""
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def fail_command
|
143
|
+
$stdout.flush
|
144
|
+
$stderr.flush
|
145
|
+
raise <<-EOF.strip_heredoc
|
146
|
+
The last command exited with status: #{$?.exitstatus}
|
147
|
+
Formula: #{formula_file}
|
148
|
+
Build Directory: #{@package.source_directory}
|
149
|
+
EOF
|
150
|
+
end
|
151
|
+
|
152
|
+
def patch(content, *args)
|
153
|
+
patch_file_name = "patch.diff"
|
154
|
+
File.open(patch_file_name, "w+") do |f|
|
155
|
+
f.write(content)
|
156
|
+
end
|
157
|
+
args << "-p1" if args.empty?
|
158
|
+
patch_command = "patch #{args.join(' ')} <#{patch_file_name}"
|
159
|
+
notice patch_command
|
160
|
+
Kernel.system patch_command
|
161
|
+
fail_command if $?.exitstatus != 0
|
162
|
+
end
|
163
|
+
|
164
|
+
def system(*args)
|
165
|
+
notice args.join(' ')
|
166
|
+
if args.first == :nomodules
|
167
|
+
args.shift
|
168
|
+
Kernel.system args.join(' ')
|
169
|
+
else
|
170
|
+
Kernel.system @module_setup + args.join(' ')
|
171
|
+
end
|
172
|
+
fail_command if $?.exitstatus != 0
|
173
|
+
end
|
174
|
+
|
175
|
+
def check_dependencies
|
176
|
+
@depends_on = [depends_on] if depends_on.is_a? String
|
177
|
+
missing_packages = []
|
178
|
+
notice "Searching for dependencies"
|
179
|
+
depends_on.each do |package|
|
180
|
+
name, version, build = package.split('/')
|
181
|
+
path = Package.all(:name => name, :version => version, :build => build).first
|
182
|
+
if path
|
183
|
+
notice_using(path)
|
184
|
+
p = Package.new(:path => path)
|
185
|
+
new_name = p.name.underscore
|
186
|
+
class_eval %Q{
|
187
|
+
def #{new_name}
|
188
|
+
@#{new_name} = Package.new(:path => "#{path}") if @#{new_name}.nil?
|
189
|
+
@#{new_name}
|
190
|
+
end
|
191
|
+
}
|
192
|
+
else
|
193
|
+
missing_packages << package
|
194
|
+
#TODO build package instead?
|
195
|
+
end
|
196
|
+
end
|
197
|
+
unless missing_packages.empty?
|
198
|
+
raise "#{self.class} depends on: #{missing_packages.join(" ")}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end #class Formula
|
203
|
+
end #module Smithy
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module Smithy
|
2
|
+
# This class acts as a controller for formula subcommands
|
3
|
+
class FormulaCommand
|
4
|
+
# Return formula directories in order of precedence
|
5
|
+
# 1. formula directories specified on the command line
|
6
|
+
# 2. formulas located in ~/.smithy/formulas/
|
7
|
+
# 3. smithy's built in formulas
|
8
|
+
def self.formula_directories
|
9
|
+
unless @formula_directories
|
10
|
+
@formula_directories = [ File.join(ENV["HOME"], ".smithy/formulas") ]
|
11
|
+
if Smithy::Config.global[:"formula-directories"]
|
12
|
+
Smithy::Config.global[:"formula-directories"].reverse.each do |dir|
|
13
|
+
@formula_directories << dir
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@formula_directories << File.join(@@smithy_bin_root, "formulas")
|
17
|
+
end
|
18
|
+
@formula_directories
|
19
|
+
end
|
20
|
+
|
21
|
+
# Prepend a directory to the formula file paths
|
22
|
+
def self.prepend_formula_directory(dir)
|
23
|
+
@formula_directories.unshift(dir)
|
24
|
+
@formula_directories
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns an array containing the full paths
|
28
|
+
# to each file name in order of precedence
|
29
|
+
def self.formula_files
|
30
|
+
unless @formula_files
|
31
|
+
@formula_files = []
|
32
|
+
formula_directories.each do |dir|
|
33
|
+
@formula_files += Dir.glob(File.join(File.expand_path(dir),"*.rb")).sort
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@formula_files
|
37
|
+
end
|
38
|
+
|
39
|
+
# Format the full file paths to display only the name
|
40
|
+
def self.formula_names
|
41
|
+
@formula_names = formula_files.collect{|f| File.basename(f,"_formula.rb")}.uniq unless @formula_names
|
42
|
+
@formula_names
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the file path of a single formula file
|
46
|
+
def self.formula_file_path(name)
|
47
|
+
formula_files.select{|f| f =~ /\/#{name}_formula.rb/}.first
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return the file contents of a formula
|
51
|
+
def self.formula_contents(name)
|
52
|
+
raise "unkown formula '#{name}'" unless formula_file_path(name).present? && File.exists?(formula_file_path(name))
|
53
|
+
File.read(formula_file_path(name))
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.initialize_directories(options)
|
57
|
+
prepend_formula_directory(options[:d]) if options[:d]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.which_command(options,args)
|
61
|
+
initialize_directories(options)
|
62
|
+
formula_name = args.first.split("/").first
|
63
|
+
puts formula_file_path(formula_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.display_command(options,args)
|
67
|
+
initialize_directories(options)
|
68
|
+
formula_name = args.first.split("/").first
|
69
|
+
puts formula_contents(formula_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.list_command(options,args)
|
73
|
+
initialize_directories(options)
|
74
|
+
puts formula_names
|
75
|
+
end
|
76
|
+
|
77
|
+
# construct a new fomula object given a formula name or full name/version/build
|
78
|
+
def self.build_formula(package, formula_name = nil)
|
79
|
+
name, version, build = package.split("/")
|
80
|
+
formula_name = name if formula_name.blank?
|
81
|
+
raise "unknown formula #{formula_name}" unless formula_names.include?(formula_name)
|
82
|
+
|
83
|
+
require formula_file_path(formula_name)
|
84
|
+
f = "#{formula_name.underscore.camelize}Formula".constantize.new
|
85
|
+
# Set the actual formula file path, otherwise it's just formula.rb
|
86
|
+
f.formula_file = formula_file_path(formula_name)
|
87
|
+
|
88
|
+
version = f.version if version.blank?
|
89
|
+
build = operating_system if build.blank?
|
90
|
+
p = Package.new :path => [name, version, build].join("/"), :group_writable => f.group_writable?
|
91
|
+
f.set_package(p) if p.valid?
|
92
|
+
|
93
|
+
return f
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.install_command(options,args)
|
97
|
+
initialize_directories(options)
|
98
|
+
|
99
|
+
packages = args.dup
|
100
|
+
raise "You must supply at least one package to install" if packages.empty?
|
101
|
+
|
102
|
+
packages.each do |package|
|
103
|
+
f = build_formula(package, options[:"formula-name"])
|
104
|
+
f.package.create(:formula => true)
|
105
|
+
|
106
|
+
formula_prefix_contents = Dir["#{f.prefix}/*"]
|
107
|
+
unless formula_prefix_contents.empty?
|
108
|
+
# overwrite = "unknown"
|
109
|
+
# while overwrite == "unknown" do
|
110
|
+
# notice_conflict f.package.prefix
|
111
|
+
# prompt = Readline.readline(" "*FILE_NOTICE_COLUMNS+"Is not empty, delete contents? (enter \"h\" for help) [ynhq] ")
|
112
|
+
# case prompt.downcase
|
113
|
+
# when "y"
|
114
|
+
# overwrite = true
|
115
|
+
# when "n"
|
116
|
+
# overwrite = false
|
117
|
+
# when "h"
|
118
|
+
# indent = " "*FILE_NOTICE_COLUMNS
|
119
|
+
# puts indent+"y - yes, delete existing install"
|
120
|
+
# puts indent+"n - no, do not overwrite"
|
121
|
+
# puts indent+"h - help, show this help"
|
122
|
+
# puts indent+"q - exit now"
|
123
|
+
# when "q"
|
124
|
+
# raise "Abort new formula install"
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
# if overwrite
|
128
|
+
if options[:clean]
|
129
|
+
notice "cleaning #{f.prefix}"
|
130
|
+
formula_prefix_contents.each do |f|
|
131
|
+
FileUtils.rm_rf(f)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
# end
|
135
|
+
end
|
136
|
+
|
137
|
+
if f.url.eql?("none")
|
138
|
+
Dir.chdir File.join(f.package.prefix)
|
139
|
+
else
|
140
|
+
d = DownloadCache.new(f, options[:"formula-name"]).get
|
141
|
+
raise "Download failure" unless d
|
142
|
+
# f.package.extract(:archive => d, :overwrite => true)
|
143
|
+
f.package.extract(:archive => d)
|
144
|
+
Dir.chdir File.join(f.package.prefix, "source")
|
145
|
+
end
|
146
|
+
|
147
|
+
if f.run_install
|
148
|
+
f.package.create_valid_build_file
|
149
|
+
f.package.set_file_permissions_recursive
|
150
|
+
|
151
|
+
if f.modulefile.present?
|
152
|
+
f.create_modulefile
|
153
|
+
elsif options[:"modulefile"]
|
154
|
+
ModuleFile.new(:package => f.package).create
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end #packages.each
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.new_command(options,args)
|
161
|
+
@formula_name = options[:name]
|
162
|
+
@formula_name = url_filename_basename(args.first) unless options[:name]
|
163
|
+
@formula_name = @formula_name.camelize
|
164
|
+
@formula_url = args.first
|
165
|
+
@formula_homepage = options[:homepage]
|
166
|
+
@formula_homepage = "#{URI(@formula_url).scheme}://#{URI(@formula_url).host}/" unless options[:homepage]
|
167
|
+
|
168
|
+
destination = File.join(ENV["HOME"], ".smithy/formulas")
|
169
|
+
destination = Smithy::Config.global[:"formula-directories"].first if Smithy::Config.global[:"formula-directories"]
|
170
|
+
FileUtils::mkdir_p(destination)
|
171
|
+
|
172
|
+
destination = File.join(destination, "#{@formula_name.underscore}_formula.rb")
|
173
|
+
FileOperations.render_erb :binding => binding,
|
174
|
+
:erb => File.join(@@smithy_bin_root, "etc/templates/formula.rb.erb"),
|
175
|
+
:destination => destination
|
176
|
+
if Smithy::Config.global[:"file-group-name"]
|
177
|
+
FileOperations.set_group(destination, Smithy::Config.global[:"file-group-name"])
|
178
|
+
FileOperations.make_group_writable(destination)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.create_module_command(options,args)
|
183
|
+
packages = args.dup
|
184
|
+
raise "You must supply at least one package to install" if packages.empty?
|
185
|
+
|
186
|
+
packages.each do |package|
|
187
|
+
f = build_formula(package, options[:"formula-name"])
|
188
|
+
if f.modulefile.present?
|
189
|
+
f.create_modulefile
|
190
|
+
else
|
191
|
+
ModuleFile.new(:package => f.package).create
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end #FormulaCommand
|
197
|
+
|
198
|
+
end
|