software_smithy 1.1 → 1.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/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
|