Compare commits
5 Commits
rename_to_
...
a066b73f2d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a066b73f2d | ||
|
|
5e05567309 | ||
|
|
b59ac53e4b | ||
|
|
7f1dc01247 | ||
|
|
0c37362a8a |
@@ -1,5 +1,9 @@
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0] - 2025-01-21
|
||||
|
||||
- Rename to from Picop to Picopackage
|
||||
|
||||
## [0.1.0] - 2025-01-19
|
||||
|
||||
- Initial release
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
picop (0.1.0)
|
||||
picopackage (0.2.0)
|
||||
digest
|
||||
open-uri (~> 0.5)
|
||||
yaml (~> 0.4)
|
||||
@@ -70,7 +70,7 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
debug
|
||||
minitest (~> 5.16)
|
||||
picop!
|
||||
picopackage!
|
||||
rake (~> 13.0)
|
||||
rubocop (~> 1.21)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Picop
|
||||
# Picopackage
|
||||
|
||||
TODO: Delete this and the text below, and describe your gem
|
||||
|
||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/picop`. To experiment with that code, run `bin/console` for an interactive prompt.
|
||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/picopackge`. To experiment with that code, run `bin/console` for an interactive prompt.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
16
lib/picopackage.rb
Normal file
16
lib/picopackage.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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"
|
||||
require_relative "picopackage/fetch"
|
||||
require_relative "picopackage/cli"
|
||||
|
||||
module Picopackage
|
||||
class Error < StandardError; end
|
||||
class FileTooLargeError < StandardError; end
|
||||
class FetchError < StandardError; end
|
||||
class LocalModificationError < StandardError; end
|
||||
end
|
||||
@@ -15,13 +15,13 @@ module Picopackage
|
||||
dir = argv.first || '.'
|
||||
Picopackage::Scanner.scan(dir).each {|f| puts f.file_path }
|
||||
|
||||
when 'sign'
|
||||
when 'digest'
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: ppkg sign FILE"
|
||||
opts.banner = "Usage: ppkg digest FILE"
|
||||
end.parse!(argv)
|
||||
|
||||
file = argv.first
|
||||
Picopackage::SourceFile.from_file(file).sign
|
||||
Picopackage::SourceFile.from_file(file).digest!
|
||||
|
||||
when 'checksum'
|
||||
OptionParser.new do |opts|
|
||||
@@ -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, File.dirname(file), 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
|
||||
|
||||
@@ -4,33 +4,41 @@ require 'tempfile'
|
||||
require 'json'
|
||||
require 'debug'
|
||||
|
||||
module Picop
|
||||
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.digest!
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module Picop
|
||||
module Picopackage
|
||||
class Provider
|
||||
def self.for(url)
|
||||
PROVIDERS.each do |provider|
|
||||
@@ -28,7 +28,6 @@ module Picop
|
||||
class DefaultProvider
|
||||
MAX_SIZE = 1024 * 1024
|
||||
TIMEOUT = 10
|
||||
|
||||
attr_reader :url, :source_file
|
||||
|
||||
def self.handles_url?(url) = :maybe
|
||||
@@ -40,9 +39,9 @@ module Picop
|
||||
@content = nil
|
||||
end
|
||||
|
||||
def transform_url(url) = url
|
||||
|
||||
def body = @body ||= fetch
|
||||
def json_body = @json_body ||= JSON.parse(body)
|
||||
def transform_url(url) = url
|
||||
|
||||
def fetch
|
||||
begin
|
||||
@@ -66,62 +65,43 @@ module Picop
|
||||
|
||||
def handles_body?
|
||||
true
|
||||
rescue FileTooLargeError
|
||||
rescue FileTooLargeError, Net::HTTPError, RuntimeError => e
|
||||
false
|
||||
end
|
||||
|
||||
def content
|
||||
# Implement in subclass - this come from the `body`. Spliting content into code and metadata is the job of the SourceFile class
|
||||
raise NotImplementedError
|
||||
end
|
||||
# Implement in subclass - this come from the `body`.
|
||||
# Spliting content into code and metadata is the job of the SourceFile class
|
||||
def content = body
|
||||
|
||||
def filename
|
||||
# Implement in subclass - this should return the filename extracted from the body - if it exists, but not from the metadata
|
||||
raise NotImplementedError
|
||||
end
|
||||
def filename = File.basename @url
|
||||
|
||||
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
|
||||
|
||||
class GithubGistProvider < DefaultProvider
|
||||
def self.handles_url?(url) = url.match?(%r{gist\.github\.com})
|
||||
|
||||
def content = json_body["files"].values.first["content"]
|
||||
def filename = json_body["files"].values.first["filename"]
|
||||
def transform_url(url)
|
||||
gist_id = url[/gist\.github\.com\/[^\/]+\/([a-f0-9]+)/, 1]
|
||||
"https://api.github.com/gists/#{gist_id}"
|
||||
end
|
||||
|
||||
def content
|
||||
data = JSON.parse(body)
|
||||
file = data["files"].values.first["content"]
|
||||
end
|
||||
|
||||
def filename
|
||||
data = JSON.parse(body)
|
||||
data["files"].values.first["filename"]
|
||||
end
|
||||
end
|
||||
|
||||
class OpenGistProvider < DefaultProvider
|
||||
def handles_url?(url)
|
||||
:maybe
|
||||
end
|
||||
|
||||
def transform_url(url)
|
||||
"#{url}.json"
|
||||
end
|
||||
|
||||
def content
|
||||
data = JSON.parse(body)
|
||||
@content = data.dig("files",0, "content")
|
||||
end
|
||||
|
||||
def filename
|
||||
data = JSON.parse(body)
|
||||
data.dig("files",0, "filename")
|
||||
def handles_url?(url) = :maybe
|
||||
def transform_url(url) = "#{url}.json"
|
||||
def content = json_body.dig("files",0, "content")
|
||||
def filename = json_body.dig("files",0, "filename")
|
||||
def handles_body?
|
||||
content && filename
|
||||
rescue FileTooLargeError, Net::HTTPError, RuntimeError => e
|
||||
false
|
||||
end
|
||||
# If we successfully fetch the body, and the body contains content and a filename, then we can handle the body
|
||||
end
|
||||
|
||||
PROVIDERS = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module Picop
|
||||
module Picopackage
|
||||
module Scanner
|
||||
def self.scan(directory, pattern: "**/*")
|
||||
Dir.glob(File.join(directory, pattern)).select do |file|
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require "yaml"
|
||||
require "digest"
|
||||
|
||||
module Picop
|
||||
module Picopackage
|
||||
class SourceFile
|
||||
attr_reader :content, :metadata, :code, :original_path
|
||||
|
||||
@@ -9,12 +9,15 @@ module Picop
|
||||
|
||||
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 checksum
|
||||
instance.update_metadata(updated_metadata)
|
||||
|
||||
instance
|
||||
end
|
||||
|
||||
@@ -26,9 +29,17 @@ module Picop
|
||||
@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)}"
|
||||
|
||||
@@ -57,9 +68,9 @@ module Picop
|
||||
@content = generate_content
|
||||
end
|
||||
|
||||
def sign
|
||||
def digest!
|
||||
hash = checksum
|
||||
return puts "File already signed" if metadata['content_checksum'] == hash
|
||||
return puts "File already has a checksum" if metadata['content_checksum'] == hash
|
||||
|
||||
new_metadata = metadata.merge('content_checksum' => hash)
|
||||
update_metadata(new_metadata)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Picop
|
||||
VERSION = "0.1.0"
|
||||
module Picopackage
|
||||
VERSION = "0.2.0"
|
||||
end
|
||||
|
||||
@@ -8,17 +8,17 @@ Gem::Specification.new do |spec|
|
||||
spec.authors = ["Dan Milne"]
|
||||
spec.email = ["d@nmilne.com"]
|
||||
|
||||
spec.summary = "TODO: Write a short summary, because RubyGems requires one."
|
||||
spec.description = "TODO: Write a longer description or delete this line."
|
||||
spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
||||
spec.summary = "Picopackage Tool."
|
||||
spec.description = "Picopackage Tool for managing Picopackages."
|
||||
spec.homepage = "https://picopackage.org"
|
||||
spec.license = "MIT"
|
||||
spec.required_ruby_version = ">= 3.1.0"
|
||||
|
||||
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
||||
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
||||
|
||||
spec.metadata["homepage_uri"] = spec.homepage
|
||||
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
||||
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
||||
#spec.metadata["homepage_uri"] = spec.homepage
|
||||
#spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
||||
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
||||
|
||||
# Specify which files should be added to the gem when it is released.
|
||||
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
||||
|
||||
Reference in New Issue
Block a user