Module: Fig

Defined in:
lib/fig.rb,
lib/fig/os.rb,
lib/fig/figrc.rb,
lib/fig/parser.rb,
lib/fig/logging.rb,
lib/fig/options.rb,
lib/fig/package.rb,
lib/fig/windows.rb,
lib/fig/repository.rb,
lib/fig/environment.rb,
lib/fig/package/set.rb,
lib/fig/networkerror.rb,
lib/fig/package/path.rb,
lib/fig/packageerror.rb,
lib/fig/notfounderror.rb,
lib/fig/urlaccesserror.rb,
lib/fig/userinputerror.rb,
lib/fig/configfileerror.rb,
lib/fig/package/archive.rb,
lib/fig/package/command.rb,
lib/fig/package/include.rb,
lib/fig/package/install.rb,
lib/fig/package/publish.rb,
lib/fig/repositoryerror.rb,
lib/fig/log4rconfigerror.rb,
lib/fig/package/override.rb,
lib/fig/package/resource.rb,
lib/fig/package/retrieve.rb,
lib/fig/package/statement.rb,
lib/fig/package/configuration.rb,
lib/fig/applicationconfiguration.rb

Overview

I don’t know how to set environment variables so that a sub-shell will be able to use them. Therefore, I’m punting, and creating a batch script on the fly to run a user supplied command.

Defined Under Namespace

Modules: Logging Classes: ApplicationConfiguration, ConfigFileError, Environment, FigRC, Log4rConfigError, NetworkError, NotFoundError, OS, Package, PackageError, Parser, Repository, RepositoryError, URLAccessError, UserInputError, Windows

Constant Summary collapse

DEFAULT_FIG_FILE =
'package.fig'
USAGE =
<<EOF

Usage: fig [--update | --update-if-missing] [--file <path>] [--set <VAR=value>] [--config <config>] [--login] [--log-level <level>] [--log-config <path>] [--figrc <path>] [--no-figrc] [-- <command>]
       fig [--publish | --publish-local] [--append <VAR=val>] [--resource <fullpath>] [--include <package name/version>] [--force] [--no-file] [--archive <path>] [--file] [--set <VAR=value>] [--config <config>] [--login] [--log-level <level>] [--log-config <path>] [--figrc <path>] [--no-figrc]
       fig [--set <VAR=value>] [--get <VAR> | --list-configs <package name>/<version> | --list | --list-remote | --clean <package name/version> | --version] [--log-level <level>] [--log-config <path>] [--figrc <path>] [--no-figrc] [-- <command>]
       fig [<package name>/<version>] [--log-level <level>] [--log-config <path>] [--figrc <path>] [--no-figrc]

Relevant env vars: FIG_REMOTE_URL (required), FIG_HOME (path to local repository cache, defaults
to $HOME/.fighome).

EOF
LOG_LEVELS =
%w[ off fatal error warn info debug all ]
LOG_ALIASES =
{ 'warning' => 'warn' }

Instance Method Summary collapse

Instance Method Details

#display_configs_in_local_packages_list(options, repository) ⇒ Object



91
92
93
94
95
96
97
98
# File 'lib/fig.rb', line 91

def display_configs_in_local_packages_list(options, repository)
  options[:list_configs].each do |descriptor|
    package_name, version_name = descriptor.split('/')
    repository.read_local_package(package_name, version_name).configs.each do |config|
      puts config.name
    end
  end
end

#display_package_list(repository) ⇒ Object



79
80
81
82
83
# File 'lib/fig.rb', line 79

def display_package_list(repository)
  repository.list_packages.sort.each do |item|
    puts item
  end
end

#display_remote_package_list(repository) ⇒ Object



85
86
87
88
89
# File 'lib/fig.rb', line 85

def display_remote_package_list(repository)
  repository.list_remote_packages.sort.each do |item|
    puts item
  end
end

#error_has_message?(error) ⇒ Boolean

Returns:

  • (Boolean)


284
285
286
287
# File 'lib/fig.rb', line 284

def error_has_message?(error)
  class_name = error.class.name
  return error.message != class_name
end

#initialize_remote_url(options) ⇒ Object



52
53
54
55
56
57
58
59
60
61
# File 'lib/fig.rb', line 52

