class Fig::Repository

Overall management of a repository. Handles local operations itself; defers remote operations to others.

Constants

LOCAL_VERSION_SUPPORTED
METADATA_SUBDIRECTORY
PACKAGE_FILE_IN_REPO
REMOTE_VERSION_SUPPORTED
RESOURCES_FILE
VERSION_FILE_NAME

Attributes

remote_repository_url[R]

Public Class Methods

new( application_configuration, options, operating_system, local_repository_directory, remote_repository_url, parser, publish_listeners ) click to toggle source
# File lib/fig/repository.rb, line 32
def initialize(
  application_configuration,
  options,
  operating_system,
  local_repository_directory,
  remote_repository_url,
  parser,
  publish_listeners
)
  @application_configuration    = application_configuration
  @options                      = options
  @operating_system             = operating_system
  @local_repository_directory   = local_repository_directory
  @remote_repository_url        = remote_repository_url
  @parser                       = parser
  @publish_listeners            = publish_listeners

  initialize_local_repository()
  reset_cached_data()
end

Public Instance Methods

clean(descriptor) click to toggle source
# File lib/fig/repository.rb, line 123
def clean(descriptor)
  check_local_repository_format()

  @package_cache.remove_package(descriptor.name, descriptor.version)

  nuke_package descriptor

  return
end
get_package( descriptor, allow_any_version = false ) click to toggle source
# File lib/fig/repository.rb, line 86
def get_package(
  descriptor,
  allow_any_version = false
)
  check_local_repository_format()

  if not descriptor.version
    if allow_any_version
      package = @package_cache.get_any_version_of_package(descriptor.name)
      if package
        Fig::Logging.warn(
          "Picked version #{package.version} of #{package.name} at random."
        )
        return package
      end
    end

    raise Fig::RepositoryError.new(
      %Q<Cannot retrieve "#{descriptor.name}" without a version.>
    )
  end

  package = @package_cache.get_package(descriptor.name, descriptor.version)
  return package if package

  Fig::Logging.debug \
    "Considering #{Fig::PackageDescriptor.format(descriptor.name, descriptor.version, nil)}."

  if should_update?(descriptor)
    check_remote_repository_format()

    update_package(descriptor)
  end

  return read_local_package(descriptor)
end
list_packages() click to toggle source
# File lib/fig/repository.rb, line 57
def list_packages
  check_local_repository_format

  results = []
  if File.exist?(local_package_directory)
    @operating_system.list(local_package_directory).each do
      |name|

      @operating_system.list(
        File.join local_package_directory(), name
      ).each do
        |version|

        results << Fig::PackageDescriptor.format(name, version, nil)
      end
    end
  end

  return results
