OSDN Git Service

Check file digest.
[osdn-codes/osdn-cli.git] / lib / osdn / cli / command / frs_upload.rb
1 require 'digest'
2
3 module OSDN; module CLI; module Command
4   class FrsUpload < Base
5     def help
6       puts "#{$0} frs_upload [opts] [target_dir]"
7       puts "Options:"
8       puts "  -n --dry-run               Do noting (use with global -v to inspect)"
9       puts "  -p --project=<project>     Target project (numeric id or name)"
10       #puts "     --package=<project>     Target package (numeric id)"
11       #puts "     --release=<project>     Target release (numeric id)"
12       puts "  -v --visibility=<public|private|hidden>"
13       puts "                             Default visibility for newly created items"
14     end
15
16     def run
17       update_token
18       opts = GetoptLong.new(
19         [ '--dry-run', '-n', GetoptLong::NO_ARGUMENT ],
20         [ '--project', '-p', GetoptLong::REQUIRED_ARGUMENT ],
21         [ '--release', '-r', GetoptLong::REQUIRED_ARGUMENT ],
22         [ '--visibility', '-v', GetoptLong::REQUIRED_ARGUMENT ],
23       )
24       opts.each do |opt, arg|
25         case opt
26         when '--project'
27           arg.empty? or
28             @target_proj = arg
29         #when '--release'
30         #  arg.empty? or
31         #    @target_release = arg
32         #when '--package'
33         #  arg.empty? or
34         #    @target_package = arg
35         when '--visibility'
36           unless %w(public private hidden).member?(arg)
37             logger.fatal "Invalid visibility status: #{arg}"
38             exit
39           end
40           @visibility = arg
41         when '--dry-run'
42           @dry_run = true
43         end
44       end
45
46       @target_dir = Pathname.new(ARGV.shift || '.')
47       proj_info = api.get_project target_proj # check project existance
48
49       Pathname.glob(@target_dir+'*').each do |pdir|
50         unless load_variables(pdir).package_id
51           logger.info "Createing new package '#{pdir.basename}'"
52           if @dry_run
53             pinfo = Hashie::Mash.new id: '(dry-run)', name: pdir.basename, url: '(dry-run)'
54           else
55             pinfo = api.create_package target_proj, pdir.basename, visibility: @visibility
56             update_variables pdir, package_id: pinfo.id
57           end
58           $stdout.puts "New package '#{pinfo.name}' has been created; #{pinfo.url}"
59         end
60
61         Pathname.glob(pdir + '*').each do |rdir|
62           vars = load_variables(rdir)
63           rinfo = nil
64           if vars.release_id
65             rinfo = api.get_release target_proj, target_package(rdir), target_release(rdir)
66           else vars.release_id
67             logger.info "Createing new release '#{rdir.basename}'"
68             if @dry_run
69               rinfo = Hashie::Mash.new id: '(dry-run)', name: rdir.basename, url: '(dry-run)', files: []
70             else
71               rinfo = api.create_release target_proj, target_package(rdir), rdir.basename, visibility: @visibility
72               update_variables rdir, release_id: rinfo.id
73             end
74             $stdout.puts "New release '#{rinfo.name}' has been created; #{rinfo.url}"
75           end
76           
77           Pathname.glob(rdir + '*').each do |file|
78             if file.directory?
79               logger.error "Skip direcotry #{file}"
80               next
81             end
82
83             digests = {
84               sha256: hexdigest(Digest::SHA256, file),
85               sha1:   hexdigest(Digest::SHA1, file),
86               md5:    hexdigest(Digest::MD5, file),
87             }
88             if remote_f = rinfo.files.find { |f| f.name == file.basename.to_s }
89               if digests.find { |type, dig| dig != remote_f.send("digest_#{type}") }
90                 logger.error "#{file} was changed from remote file! Please delete remote file before uploading new one."
91               end
92               logger.warn "Skip already uploaded file '#{file}'"
93             else
94               logger.info "Uploading file #{file} (#{file.size} bytes)"
95               if @dry_run
96                 finfo = Hashie::Mash.new id: '(dry-run)', url: '(dry-run)'
97               else
98                 # TODO: show progress bar!
99                 fio = file.open
100                 finfo = api.create_release_file target_proj, target_package(rdir), target_release(rdir), fio, visibility: @visibility
101                 fio.close
102                 logger.info "Upload completed."
103               end
104               $stdout.puts "New file '#{file}' has been uploaded; #{finfo.url}"
105             end
106           end
107         end
108       end
109     end
110
111     def self.description
112       "Upload local file tree and create package/release implicitly."
113     end
114
115     private
116     def target_proj
117       @target_proj and return @target_proj
118       vars = load_variables(@target_dir)
119       vars.project && !vars.project.empty? and
120         return vars.project
121       logger.fatal "No target project is specified."
122       exit
123     end
124
125     def target_package(dir)
126       @target_package and return @target_package
127       vars = load_variables(dir)
128       vars.package_id && !vars.package_id.to_s.empty? and
129         return vars.package_id
130       logger.fatal "No target package is specified."
131       exit
132     end
133
134     def target_release(dir)
135       @target_release and return @target_release
136       vars = load_variables(dir)
137       vars.release_id && !vars.release_id.to_s.empty? and
138         return vars.release_id
139       logger.fatal "No target release is specified."
140       exit
141     end
142
143     def api
144       OSDNClient::ProjectApi.new
145     end
146
147     def hexdigest(klass, file)
148       fio = file.open
149       dig = klass.new
150       while buf = fio.read(1024*1024) and buf.length > 0
151         dig << buf
152       end
153       fio.close
154       dig.hexdigest
155     end
156   end
157 end; end; end