def initialize_remote_url(options)
  if options[:update] || options[:publish] || options[:update_if_missing] || options[:list_remote]
    if ENV['FIG_REMOTE_URL'].nil?
      raise UserInputError.new 'Please define the FIG_REMOTE_URL environment variable.'
    end
    return ENV['FIG_REMOTE_URL']
  end

  return nil
end

#initialize_shell_command(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/fig.rb', line 39

def initialize_shell_command(argv)
  shell_command = nil
  argv.each_with_index do |arg, i|
    if arg == '--'
      shell_command = argv[(i+1)..-1]
      argv.slice!(i..-1)
      break
    end
  end

  return shell_command
end

#load_package_config_file_contents(options) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fig.rb', line 63

def load_package_config_file_contents(options)
  package_config_file = options[:package_config_file]

  if package_config_file == :none
    return nil
  elsif package_config_file == '-'
    return $stdin.read
  elsif package_config_file.nil?
    if File.exist?(DEFAULT_FIG_FILE)
      return File.read(DEFAULT_FIG_FILE)
    end
  else
    return read_in_package_config_file(options)
  end
end

#log_error_message(error) ⇒ Object



256
257
258
259
260
261
# File 'lib/fig.rb', line 256

def log_error_message(error)
  # If there's no message, we assume that the cause has already been logged.
  if error_has_message?(error)
    Logging.fatal error.to_s
  end
end

#parse_descriptor(descriptor) ⇒ Object



23
24
25
26
27
28
29
# File 'lib/fig.rb', line 23

def parse_descriptor(descriptor)
  # todo should use treetop for these:
  package_name = descriptor =~ %r< ^ ( [^:/]+ ) >x ? $1 : nil
  config_name  = descriptor =~ %r< : ( [^:/]+ ) >x ? $1 : nil
  version_name = descriptor =~ %r< / ( [^:/]+ ) >x ? $1 : nil
  return package_name, config_name, version_name
end

#parse_options(argv) ⇒ Object

Returns hash of option values, the remainders of argv, and an exit code if full program processing occured in this method, otherwise nil.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/fig/options.rb', line 38

