module OSDN; module CLI; module Command
class Relfile < FrsBase
+ attr_accessor :target_proj, :target_package, :target_release, :visibility, :force_digest, :show_progress
+
def help
puts "#{$0} relfile [opts] [list]"
- puts "#{$0} relfile [opts] create <target-file>"
+ puts "#{$0} relfile [opts] create <target-file> [target-files...]"
puts "#{$0} relfile [opts] update <numeric-file-id>"
puts "#{$0} relfile [opts] delete <numeric-file-id>"
puts "Options:"
puts " -f --format=<pretty|json> Set output format"
puts " -p --project=<project> Target project (numeric id or name)"
- puts " --package=<project> Target package (numeric id)"
- puts " --release=<project> Target release (numeric id)"
+ puts " --package=<package-id> Target package (numeric id)"
+ puts " --release=<release-id> Target release (numeric id)"
puts " -v --visibility=<public|private|hidden>"
+ puts " --force-digest Calc local file digest forcely"
+ puts " --progress Force to show upload progress"
+ puts " --no-progress Force to hide upload progress"
+ puts " --bwlimit=RATE Limit bandwidth (in KB)"
end
def self.description
[ '--package', GetoptLong::REQUIRED_ARGUMENT ],
[ '--release', GetoptLong::REQUIRED_ARGUMENT ],
[ '--visibility', '-v', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--force-digest', GetoptLong::NO_ARGUMENT],
+ [ '--progress', GetoptLong::NO_ARGUMENT],
+ [ '--no-progress', GetoptLong::NO_ARGUMENT],
+ [ '--bwlimit', GetoptLong::REQUIRED_ARGUMENT ],
)
opts.each do |opt, arg|
case opt
when '--release'
arg.empty? or
@target_release = arg
+ when '--force-digest'
+ @force_digest = true
when '--visibility'
unless %w(public private hidden).member?(arg)
logger.fatal "Invalid visibility status: #{arg}"
exit
end
@visibility = arg
+ when '--progress'
+ @show_progress = true
+ when '--no-progress'
+ @show_progress = false
+ when '--bwlimit'
+ arg.to_i != 0 and
+ OSDN::CLI._rate_limit = arg.to_i * 1024
end
end
end
end
def create
- filename = ARGV.shift
- if !filename
+ if ARGV.empty? || ARGV.first == ""
logger.fatal "Target filename is missing."
help
return
end
+
+ ARGV.each do |f|
+ create_one(f)
+ end
+ end
+
+ def calc_file_digest(filename)
file = Pathname('.') + filename
- logger.debug "Calculating digest for #{file}..."
- digests = {
- sha256: hexdigest(Digest::SHA256, file),
- sha1: hexdigest(Digest::SHA1, file),
- md5: hexdigest(Digest::MD5, file),
- }
+ vars = load_variables(file.dirname)
+ digests = nil
+ if !@force_digest && vars.local_file_info &&
+ vars.local_file_info[file.basename.to_s]
+ finfo = vars.local_file_info[file.basename.to_s]
+ if finfo.filesize == file.size && finfo.mtime == file.mtime
+ digests = vars.local_file_info[file.basename.to_s].digests
+ end
+ end
+
+ unless digests
+ logger.info "Calculating digest for #{file}..."
+ digests = {
+ sha256: hexdigest(Digest::SHA256, file),
+ sha1: hexdigest(Digest::SHA1, file),
+ md5: hexdigest(Digest::MD5, file),
+ }
+ update_variables file.dirname, {local_file_info: {file.basename.to_s => {digests: digests, mtime: file.mtime, filesize: file.size}}}
+ end
+ end
+
+ def create_one(filename)
+ file = Pathname('.') + filename
+ calc_file_digest file
+ vars = load_variables(file.dirname)
fio = file.open
- logger.level <= Logger::INFO and
+ logger.level <= Logger::INFO && @show_progress != false || @show_progress and
OSDN::CLI._show_progress = true
- logger.info "Starting upload #{file}..."
- f = api.create_release_file target_proj, target_package, target_release, fio, visibility: @visibility
- fio.close
- OSDN::CLI._show_progress = false
- if digests.find { |type, dig| dig != f.send("digest_#{type}") }
- logger.error "File digests are mismatch! Upload file #{file} may be broken! Please check."
+ logger.info "Starting upload #{file} (#{file.size} bytes)..."
+ max_upload_tries = 5
+ upload_tries = 0
+ remote_file = nil
+ begin
+ upload_tries += 1
+ remote_file = api.create_release_file target_proj, target_package, target_release, fio, visibility: @visibility
+ rescue OSDNClient::ApiError => e
+ if max_upload_tries - upload_tries <= 0
+ logger.error "Max upload attempts (#{max_upload_tries}) has been exceeded, give up!"
+ raise e
+ elsif [0, 100, 502].member?(e.code.to_i)
+ fio.rewind
+ logger.error "Upload error (#{e.code} #{e.message}), retrying (#{upload_tries}/#{max_upload_tries})..."
+ sleep 10
+ retry
+ else
+ raise e
+ end
+ ensure
+ fio.close
+ OSDN::CLI._show_progress = false
+ end
+
+ if vars.local_file_info[file.basename.to_s].digests.find { |type, dig| rd = remote_file.send("digest_#{type}"); rd && rd != '' && rd != dig }
+ logger.error "File digest mismatch! Uploaded file #{file} may be broken! Please check."
+ elsif file.size != remote_file.size
+ logger.error "File size mismatch! Uploaded file #{file} may be broken! Please check."
else
logger.info "Upload complete."
end
- puts format_file(f)
+ puts format_file(remote_file)
+ remote_file
end
def update