Class: OSDN::CLI::Command::FrsUpload

Inherits:
Base
  • Object
show all
Defined in:
lib/osdn/cli/command/frs_upload.rb

Instance Attribute Summary

Attributes inherited from Base

#credential, #format, #logger

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#credential_path, #initialize, #load_credential, #load_variables, #set_client_token, #set_credential, #update_token, #update_variables, #write_credential, #write_variables

Constructor Details

This class inherits a constructor from OSDN::CLI::Command::Base

Class Method Details

.descriptionObject



86
87
88
# File 'lib/osdn/cli/command/frs_upload.rb', line 86

def self.description
  "Upload local file tree and create package/release implicitly."
end

Instance Method Details

#helpObject



3
4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/osdn/cli/command/frs_upload.rb', line 3

def help
  puts "#{$0} frs_upload [opts] [target_dir]"
  puts "Options:"
  puts "  -n --dry-run               Do noting (use with global -v to inspect)"
  puts "  -p --project=<project>     Target project (numeric id or name)"
  puts "     --package=<package-id>  Target package (numeric id)"
  puts "     --release=<release-id>  Target release (numeric id)"
  puts "  -v --visibility=<public|private|hidden>"
  puts "                             Default visibility for newly created items"
  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

#process_file(file, rdir, rinfo) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/osdn/cli/command/frs_upload.rb', line 161

def process_file(file, rdir, rinfo)
  if file.directory?
    logger.error "Skip direcotry #{file}"
    return false
  end

  vars = load_variables(rdir)
  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[:size] == 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 rdir, {local_file_info: {file.basename.to_s => {digests: digests, mtime: file.mtime, size: file.size}}}
  end
  if remote_f = rinfo.files.find { |f| f.name == file.basename.to_s }
    if digests.find { |type, dig| dig != remote_f.send("digest_#{type}") }
      logger.error "#{file} was changed from remote file! Please delete remote file before uploading new one."
    end
    logger.info "Skip already uploaded file '#{file}'"
    return
  end

  logger.info "Uploading file #{file} (#{file.size} bytes)"
  if @dry_run
    finfo = Hashie::Mash.new id: '(dry-run)', url: '(dry-run)'
  else
    logger.level <= Logger::INFO && @show_progress != false || @show_progress and
      OSDN::CLI._show_progress = true
    fio = file.open
    logger.info "Starting upload #{file}..."
    max_upload_tries = 5
    upload_tries = 0
    begin
      upload_tries += 1
      finfo = api.create_release_file target_proj, target_package(rdir), target_release(rdir), 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
      OSDN::CLI._show_progress = false
      fio.close
    end
    if digests.find { |type, dig| dig != finfo.send("digest_#{type}") }
      logger.error "File digests are mismatch! Upload file #{file} may be broken! Please check."
    else
      logger.info "Upload complete."
    end
  end
  $stdout.puts "New file '#{file}' has been uploaded; #{finfo.url}"
end

#process_package(pdir) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/osdn/cli/command/frs_upload.rb', line 90

def process_package(pdir)
  if cur_pkgid = load_variables(pdir).package_id
    # check package existance on server
    begin
      api.get_package target_proj, target_package(pdir)
    rescue OSDNClient::ApiError => e
      begin
        err = JSON.parse(e.response_body)
      rescue
        raise e
      end
      if err['status'] == 404
        logger.warn "Package ##{cur_pkgid} has been deleted on server and local directory '#{pdir}' remains. You can delete the local directory or delete '#{pdir}/.osdn.vars' file to create a package again with new ID."
        return false
      end
      raise e
    end
  else
    logger.info "Createing new package '#{pdir.basename}'"
    if @dry_run
      pinfo = Hashie::Mash.new id: '(dry-run)', name: pdir.basename, url: '(dry-run)'
    else
      pinfo = api.create_package target_proj, pdir.basename, visibility: @visibility
      update_variables pdir, package_id: pinfo.id
    end
    $stdout.puts "New package '#{pinfo.name}' has been created; #{pinfo.url}"
  end

  Pathname.glob(pdir + '*').sort.each do |rdir|
    process_release(rdir)
  end
end

#process_release(rdir) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/osdn/cli/command/frs_upload.rb', line 123

def process_release(rdir)
  if !rdir.directory?
    logger.warn "Skip normal file '#{rdir}' in release level"
    return false
  end

  vars = load_variables(rdir)
  rinfo = nil
  if vars.release_id
    begin
      rinfo = api.get_release target_proj, target_package(rdir), target_release(rdir)
    rescue OSDNClient::ApiError => e
      begin
        err = JSON.parse(e.response_body)
      rescue
        raise e
      end
      if err['status'] == 404
        logger.warn "Release ##{vars.release_id} has been deleted on server and local directory '#{rdir}' remains. You can delete the local directory or delete '#{rdir}/.osdn.vars' file to create a release again with new ID."
        return false
      end
      raise e
    end
  else
    logger.info "Createing new release '#{rdir.basename}'"
    if @dry_run
      rinfo = Hashie::Mash.new id: '(dry-run)', name: rdir.basename, url: '(dry-run)', files: []
    else
      rinfo = api.create_release target_proj, target_package(rdir), rdir.basename, visibility: @visibility
      update_variables rdir, release_id: rinfo.id
    end
    $stdout.puts "New release '#{rinfo.name}' has been created; #{rinfo.url}"
  end
  Pathname.glob(rdir + '*').sort.each do |file|
    process_file(file, rdir, rinfo)
  end
end

#process_targetObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/osdn/cli/command/frs_upload.rb', line 68

def process_target
  proj_info = api.get_project target_proj # check project existance

  vars = load_variables(@target_dir)
  parent_vars = load_variables(@target_dir.parent)

  if @target_release || vars.release_id ||
     parent_vars.package_id && !vars.release_id # new release case...
    process_release(@target_dir)
  elsif @target_package || vars.package_id
    process_package(@target_dir)
  else
    Pathname.glob(@target_dir+'*').sort.each do |pdir|
      process_package(pdir)
    end
  end
end

#runObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/osdn/cli/command/frs_upload.rb', line 18

def run
  update_token
  opts = GetoptLong.new(
    [ '--dry-run', '-n', GetoptLong::NO_ARGUMENT ],
    [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--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 '--project'
      arg.empty? or
        @target_proj = arg
    when '--release'
      arg.empty? or
        @target_release = arg
    when '--package'
      arg.empty? or
        @target_package = arg
    when '--visibility'
      unless %w(public private hidden).member?(arg)
        logger.fatal "Invalid visibility status: #{arg}"
        exit
      end
      @visibility = arg
    when '--force-digest'
      @force_digest = true
    when '--dry-run'
      @dry_run = true
    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

  (ARGV.empty? ? ['.'] : ARGV).each do |d|
    @target_dir = Pathname.new(d)
    process_target
  end
end