def parse_options(argv)
  options = {}

  parser = OptionParser.new do |opts|
    opts.banner = USAGE
    opts.on('-?', '-h','--help','display this help text') do
      puts opts.help
      puts "\n    --  end of fig options; everything following is a command to run in the fig environment.\n\n"
      return nil, nil, 0
    end

    opts.on('-v', '--version', 'Print fig version') do
      line = nil

      begin
        File.open(
          "#{File.expand_path(File.dirname(__FILE__) + '/../../VERSION')}"
        ) do |file|
          line = file.gets
        end
      rescue
        $stderr.puts 'Could not retrieve version number. Something has mucked with your gem install.'
        return nil, nil, 1
      end

      if line !~ /\d+\.\d+\.\d+/
        $stderr.puts %Q<"#{line}" does not look like a version number. Something has mucked with your gem install.>
        return nil, nil, 1
      end

      puts File.basename($0) + ' v' + line

      return nil, nil, 0
    end

    options[:non_command_package_statements] = []
    opts.on(
      '-p',
      '--append VAR=VAL',
      'append (actually, prepend) VAL to environment var VAR, delimited by separator'
    ) do |var_val|
      var, val = var_val.split('=')
      options[:non_command_package_statements] << Package::Path.new(var, val)
    end

    options[:archives] = []
    opts.on(
      '--archive FULLPATH',
      'include FULLPATH archive in package (when using --publish)'
    ) do |path|
      options[:archives] << Package::Archive.new(path)
    end

    options[:cleans] = []
    opts.on('--clean PKG', 'remove package from $FIG_HOME') do |descriptor|
      options[:cleans] << descriptor
    end

    options[:config] = 'default'
    opts.on(
      '-c',
      '--config CFG',
      %q<apply configuration CFG, default is 'default'>
    ) do |config|
      options[:config] = config
    end

    options[:package_config_file] = nil
    opts.on(
      '--file FILE',
      %q<read fig file FILE. Use '-' for stdin. See also --no-file>
    ) do |path|
      options[:package_config_file] = path
    end

    options[:force] = nil
    opts.on(
      '--force',
      'force-overwrite existing version of a package to the remote repo'
    ) do |force|
      options[:force] = force
    end

    options[:get] = nil
    opts.on(
      '-g',
      '--get VAR',
      'print value of environment variable VAR'
    ) do |get|
      options[:get] = get
    end

    opts.on(
      '-i',
      '--include PKG',
      'include PKG (with any variable prepends) in environment'
    ) do |descriptor|
      package_name, config_name, version_name = parse_descriptor(descriptor)
      options[:non_command_package_statements] << Package::Include.new(package_name, config_name, version_name, {})
    end

    options[:list] = false
    opts.on('--list', 'list packages in $FIG_HOME') do
      options[:list] = true
    end

    options[:list_configs] = []
    opts.on(
      '--list-configs PKG', 'list configurations in package'
    ) do |descriptor|
      options[:list_configs] << descriptor
    end

    options[:list_remote] = false
    opts.on('--list-remote', 'list packages in remote repo') do
      options[:list_remote] = true
    end

    options[:login] = false
    opts.on(
      '-l', '--login', 'login to remote repo as a non-anonymous user'
    ) do
      options[:login] = true
    end

    opts.on(
      '--no-file', 'ignore package.fig file in current directory'
    ) do |path|
      options[:package_config_file] = :none
    end

    options[:publish] = nil
    opts.on(
      '--publish PKG', 'install PKG in $FIG_HOME and in remote repo'
    ) do |publish|
      options[:publish] = publish
    end

    options[:publish_local] = nil
    opts.on(
      '--publish-local PKG', 'install package only in $FIG_HOME'
    ) do |publish_local|
      options[:publish_local] = publish_local
    end

    options[:resources] =[]
    opts.on(
      '--resource FULLPATH',
      'include FULLPATH resource in package (when using --publish)'
    ) do |path|
      options[:resources] << Package::Resource.new(path)
    end

    opts.on(
      '-s', '--set VAR=VAL', 'set environment variable VAR to VAL'
    ) do |var_val|
      var, val = var_val.split('=')
      options[:non_command_package_statements] << Package::Set.new(var, val)
    end

    options[:update] = false
    opts.on(
      '-u',
      '--update',
      'check remote repo for updates and download to $FIG_HOME as necessary'
    ) do
      options[:update] = true
    end

    options[:update_if_missing] = false
    opts.on(
      '-m',
      '--update-if-missing',
      'check remote repo for updates only if package missing from $FIG_HOME'
    ) do
      options[:update_if_missing] = true
    end

    opts.on(
      '--figrc PATH', 'add PATH to configuration used for Fig'
    ) do |path|
      options[:figrc] = path
    end

    opts.on('--no-figrc', 'ignore ~/.figrc') { options[:no_figrc] = true }

    opts.on(
      '--log-config PATH', 'use PATH file as configuration for Log4r'
    ) do |path|
      options[:log_config] = path
    end

    level_list = LOG_LEVELS.join(', ')
    opts.on(
      '--log-level LEVEL',
      LOG_LEVELS,
      LOG_ALIASES,
      'set logging level to LEVEL',
      "  (#{level_list})"
    ) do |log_level|
      options[:log_level] = log_level
    end

    options[:home] = ENV['FIG_HOME'] || File.expand_path('~/.fighome')
  end

  # Need to catch the exception thrown from parser and retranslate into a fig exception
  begin
    parser.parse!(argv)
  rescue OptionParser::MissingArgument => error
    $stderr.puts "Please provide the #{error}."
    return nil, nil, 1
  end

  return options, argv, nil
end

#parse_package_config_file(options, package_config_file, environment, configuration) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/fig.rb', line 119

def parse_package_config_file(options, package_config_file, environment, configuration)
  if package_config_file
    package = Parser.new(configuration).parse_package(nil, nil, '.', package_config_file)
    if options[:update] || options[:update_if_missing]
      package.retrieves.each do |var, path|
        environment.add_retrieve(var, path)
      end
    end

    unless options[:publish] || options[:list] || options[:publish_local]
      environment.register_package(package)
      environment.apply_config(package, options[:config], nil)
    end
  else
    package = Package.new(nil, nil, '.', [])
  end

  return package, environment
end

#publish(argv, options, package, repository) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/fig.rb', line 139

