diff --git a/Gemfile.lock b/Gemfile.lock index 4281ee6..8be10b7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - picopackage (0.1.0) + picopackage (0.2.0) digest open-uri (~> 0.5) yaml (~> 0.4) diff --git a/lib/picopackage.rb b/lib/picopackage.rb index c1e3587..295d58e 100644 --- a/lib/picopackage.rb +++ b/lib/picopackage.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative "picopackage/version" +require_relative "picopackage/http_fetcher" require_relative "picopackage/provider" require_relative "picopackage/source_file" require_relative "picopackage/scanner" @@ -10,5 +11,6 @@ require_relative "picopackage/cli" module Picopackage class Error < StandardError; end class FileTooLargeError < StandardError; end + class FetchError < StandardError; end class LocalModificationError < StandardError; end end diff --git a/lib/picopackage/cli.rb b/lib/picopackage/cli.rb index b3641e1..cf59495 100644 --- a/lib/picopackage/cli.rb +++ b/lib/picopackage/cli.rb @@ -77,14 +77,30 @@ module Picopackage end begin - source_file = Fetch.fetch(url, path, force: options[:force]) + Fetch.fetch(url, path, force: options[:force]) + rescue LocalModificationError => e + puts "Error: #{e.message}" + rescue => e + puts "Error: #{e.message}" + exit 1 + end + + when 'update' + options = { force: false } + OptionParser.new do |opts| + opts.banner = "Usage: ppkg update [options] FILE" + opts.on('-f', '--force', 'Force update') { |f| options[:force] = f } + end.parse!(argv) + + file = argv.first + source_file = SourceFile.from_file(file) + begin + Fetch.fetch(source_file.url, path, force: options[:force]) rescue LocalModificationError => e puts "Error: #{e.message}" rescue => e puts "Error: #{e.message}" exit 1 - # Optionally retry with force - # source_file = Fetch.fetch(url, destination, force: true) end else diff --git a/lib/picopackage/fetch.rb b/lib/picopackage/fetch.rb index b1aacaa..00210af 100644 --- a/lib/picopackage/fetch.rb +++ b/lib/picopackage/fetch.rb @@ -8,29 +8,37 @@ module Picopackage class Fetch def self.fetch(url, destination, force: false) raise ArgumentError, "Destination directory does not exist: #{destination}" unless Dir.exist?(destination) -debugger + provider = Provider.for(url) - file_path = File.join(destination, provider.source_file.filename) - debugger + source_file = provider.source_file + + file_path = File.join(destination, source_file.filename) + if File.exist?(file_path) && force - provider.source_file.save(destination) + source_file.save(destination) elsif File.exist?(file_path) local_source_file = SourceFile.from_file(file_path) - status = Status.compare(local_source_file, provider.source_file) + status = Status.compare(local_source_file, source_file) if force - provider.source_file.save(destination) + source_file.save(destination) elsif status.modified? raise LocalModificationError, "#{status.message}. Use -f or --force to overwrite local version" elsif status.outdated? - puts "Updated from #{local_source_file.version} to #{provider.source_file.version}" - provider.source_file.save(destination) + puts "Updated from #{local_source_file.version} to #{source_file.version}" + source_file.save(destination) elsif status.up_to_date? puts status.message end else - provider.source_file.save(destination) + source_file.save(destination) + if source_file.imported? + source_file.sign + puts "Picopackage created for #{source_file.filename}" + else + puts "Picopackage downloaded to #{file_path}" + end end provider.source_file end @@ -82,15 +90,15 @@ debugger def message case state when :up_to_date - "File is up to date" + "Picopackage is up to date" when :outdated if modified? - "Local file (v#{local_version}) has modifications but remote version (v#{remote_version}) is available" + "Local Picopackage (v#{local_version}) has modifications but remote version (v#{remote_version}) is available" else - "Local file (v#{local_version}) is outdated. Remote version: v#{remote_version}" + "Local Picopackage (v#{local_version}) is outdated. Remote version: v#{remote_version}" end when :modified - "Local file has been modified from original version (v#{local_version})" + "Local Picopackage has been modified from original version (v#{local_version})" end end end diff --git a/lib/picopackage/provider.rb b/lib/picopackage/provider.rb index 0470338..cb945f3 100644 --- a/lib/picopackage/provider.rb +++ b/lib/picopackage/provider.rb @@ -28,7 +28,6 @@ module Picopackage class DefaultProvider MAX_SIZE = 1024 * 1024 TIMEOUT = 10 - attr_reader :url, :source_file def self.handles_url?(url) = :maybe @@ -44,6 +43,8 @@ module Picopackage def body = @body ||= fetch + def json_body = @json_body ||= JSON.parse(body) + def fetch begin Net::HTTP.start(@uri.host, @uri.port, use_ssl: @uri.scheme == 'https', read_timeout: TIMEOUT, open_timeout: TIMEOUT) do |http| @@ -71,7 +72,9 @@ module Picopackage end def content - # Implement in subclass - this come from the `body`. Spliting content into code and metadata is the job of the SourceFile class + # Implement in subclass - this come from the `body`. + # Spliting content into code and metadata is the job of the SourceFile class + raise NotImplementedError end @@ -81,7 +84,9 @@ module Picopackage end def source_file - @source_file ||= SourceFile.from_content(content) + @source_file ||= SourceFile.from_content( + content, metadata: {'filename' => filename, 'url' => url, 'version' => '0.0.1'} + ) end end @@ -93,15 +98,9 @@ module Picopackage "https://api.github.com/gists/#{gist_id}" end - def content - data = JSON.parse(body) - file = data["files"].values.first["content"] - end + def content = json_body["files"].values.first["content"] - def filename - data = JSON.parse(body) - data["files"].values.first["filename"] - end + def filename = json_body["files"].values.first["filename"] end class OpenGistProvider < DefaultProvider @@ -109,19 +108,11 @@ module Picopackage :maybe end - def transform_url(url) - "#{url}.json" - end + def transform_url(url) = "#{url}.json" - def content - data = JSON.parse(body) - @content = data.dig("files",0, "content") - end + def content = json_body.dig("files",0, "content") - def filename - data = JSON.parse(body) - data.dig("files",0, "filename") - end + def filename = json_body.dig("files",0, "filename") end PROVIDERS = [ diff --git a/lib/picopackage/source_file.rb b/lib/picopackage/source_file.rb index 174ddc1..542bdad 100644 --- a/lib/picopackage/source_file.rb +++ b/lib/picopackage/source_file.rb @@ -9,12 +9,15 @@ module Picopackage def self.from_file(file_path) = new(content: File.read(file_path), original_path: file_path) - def self.from_content(content, filename: nil) + def self.from_content(content, metadata: {}) instance = new(content: content) - if filename && !instance.metadata['filename'] - metadata = instance.metadata.merge('filename' => filename) - instance.update_metadata(metadata) #TODO: FIX THIS - end + instance.imported! if instance.metadata.empty? + + updated_metadata = metadata.merge(instance.metadata) + + ## For new Picopackages, we should add metadata and sign + instance.update_metadata(updated_metadata) + instance end @@ -25,10 +28,18 @@ module Picopackage @metadata = extract_metadata @code = extract_code end + + def imported! = @imported = true + + def imported? = @imported ||= false + + def content = @content + def url = @metadata['url'] + def filename = @metadata['filename'] - def version = @metadata['version'] || '0.0.0' + def version = @metadata['version'] || '0.0.1' def checksum = "sha256:#{Digest::SHA256.hexdigest(code)}"