end
list_remote_packages() click to toggle source
# File lib/fig/repository.rb, line 78
def list_remote_packages
  check_remote_repository_format()

  paths = @operating_system.download_list(remote_repository_url())

  return paths.reject { |path| path =~ %r< ^ #{METADATA_SUBDIRECTORY} / >xs }
end
publish_package( package_statements, descriptor, local_only, source_package, was_forced ) click to toggle source
# File lib/fig/repository.rb, line 133
def publish_package(
  package_statements, descriptor, local_only, source_package, was_forced
)
  check_local_repository_format()
  if not local_only
    check_remote_repository_format()
  end

  publisher = Fig::RepositoryPackagePublisher.new
  publisher.application_configuration    = @application_configuration
  publisher.options                      = @options
  publisher.operating_system             = @operating_system
  publisher.publish_listeners            = @publish_listeners
  publisher.package_statements           = package_statements
  publisher.descriptor                   = descriptor
  publisher.source_package               = source_package
  publisher.was_forced                   = was_forced
  publisher.base_temp_dir                = base_temp_dir
  publisher.runtime_for_package          = runtime_for_package(descriptor)
  publisher.local_only                   = local_only
  publisher.local_directory_for_package  =
    local_directory_for_package(descriptor)
  publisher.local_fig_file_for_package =
    local_fig_file_for_package(descriptor)

  if not local_only
    publisher.remote_directory_for_package =
      remote_directory_for_package(descriptor)
    publisher.remote_fig_file_for_package =
      remote_fig_file_for_package(descriptor)
  end

  return publisher.publish_package()
end
reset_cached_data() click to toggle source
# File lib/fig/repository.rb, line 53
def reset_cached_data()
  @package_cache = Fig::PackageCache.new()
end
update_if_missing() click to toggle source
# File lib/fig/repository.rb, line 172
def update_if_missing()
  @update_condition = :if_missing
end
update_unconditionally() click to toggle source
# File lib/fig/repository.rb, line 168
def update_unconditionally()
  @update_condition = :unconditionally
end

Private Instance Methods

base_temp_dir() click to toggle source

Note that we use a temporary directory within the repository so that we don't need to worry about crossing file-system boundaries. If we stored things in /tmp, we couldn't do true file moves, just copies and deletes, and there would be race conditions and other not-fun things to worry about.

# File lib/fig/repository.rb, line 492
def base_temp_dir()
  File.join(@local_repository_directory, 'tmp')
end
check_local_repository_format() click to toggle source
# File lib/fig/repository.rb, line 191
def check_local_repository_format()
  version = local_repository_version

  if version != LOCAL_VERSION_SUPPORTED
    message =
      "Local repository is in version #{version} format. This version of fig can only deal with repositories in version #{LOCAL_VERSION_SUPPORTED} format."
    if version < LOCAL_VERSION_SUPPORTED
      message +=
        " Either point $FIG_HOME to a different location or rename or delete #{@local_repository_directory}. (Be mindful of packages that have been published only locally.)"
    end
    Fig::Logging.fatal message
    raise Fig::RepositoryError.new
  end

  return
end
check_remote_repository_format() click to toggle source
# File lib/fig/repository.rb, line 208
def check_remote_repository_format()
  version = remote_repository_version

  if version != REMOTE_VERSION_SUPPORTED
    Fig::Logging.fatal \
      "Remote repository is in version #{version} format. This version of fig can only deal with repositories in version #{REMOTE_VERSION_SUPPORTED} format."
    raise Fig::RepositoryError.new
  end

  return
end
download_assets(package, descriptor, temporary_package, temporary_runtime) click to toggle source
# File lib/fig/repository.rb, line 422
def download_assets(package, descriptor, temporary_package, temporary_runtime)
  remote_package_directory = remote_directory_for_package(descriptor)
  package.archive_locations.each do
    |archive_location|

    if not Fig::URL.is_url?(archive_location)
      archive_location = Fig::URL.append_path_components(
        remote_package_directory, [archive_location]
      )
    end
    @operating_system.download_and_unpack_archive(
      archive_location, temporary_package, temporary_runtime
    )
  end
  package.resource_locations.each do
    |resource_location|

    if not Fig::URL.is_url?(resource_location)
      resource_location = Fig::URL.append_path_components(
        remote_package_directory, [resource_location]
      )
    end

    basename, path =
      @operating_system.download_resource(
        resource_location, temporary_package
      )

    @operating_system.copy path, File.join(temporary_runtime, basename)
  end

  return
end
fig_file_for_package_download(package_download_dir) click to toggle source
# File lib/fig/repository.rb, line 466
def fig_file_for_package_download(package_download_dir)
  File.join(package_download_dir, PACKAGE_FILE_IN_REPO)
end
initialize_local_repository() click to toggle source
# File lib/fig/repository.rb, line 180
def initialize_local_repository()
  FileUtils.mkdir_p(@local_repository_directory)

  version_file = local_version_file()
  if not File.exist?(version_file)
    File.open(version_file, 'w') { |handle| handle.write(LOCAL_VERSION_SUPPORTED) }
  end

  return
end
install_package(descriptor, temporary_directory) click to toggle source
# File lib/fig/repository.rb, line 321
def install_package(descriptor, temporary_directory)
  remote_fig_file   = remote_fig_file_for_package(descriptor)
  package_directory = local_directory_for_package(descriptor)
  local_fig_file    = fig_file_for_package_download(package_directory)

  if @operating_system.path_up_to_date?(
    remote_fig_file, local_fig_file, :prompt_for_login
  )
    Fig::Logging.debug \
      "Skipping update of #{descriptor.to_string} because it looks like #{local_fig_file} is up-to-date."
    return
  end

  temporary_package = File.join temporary_directory, 'packages'
  temporary_runtime = File.join temporary_directory, 'runtime'
  temp_fig_file   = fig_file_for_package_download(temporary_package)
  set_up_package_download_directories(
    temporary_directory, temporary_package, package_directory
  )

  @updating_package_definition = true
  return if ! @operating_system.download(
    remote_fig_file, temp_fig_file, :prompt_for_login
  )

  package = read_package_from_directory(temporary_package, descriptor)
  @updating_package_definition = false

  download_assets(package, descriptor, temporary_package, temporary_runtime)

  nuke_package descriptor

  FileUtils.mkdir_p File.dirname(package_directory)
  FileUtils.mv temporary_package, package_directory

  runtime_directory = package.runtime_directory
  if File.exists? temporary_runtime
    FileUtils.mkdir_p File.dirname(runtime_directory)
    FileUtils.mv temporary_runtime, runtime_directory
  else
    FileUtils.mkdir_p runtime_directory
  end

  return
end
local_directory_for_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 476
def local_directory_for_package(descriptor)
  return File.join(
    local_package_directory, descriptor.name, descriptor.version
  )
end
local_fig_file_for_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 462
def local_fig_file_for_package(descriptor)
  File.join(local_directory_for_package(descriptor), PACKAGE_FILE_IN_REPO)
end
local_package_directory() click to toggle source
# File lib/fig/repository.rb, line 235
def local_package_directory()
  return File.expand_path(File.join(@local_repository_directory, 'packages'))
end
local_repository_version() click to toggle source
# File lib/fig/repository.rb, line 220
def local_repository_version()
  if @local_repository_version.nil?
    version_file = local_version_file()

    @local_repository_version =
      parse_repository_version(version_file, version_file)
  end

  return @local_repository_version
end
local_version_file() click to toggle source
# File lib/fig/repository.rb, line 231
def local_version_file()
  return File.join(@local_repository_directory, VERSION_FILE_NAME)
end
nuke_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 509
def nuke_package(descriptor)
  FileUtils.rm_rf runtime_for_package(descriptor)
  FileUtils.rm_rf local_directory_for_package(descriptor)

  return
end
package_download_temp_dir(descriptor) click to toggle source
# File lib/fig/repository.rb, line 496
def package_download_temp_dir(descriptor)
  base_directory = File.join(base_temp_dir(), 'package-download')
  FileUtils.mkdir_p(base_directory)

  return Dir.mktmpdir(
    "#{descriptor.name}.version.#{descriptor.version}+", base_directory
  )
end
package_missing?(descriptor) click to toggle source
# File lib/fig/repository.rb, line 505
def package_missing?(descriptor)
  not File.exist?(local_fig_file_for_package(descriptor))
end
package_runtime_directory() click to toggle source
# File lib/fig/repository.rb, line 239
def package_runtime_directory()
  return File.expand_path(File.join(@local_repository_directory, 'runtime'))
end
parse_repository_version(version_file, description) click to toggle source
# File lib/fig/repository.rb, line 269
def parse_repository_version(version_file, description)
  if not File.exist?(version_file)
    return 1 # Since there was no version file early in Fig development.
  end

  version_string = IO.read(version_file)
  version_string.strip!()
  if version_string !~ / \A \d+ \z /x
    Fig::Logging.fatal \
      %Q<Could not parse the contents of "#{description}" ("#{version_string}") as a version.>
    raise Fig::RepositoryError.new
  end

  return version_string.to_i()
end
read_local_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 291
def read_local_package(descriptor)
  directory = local_directory_for_package(descriptor)
  return read_package_from_directory(directory, descriptor)
end
read_package_from_directory(directory, descriptor) click to toggle source
# File lib/fig/repository.rb, line 367
def read_package_from_directory(directory, descriptor)
  dot_fig_file = File.join(directory, PACKAGE_FILE_IN_REPO)
  if not File.exist?(dot_fig_file)
    message =
      %Q<Fig file not found for package "#{descriptor.name || '<unnamed>'}". There is nothing in "#{dot_fig_file}".>
    if ! @update_condition
      message +=
        ' You might want to try specifying the --update or --update-if-missing options.'
    end

    Fig::Logging.fatal message
    raise Fig::RepositoryError.new
  end

  return read_package_from_file(dot_fig_file, descriptor)
end
read_package_from_file(file_name, descriptor) click to toggle source
# File lib/fig/repository.rb, line 384
def read_package_from_file(file_name, descriptor)
  if not File.exist?(file_name)
    Fig::Logging.fatal "Package not found: #{descriptor.to_string()}"
    raise Fig::RepositoryError.new
  end
  content = File.read(file_name)

  unparsed_package = Fig::NotYetParsedPackage.new
  unparsed_package.descriptor         = descriptor
  unparsed_package.working_directory  =
    unparsed_package.include_file_base_directory =
    runtime_for_package(descriptor)
  unparsed_package.source_description = descriptor.to_string()
  unparsed_package.unparsed_text      = content

  package = @parser.parse_package(unparsed_package)

  @package_cache.add_package(package)

  return package
end
remote_directory_for_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 470
def remote_directory_for_package(descriptor)
  return Fig::URL.append_path_components(
    remote_repository_url(), [descriptor.name, descriptor.version]
  )
end
remote_fig_file_for_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 456
def remote_fig_file_for_package(descriptor)
  return Fig::URL.append_path_components(
    remote_directory_for_package(descriptor), [PACKAGE_FILE_IN_REPO]
  )
end
remote_repository_version() click to toggle source
# File lib/fig/repository.rb, line 243
def remote_repository_version()
  if @remote_repository_version.nil?
    temp_dir = base_temp_dir()
    @operating_system.delete_and_recreate_directory(temp_dir)
    remote_version_file = Fig::URL.append_path_components(
      remote_repository_url(), [VERSION_FILE_NAME]
    )
    local_version_file = File.join(temp_dir, "remote-#{VERSION_FILE_NAME}")
    begin
      @operating_system.download(
        remote_version_file, local_version_file, :prompt_for_login
      )
    rescue Fig::FileNotFoundError
      # The download may create an empty file, so get rid of it.
      if File.exist?(local_version_file)
        File.unlink(local_version_file)
      end
    end

    @remote_repository_version =
      parse_repository_version(local_version_file, remote_version_file)
  end

  return @remote_repository_version
end
runtime_for_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 482
def runtime_for_package(descriptor)
  return File.join(
    package_runtime_directory, descriptor.name, descriptor.version
  )
end
set_up_package_download_directories( temporary_directory, temporary_package, package_directory ) click to toggle source
# File lib/fig/repository.rb, line 406
def set_up_package_download_directories(
  temporary_directory, temporary_package, package_directory
)
  FileUtils.rm_rf temporary_directory
  if File.exist? package_directory
    FileUtils.mkdir_p File.dirname(temporary_package)
    FileUtils.cp_r(
      package_directory, temporary_package, :preserve => true
    )
  else
    FileUtils.mkdir_p temporary_package
  end

  return
end
should_update?(descriptor) click to toggle source
# File lib/fig/repository.rb, line 285
def should_update?(descriptor)
  return true if @update_condition == :unconditionally

  return @update_condition == :if_missing && package_missing?(descriptor)
end
update_package(descriptor) click to toggle source
# File lib/fig/repository.rb, line 296
def update_package(descriptor)
  temp_dir = package_download_temp_dir(descriptor)
  begin
    install_package(descriptor, temp_dir)
  rescue Fig::FileNotFoundError => error
    if @updating_package_definition
      Fig::Logging.fatal \
        "Package #{descriptor.to_string} not found in remote repository. (Was looking for #{error.path}.)"
    else
      Fig::Logging.fatal \
        "Part of package #{descriptor.to_string} was not downloadable. (Was attempting to get #{error.path}.)"
    end

    raise Fig::RepositoryError.new
  rescue StandardError => exception
    Fig::Logging.fatal %Q<Install of #{descriptor.to_string} failed: #{exception}>

    raise Fig::RepositoryError.new
  ensure
    FileUtils.rm_rf(temp_dir)
  end

  return
end