fig18 0.1.42-mswin32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,116 @@
1
+ require 'ostruct'
2
+ require 'set'
3
+
4
+ require 'fig/logging'
5
+
6
+ # This class copies files from the project directories in ~/.fighome to the
7
+ # user's working directory. It keeps track of which files have already been copied, and which
8
+ # package/versions they came from, and deletes files as necessary to ensure that
9
+ # we never have files from two different versions of the same package in the user's
10
+ # working directory.
11
+ class Retriever
12
+ def initialize(base_dir)
13
+ @base_dir = base_dir
14
+ @configs = {}
15
+ @fig_dir = File.join(@base_dir, '.fig')
16
+
17
+ file = File.join(@fig_dir, 'retrieve')
18
+ if File.exist?(file)
19
+ load(file)
20
+ end
21
+ end
22
+
23
+ def with_package_config(name, version)
24
+ if name and version
25
+ @config = @configs[name]
26
+ if @config && @config.version != version
27
+ @config.files.each do |relpath|
28
+ Fig::Logging.info "- [#{@config.name}/#{@config.version}] #{relpath}"
29
+ FileUtils.rm_f(File.join(@base_dir, relpath))
30
+ end
31
+ @config = nil
32
+ end
33
+ if not @config
34
+ @config = new_package_config(name, version)
35
+ @configs[name] = @config
36
+ end
37
+ else
38
+ @config = nil
39
+ end
40
+ yield
41
+ end
42
+
43
+ def retrieve(source, relpath)
44
+ copy(source, relpath)
45
+ end
46
+
47
+ def save
48
+ FileUtils.mkdir_p(@fig_dir)
49
+ File.open(File.join(@fig_dir, 'retrieve'), 'w') do |f|
50
+ @configs.each do |name,config|
51
+ config.files.each do |target|
52
+ f << target << '=' << config.name << '/' << config.version << "\n"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def load(file)
61
+ File.open(file).each_line do |line|
62
+ line = line.strip()
63
+ if line =~ /^(.+)=(.+)\/(.+)$/
64
+ target = $1
65
+ config_name = $2
66
+ config_version = $3
67
+ config = @configs[config_name]
68
+ if config
69
+ if config.version != config_version
70
+ raise 'version mismatch in .figretrieve'
71
+ end
72
+ else
73
+ config = new_package_config(config_name, config_version)
74
+ @configs[config_name] = config
75
+ end
76
+ config.files << target
77
+ else
78
+ raise "parse error in #{file}: #{line}"
79
+ end
80
+ end
81
+ end
82
+
83
+ def new_package_config(name, version)
84
+ config = OpenStruct.new
85
+ config.name = name
86
+ config.version = version
87
+ config.files = Set.new()
88
+ return config
89
+ end
90
+
91
+ def copy(source, relpath)
92
+ target = File.join(@base_dir, relpath)
93
+ if File.directory?(source)
94
+ FileUtils.mkdir_p(target)
95
+ Dir.foreach(source) do |child|
96
+ if child != '.' and child != '..'
97
+ source_file = File.join(source, child)
98
+ target_file = File.join(relpath, child)
99
+ Fig::Logging.debug "Copying #{source_file} to #{target_file}."
100
+ copy(source_file, target_file)
101
+ end
102
+ end
103
+ else
104
+ if !File.exist?(target) || File.mtime(source) > File.mtime(target)
105
+ if Fig::Logging.debug?
106
+ Fig::Logging.debug "Copying package [#{@config.name}/#{@config.version}] from #{source} to #{target}."
107
+ else
108
+ Fig::Logging.info "+ [#{@config.name}/#{@config.version}] #{relpath}"
109
+ end
110
+ FileUtils.mkdir_p(File.dirname(target))
111
+ FileUtils.cp(source, target, :preserve => true)
112
+ end
113
+ @config.files << relpath if @config
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,13 @@
1
+ require 'fig/userinputerror'
2
+
3
+ module Fig
4
+ class URLAccessError < UserInputError
5
+ attr_reader :urls, :package, :version
6
+
7
+ def initialize(urls, package, version)
8
+ @urls = urls
9
+ @package = package
10
+ @version = version
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module Fig
2
+ class UserInputError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ # Keeping Windows-specific implementation details here.
2
+
3
+ require 'erb'
4
+ require 'fileutils'
5
+
6
+ # I don't know how to set environment variables so that a sub-shell will
7
+ # be able to use them. Therefore, I'm punting, and creating a batch script
8
+ # on the fly to run a user supplied command.
9
+
10
+ module Fig
11
+ class Windows
12
+
13
+ BATCH_SCRIPT_TEMPLATE = <<EOF
14
+ @echo off
15
+ % ENV.each do |k,v|
16
+ set <%= k %>=<%= v %>
17
+ % end
18
+
19
+ cmd /C <%= command %>
20
+ EOF
21
+
22
+
23
+ def self.with_generated_batch_script(cmd)
24
+ command = cmd.join(' ')
25
+ template = ERB.new(BATCH_SCRIPT_TEMPLATE, 0, '%')
26
+ output = template.result(binding)
27
+ begin
28
+ tf = File.new('C:/tmp/fig_command.bat', 'w')
29
+ FileUtils.chmod(0755, tf.path)
30
+ File.open(tf.path, 'w') do |fh|
31
+ fh.puts output
32
+ end
33
+ tf.close
34
+ yield tf.path
35
+ ensure
36
+ # tf.delete
37
+ end
38
+ end
39
+
40
+ def self.shell_exec_windows(cmd)
41
+ with_generated_batch_script(cmd) do |f|
42
+ Kernel.exec(f)
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/fig.rb ADDED
@@ -0,0 +1,230 @@
1
+ require 'rubygems'
2
+ require 'net/ftp'
3
+ require 'log4r'
4
+
5
+ require 'fig/environment'
6
+ require 'fig/figrc'
7
+ require 'fig/logging'
8
+ require 'fig/options'
9
+ require 'fig/os'
10
+ require 'fig/package'
11
+ require 'fig/package/configuration'
12
+ require 'fig/package/publish'
13
+ require 'fig/parser'
14
+ require 'fig/repository'
15
+ require 'fig/retriever'
16
+ require 'fig/userinputerror'
17
+ require 'fig/windows'
18
+
19
+ module Fig
20
+ DEFAULT_FIG_FILE = 'package.fig'
21
+
22
+ def parse_descriptor(descriptor)
23
+ # todo should use treetop for these:
24
+ package_name = descriptor =~ %r< ^ ( [^:/]+ ) >x ? $1 : nil
25
+ config_name = descriptor =~ %r< : ( [^:/]+ ) >x ? $1 : nil
26
+ version_name = descriptor =~ %r< / ( [^:/]+ ) >x ? $1 : nil
27
+ return package_name, config_name, version_name
28
+ end
29
+
30
+ def run_fig(argv)
31
+ shell_command = nil
32
+ argv.each_with_index do |arg, i|
33
+ if arg == '--'
34
+ shell_command = argv[(i+1)..-1]
35
+ argv.slice!(i..-1)
36
+ break
37
+ end
38
+ end
39
+
40
+ options, argv, exit_value = parse_options(argv)
41
+ if not exit_value.nil?
42
+ return exit_value
43
+ end
44
+
45
+ Logging.initialize_pre_configuration(options[:log_level])
46
+
47
+ vars = {}
48
+ ENV.each {|key,value| vars[key]=value }
49
+
50
+ remote_url = nil
51
+ if options[:update] || options[:publish] || options[:update_if_missing] || options[:list_remote]
52
+ remote_url = ENV['FIG_REMOTE_URL']
53
+ if remote_url.nil?
54
+ $stderr.puts 'Please define the FIG_REMOTE_URL environment variable.'
55
+ return 1
56
+ end
57
+ end
58
+
59
+ configuration = FigRC.find(
60
+ options[:figrc],
61
+ remote_url,
62
+ options[:login],
63
+ options[:home],
64
+ options[:no_figrc]
65
+ )
66
+
67
+ Logging.initialize_post_configuration(options[:log_config] || configuration['log configuration'], options[:log_level])
68
+
69
+ remote_user = nil
70
+
71
+ os = OS.new(options[:login])
72
+ repos = Repository.new(
73
+ os,
74
+ File.expand_path(File.join(options[:home], 'repos')),
75
+ remote_url,
76
+ configuration,
77
+ remote_user,
78
+ options[:update],
79
+ options[:update_if_missing]
80
+ )
81
+ retriever = Retriever.new('.')
82
+ # Check to see if this is still happening with the new layers of abstraction.
83
+ at_exit { retriever.save }
84
+ env = Environment.new(os, repos, vars, retriever)
85
+
86
+ options[:modifiers].each do |modifier|
87
+ env.apply_config_statement(nil, modifier, nil)
88
+ end
89
+
90
+ input = nil
91
+ if options[:input] == :none
92
+ # ignore
93
+ elsif options[:input] == '-'
94
+ input = $stdin.read
95
+ elsif options[:input].nil?
96
+ input = os.read(DEFAULT_FIG_FILE) if os.exist?(DEFAULT_FIG_FILE)
97
+ else
98
+ if os.exist?(options[:input])
99
+ input = os.read(options[:input])
100
+ else
101
+ $stderr.puts %Q<File not found: "#{options[:input]}".>
102
+ return 1
103
+ end
104
+ end
105
+
106
+ options[:cleans].each do |descriptor|
107
+ package_name, version_name = descriptor.split('/')
108
+ repos.clean(package_name, version_name)
109
+ end
110
+ if options[:list]
111
+ repos.list_packages.sort.each do |item|
112
+ puts item
113
+ end
114
+ return 0
115
+ end
116
+
117
+ if options[:list_remote]
118
+ repos.list_remote_packages.sort.each do |item|
119
+ puts item
120
+ end
121
+ return 0
122
+ end
123
+
124
+ if not options[:list_configs].empty?
125
+ options[:list_configs].each do |descriptor|
126
+ package_name, version_name = descriptor.split('/')
127
+ repos.read_local_package(package_name, version_name).configs.each do |config|
128
+ puts config.name
129
+ end
130
+ end
131
+ return 0
132
+ end
133
+
134
+ if input
135
+ package = Parser.new(configuration).parse_package(nil, nil, '.', input)
136
+ direct_retrieves=[]
137
+ if options[:retrieve]
138
+ package.retrieves.each do |var, path|
139
+ if var =~ /^@([^\/]+)(.*)/
140
+ direct_retrieves << [$1, $2, path]
141
+ else
142
+ env.add_retrieve(var, path)
143
+ end
144
+ end
145
+ end
146
+ unless options[:publish] || options[:list] || options[:publish_local]
147
+ env.register_package(package)
148
+ env.apply_config(package, options[:config], nil)
149
+ direct_retrieves.each do |info|
150
+ env.direct_retrieve(info[0], info[1], info[2])
151
+ end
152
+ end
153
+ else
154
+ package = Package.new(nil, nil, '.', [])
155
+ end
156
+
157
+ if options[:publish] || options[:publish_local]
158
+ if !argv.empty?
159
+ $stderr.puts %Q<Unexpected arguments: #{argv.join(' ')}>
160
+ return 10
161
+ end
162
+ package_name, config_name, version_name = parse_descriptor(options[:publish] || options[:publish_local])
163
+ if package_name.nil? || version_name.nil?
164
+ $stderr.puts 'Please specify a package name and a version name.'
165
+ return 10
166
+ end
167
+ if not options[:modifiers].empty?
168
+ publish_statements = options[:resources] + options[:archives] + [Package::Configuration.new('default', options[:modifiers])]
169
+ publish_statements << Package::Publish.new('default','default')
170
+ elsif not package.statements.empty?
171
+ publish_statements = package.statements
172
+ else
173
+ $stderr.puts 'Nothing to publish.'
174
+ return 1
175
+ end
176
+ if options[:publish]
177
+ Logging.info "Checking status of #{package_name}/#{version_name}..."
178
+ if repos.list_remote_packages.include?("#{package_name}/#{version_name}")
179
+ Logging.info "#{package_name}/#{version_name} has already been published."
180
+ if not options[:force]
181
+ Logging.fatal 'Use the --force option if you really want to overwrite, or use --publish-local for testing.'
182
+ return 1
183
+ else
184
+ Logging.info 'Overwriting...'
185
+ end
186
+ end
187
+ end
188
+ Logging.info "Publishing #{package_name}/#{version_name}."
189
+ repos.publish_package(publish_statements, package_name, version_name, options[:publish_local])
190
+ elsif options[:echo]
191
+ puts env[options[:echo]]
192
+ elsif shell_command
193
+ argv.shift
194
+ env.execute_shell(shell_command) { |cmd| os.shell_exec cmd }
195
+ elsif argv[0]
196
+ package_name, config_name, version_name = parse_descriptor(argv.shift)
197
+ env.include_config(package, package_name, config_name, version_name, {}, nil)
198
+ env.execute_config(package, package_name, config_name, nil, argv) { |cmd| os.shell_exec cmd }
199
+ elsif input
200
+ env.execute_config(package, nil, options[:config], nil, argv) { |cmd| os.shell_exec cmd }
201
+ elsif !repos.updating?
202
+ $stderr.puts USAGE
203
+ $stderr.puts %q<Run "fig --help" for a full list of commands.>
204
+ end
205
+
206
+ return 0
207
+ end
208
+
209
+ def run_with_exception_handling(argv)
210
+ begin
211
+ return_code = run_fig(argv)
212
+ return return_code
213
+ rescue URLAccessError => exception
214
+ urls = exception.urls.join(', ')
215
+ $stderr.puts "Access to #{urls} in #{exception.package}/#{exception.version} not allowed."
216
+ return 1
217
+ rescue UserInputError => exception
218
+ # If there's no message, we assume that the cause has already been logged.
219
+ if not exception.message.nil?
220
+ $stderr.puts exception.to_s
221
+ end
222
+
223
+ return 1
224
+ rescue OptionParser::InvalidOption => exception
225
+ $stderr.puts exception.to_s
226
+ $stderr.puts USAGE
227
+ return 1
228
+ end
229
+ end
230
+ end