From 0cb98b339e20aa17a4bf4c9533e99deaaadd8439 Mon Sep 17 00:00:00 2001 From: Sadayuki Furuhashi Date: Thu, 11 Jan 2024 10:48:02 +0900 Subject: [PATCH 1/3] Add support for zip archive type for Windows and macOS Binary releases at https://github.com/sqldef/sqldef use tar.gz for linux while zip for windows and darwin. --- lib/sqldef.rb | 36 ++++++++++++++++++++++++++++-------- sqldef.gemspec | 2 ++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/sqldef.rb b/lib/sqldef.rb index 9c45491..6eb026d 100644 --- a/lib/sqldef.rb +++ b/lib/sqldef.rb @@ -7,6 +7,7 @@ require 'stringio' require 'uri' require 'zlib' +require 'zip' require_relative 'sqldef/version' module Sqldef @@ -24,6 +25,13 @@ module Sqldef ] private_constant :COMMANDS + OS_ARCHIVE = { + 'linux' => 'tar.gz', + 'windows' => 'zip', + 'darwin' => 'zip', + } + private_constant :OS_ARCHIVE + @bin = Dir.pwd class << self @@ -79,16 +87,26 @@ def download(command) return path if File.executable?(path) print("Downloading '#{command}' under '#{bin}'... ") - resp = get(build_url(command), code: 302) # Latest + url = build_url(command) + resp = get(url, code: 302) # Latest resp = get(resp['location'], code: 302) # vX.Y.Z resp = get(resp['location'], code: 200) # Binary - gzip = Zlib::GzipReader.new(StringIO.new(resp.body)) - Gem::Package::TarReader.new(gzip) do |tar| - unless file = tar.find { |f| f.full_name == command } - raise "'#{command}' was not found in the archive" + if url.end_with?('.zip') + Zip::File.open_buffer(resp.body) do |zip| + unless entry = zip.find_entry(command) + raise "'#{command}' was not found in the archive" + end + File.binwrite(path, zip.read(entry)) + end + else + gzip = Zlib::GzipReader.new(StringIO.new(resp.body)) + Gem::Package::TarReader.new(gzip) do |tar| + unless file = tar.find { |f| f.full_name == command } + raise "'#{command}' was not found in the archive" + end + File.binwrite(path, file.read) end - File.binwrite(path, file.read) end FileUtils.chmod('+x', path) @@ -109,8 +127,10 @@ def build_url(command) raise "Unexpected sqldef command: #{command}" end os = Etc.uname.fetch(:sysname).downcase - arch = GOARCH.fetch(Etc.uname.fetch(:machine)) - "https://github.com/k0kubun/sqldef/releases/latest/download/#{command}_#{os}_#{arch}.tar.gz" + archive = OS_ARCHIVE.fetch(os) + arch = Etc.uname.fetch(:machine) + goarch = GOARCH.fetch(arch, arch) + "https://github.com/k0kubun/sqldef/releases/latest/download/#{command}_#{os}_#{goarch}.#{archive}" end # TODO: Retry transient errors diff --git a/sqldef.gemspec b/sqldef.gemspec index 633c066..bc17682 100644 --- a/sqldef.gemspec +++ b/sqldef.gemspec @@ -17,6 +17,8 @@ Gem::Specification.new do |spec| spec.metadata['homepage_uri'] = spec.homepage spec.metadata['source_code_uri'] = spec.homepage + spec.add_runtime_dependency 'rubyzip' + # 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. spec.files = Dir.chdir(File.expand_path(__dir__)) do From 29708a2f5844fac761ee7ed42f200860eec9b25d Mon Sep 17 00:00:00 2001 From: Sadayuki Furuhashi Date: Thu, 11 Jan 2024 10:48:25 +0900 Subject: [PATCH 2/3] Follow the repository move from k0kubun to sqldef Github returns an additional 302 to follow the repository move which had made the downloading code broken. --- lib/sqldef.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sqldef.rb b/lib/sqldef.rb index 6eb026d..4d4bacb 100644 --- a/lib/sqldef.rb +++ b/lib/sqldef.rb @@ -130,7 +130,7 @@ def build_url(command) archive = OS_ARCHIVE.fetch(os) arch = Etc.uname.fetch(:machine) goarch = GOARCH.fetch(arch, arch) - "https://github.com/k0kubun/sqldef/releases/latest/download/#{command}_#{os}_#{goarch}.#{archive}" + "https://github.com/sqldef/sqldef/releases/latest/download/#{command}_#{os}_#{goarch}.#{archive}" end # TODO: Retry transient errors From 00acf81b87d2024ecaea494ed1b3521164ee8165 Mon Sep 17 00:00:00 2001 From: Sadayuki Furuhashi Date: Thu, 11 Jan 2024 10:49:17 +0900 Subject: [PATCH 3/3] Follow redirects during downloading This change is optional as long as the repository is kept at sqldef/sqldef and GitHub doesn't change how it redirects but works as a nice safeguard. --- lib/sqldef.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/sqldef.rb b/lib/sqldef.rb index 4d4bacb..84c1592 100644 --- a/lib/sqldef.rb +++ b/lib/sqldef.rb @@ -88,9 +88,7 @@ def download(command) print("Downloading '#{command}' under '#{bin}'... ") url = build_url(command) - resp = get(url, code: 302) # Latest - resp = get(resp['location'], code: 302) # vX.Y.Z - resp = get(resp['location'], code: 200) # Binary + resp = get(url, code: 200, max_retries: 4) if url.end_with?('.zip') Zip::File.open_buffer(resp.body) do |zip| @@ -134,15 +132,19 @@ def build_url(command) end # TODO: Retry transient errors - def get(url, code: nil) + def get(url, code: nil, max_retries:) uri = URI.parse(url) - Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http| + resp = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http| http.get("#{uri.path}?#{uri.query}") - end.tap do |resp| - if code && resp.code != code.to_s - raise "Expected '#{url}' to return #{code}, but got #{resp.code}: #{resp.body}" - end end + if resp.is_a?(Net::HTTPRedirection) && max_retries > 0 + # Follow redirects that lead to the current repository (if sqldef/sqldef is moved), + # Latest, vX.Y.Z, and to the binary + return get(resp['location'], code: code, max_retries: max_retries - 1) + elsif code && resp.code != code.to_s + raise "Expected '#{url}' to return #{code}, but got #{resp.code}: #{resp.body}" + end + resp end end end