def publish(argv, options, package, repository)
  if not argv.empty?
    $stderr.puts %Q<Unexpected arguments: #{argv.join(' ')}>
    return 10
  end

  package_name, config_name, version_name = parse_descriptor(options[:publish] || options[:publish_local])

  if package_name.nil? || version_name.nil?
    $stderr.puts 'Please specify a package name and a version name.'
    return 10
  end

  if not options[:non_command_package_statements].empty?
    publish_statements = options[:resources] + options[:archives] + [Package::Configuration.new('default', options[:non_command_package_statements])]
    publish_statements << Package::Publish.new('default','default')
  elsif not package.statements.empty?
    publish_statements = package.statements
  else
    $stderr.puts 'Nothing to publish.'
    return 1
  end

  if options[:publish]
    Logging.info "Checking status of #{package_name}/#{version_name}..."

    if repository.list_remote_packages.include?("#{package_name}/#{version_name}")
      Logging.info "#{package_name}/#{version_name} has already been published."

      if not options[:force]
        Logging.fatal 'Use the --force option if you really want to overwrite, or use --publish-local for testing.'
        return 1
      else
        Logging.info 'Overwriting...'
      end
    end
  end

  Logging.info "Publishing #{package_name}/#{version_name}."
  repository.publish_package(publish_statements, package_name, version_name, options[:publish_local])

  return 0
end

#read_in_package_config_file(options) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/fig.rb', line 31

def read_in_package_config_file(options)
  if File.exist?(options[:package_config_file])
    return File.read(options[:package_config_file])
  else
    raise UserInputError.new(%Q<File not found: "#{options[:package_config_file]}".>)
  end
end

#resolve_listing(options, repository) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/fig.rb', line 100

def resolve_listing(options, repository)
  if options[:list]
    display_package_list(repository)
    return true
  end

  if options[:list_remote]
    display_remote_package_list(repository)
    return true
  end

  if not options[:list_configs].empty?
    display_configs_in_local_packages_list(options, repository)
    return true
  end

  return false
end

#run_fig(argv) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/fig.rb', line 183

def run_fig(argv)
  shell_command = initialize_shell_command(argv)

  options, argv, exit_value = parse_options(argv)
  if not exit_value.nil?
    return exit_value
  end

  Logging.initialize_pre_configuration(options[:log_level])

  remote_url = initialize_remote_url(options)

  configuration = FigRC.find(
    options[:figrc], remote_url, options[:login], options[:home], options[:no_figrc]
  )

  Logging.initialize_post_configuration(options[:log_config] || configuration['log configuration'], options[:log_level])

  os = OS.new(options[:login])
  repository = Repository.new(
    os,
    File.expand_path(File.join(options[:home], 'repos')),
    remote_url,
    configuration,
    nil, # remote_user
    options[:update],
    options[:update_if_missing]
  )

  retriever = Retriever.new('.')
  # Check to see if this is still happening with the new layers of abstraction.
  at_exit { retriever.save }
  environment = Environment.new(os, repository, nil, retriever)

  options[:non_command_package_statements].each do |statement|
    environment.apply_config_statement(nil, statement, nil)
  end

  package_config_file = load_package_config_file_contents(options)

  options[:cleans].each do |descriptor|
    package_name, version_name = descriptor.split('/')
    repository.clean(package_name, version_name)
    return true
  end

  if resolve_listing(options, repository)
    return true
  end

  package, environment = parse_package_config_file(options, package_config_file, environment, configuration)

  if options[:publish] || options[:publish_local]
    return publish(argv, options, package, repository)
  elsif options[:get]
    puts environment[options[:get]]
  elsif shell_command
    argv.shift
    environment.execute_shell(shell_command) { |cmd| os.shell_exec cmd }
  elsif not argv.empty?
    package_name, config_name, version_name = parse_descriptor(argv.shift)
    environment.include_config(package, package_name, config_name, version_name, {}, nil)
    environment.execute_config(package, package_name, config_name, nil, argv) { |cmd| os.shell_exec cmd }
  elsif not repository.updating?
    $stderr.puts "Nothing to do.\n"
    $stderr.puts USAGE
    $stderr.puts %q<Run "fig --help" for a full list of commands.>
    return 1
  end

  return 0
end

#run_with_exception_handling(argv) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/fig.rb', line 263

def run_with_exception_handling(argv)
  begin
    return_code = run_fig(argv)
    return return_code
  rescue URLAccessError => error
    urls = exception.urls.join(', ')
    $stderr.puts "Access to #{urls} in #{exception.package}/#{exception.version} not allowed."
    return 1
  rescue UserInputError => error
    log_error_message(error)
    return 1
  rescue OptionParser::InvalidOption => error
    $stderr.puts error.to_s
    $stderr.puts USAGE
    return 1
  rescue RepositoryError => error
    log_error_message(error)
    return 1
  end
end