require 'fig/logging' require 'fig/notfounderror' require 'fig/packagecache' require 'fig/parser' require 'fig/repositoryerror' require 'fig/statement/archive' require 'fig/statement/resource' require 'fig/urlaccesserror' module Fig # Overall management of a repository. Handles local operations itself; # defers remote operations to others. class Repository METADATA_SUBDIRECTORY = '_meta' def self.is_url?(url) not (/ftp:\/\/|http:\/\/|file:\/\/|ssh:\/\// =~ url).nil? end def initialize( os, local_repository_dir, remote_repository_url, application_config, remote_repository_user = nil, update = false, update_if_missing = true ) @operating_system = os @local_repository_dir = local_repository_dir @remote_repository_url = remote_repository_url @remote_repository_user = remote_repository_user @update = update @update_if_missing = update_if_missing @parser = Parser.new(application_config) @packages = PackageCache.new() end def list_packages results = [] if File.exist?(@local_repository_dir) @operating_system.list(@local_repository_dir).each do |package_name| @operating_system.list(File.join(@local_repository_dir, package_name)).each do |version_name| results << "#{package_name}/#{version_name}" end end end return results end def list_remote_packages paths = @operating_system.download_list(@remote_repository_url) return paths.reject { |path| path =~ %r< ^ #{METADATA_SUBDIRECTORY} / >xs } end def get_package( package_name, version_name, disable_updating = false, allow_any_version = false ) if not version_name if allow_any_version package = @packages.get_any_version_of_package(package_name) if package Logging.warn( "Picked version #{package.version_name} of #{package.package_name} at random." ) return package end end raise RepositoryError.new( %Q ) end package = @packages.get_package(package_name, version_name) return package if package Logging.debug "Considering #{package_name}/#{version_name}." if should_update?(package_name, version_name, disable_updating) update_package(package_name, version_name) end return read_local_package(package_name, version_name) end def clean(package_name, version_name) @packages.remove_package(package_name, version_name) dir = File.join(@local_repository_dir, package_name) dir = File.join(dir, version_name) if version_name FileUtils.rm_rf(dir) return end def publish_package( package_statements, package_name, version_name, local_only ) temp_dir = temp_dir_for_package(package_name, version_name) @operating_system.clear_directory(temp_dir) local_dir = local_dir_for_package(package_name, version_name) @operating_system.clear_directory(local_dir) fig_file = File.join(temp_dir, '.fig') content = derive_package_content( package_statements, package_name, version_name, local_dir, local_only ) @operating_system.write(fig_file, content.join("\n").strip) @operating_system.upload( fig_file, remote_fig_file_for_package(package_name, version_name), @remote_repository_user ) unless local_only @operating_system.copy( fig_file, local_fig_file_for_package(package_name, version_name) ) FileUtils.rm_rf(temp_dir) end def updating? return @update || @update_if_missing end private def should_update?(package_name, version_name, disable_updating) return false if disable_updating return true if @update return @update_if_missing && package_missing?(package_name, version_name) end def read_local_package(package_name, version_name) dir = local_dir_for_package(package_name, version_name) return read_package_from_directory(dir, package_name, version_name) end def bundle_resources(package_statements) resources = [] new_package_statements = package_statements.reject do |statement| if ( statement.is_a?(Statement::Resource) && ! Repository.is_url?(statement.url) ) resources << statement.url true else false end end if resources.size > 0 resources = expand_globs_from(resources) file = 'resources.tar.gz' @operating_system.create_archive(file, resources) new_package_statements.unshift(Statement::Archive.new(file)) at_exit { File.delete(file) } end return new_package_statements end def install_package(package_name, version_name) temp_dir = nil begin package = read_local_package(package_name, version_name) temp_dir = temp_dir_for_package(package_name, version_name) @operating_system.clear_directory(temp_dir) package.archive_urls.each do |archive_url| if not Repository.is_url?(archive_url) archive_url = remote_dir_for_package(package_name, version_name) + '/' + archive_url end @operating_system.download_archive(archive_url, File.join(temp_dir)) end package.resource_urls.each do |resource_url| if not Repository.is_url?(resource_url) resource_url = remote_dir_for_package(package_name, version_name) + '/' + resource_url end @operating_system.download_resource(resource_url, File.join(temp_dir)) end local_dir = local_dir_for_package(package_name, version_name) @operating_system.clear_directory(local_dir) # some packages contain no files, only a fig file. if not (package.archive_urls.empty? && package.resource_urls.empty?) FileUtils.mv(Dir.glob(File.join(temp_dir, '*')), local_dir) end write_local_package(package_name, version_name, package) rescue Logging.fatal 'Install failed, cleaning up.' delete_local_package(package_name, version_name) raise RepositoryError.new ensure if temp_dir FileUtils.rm_rf(temp_dir) end end end def update_package(package_name, version_name) remote_fig_file = remote_fig_file_for_package(package_name, version_name) local_fig_file = local_fig_file_for_package(package_name, version_name) begin if @operating_system.download(remote_fig_file, local_fig_file) install_package(package_name, version_name) end rescue NotFoundError Logging.fatal "Package not found in remote repository: #{package_name}/#{version_name}" delete_local_package(package_name, version_name) raise RepositoryError.new end end # 'resources' is an Array of filenames: ['tmp/foo/file1', 'tmp/foo/*.jar'] def expand_globs_from(resources) expanded_files = [] resources.each {|f| expanded_files.concat(Dir.glob(f))} expanded_files end def read_package_from_directory(dir, package_name, version_name) file = File.join(dir, '.fig') if not File.exist?(file) file = File.join(dir, 'package.fig') end if not File.exist?(file) Logging.fatal %Q'}": #{file}> raise RepositoryError.new end return read_package_from_file(file, package_name, version_name) end def read_package_from_file(file_name, package_name, version_name) if not File.exist?(file_name) Logging.fatal "Package not found: #{package_name}/#{version_name}" raise RepositoryError.new end content = File.read(file_name) package = @parser.parse_package( package_name, version_name, File.dirname(file_name), content ) @packages.add_package(package) return package end def delete_local_package(package_name, version_name) FileUtils.rm_rf(local_dir_for_package(package_name, version_name)) end def write_local_package(package_name, version_name, package) file = local_fig_file_for_package(package_name, version_name) @operating_system.write(file, package.unparse) end def remote_fig_file_for_package(package_name, version_name) "#{@remote_repository_url}/#{package_name}/#{version_name}/.fig" end def local_fig_file_for_package(package_name, version_name) File.join(local_dir_for_package(package_name, version_name), '.fig') end def remote_dir_for_package(package_name, version_name) "#{@remote_repository_url}/#{package_name}/#{version_name}" end def local_dir_for_package(package_name, version_name) return File.join(@local_repository_dir, package_name, version_name) end def temp_dir_for_package(package_name, version_name) File.join(@local_repository_dir, 'tmp') end def package_missing?(package_name, version_name) not File.exist?(local_fig_file_for_package(package_name, version_name)) end def derive_package_content( package_statements, package_name, version_name, local_dir, local_only ) return bundle_resources(package_statements).map do |statement| if statement.is_a?(Statement::Publish) nil elsif statement.is_a?(Statement::Archive) || statement.is_a?(Statement::Resource) if statement.is_a?(Statement::Resource) && !Repository.is_url?(statement.url) archive_name = statement.url archive_remote = "#{remote_dir_for_package(package_name, version_name)}/#{statement.url}" else archive_name = statement.url.split('/').last archive_remote = "#{remote_dir_for_package(package_name, version_name)}/#{archive_name}" end if Repository.is_url?(statement.url) archive_local = File.join(temp_dir, archive_name) @operating_system.download(statement.url, archive_local) else archive_local = statement.url end @operating_system.upload(archive_local, archive_remote, @remote_repository_user) unless local_only @operating_system.copy(archive_local, local_dir + '/' + archive_name) if statement.is_a?(Statement::Archive) @operating_system.unpack_archive(local_dir, archive_name) end statement.class.new(archive_name).unparse('') else statement.unparse('') end end.select { |s|not s.nil? } end end end