ghcurl 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (10) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/Gemfile +6 -6
  4. data/LICENSE +22 -22
  5. data/README.md +100 -103
  6. data/Rakefile +3 -3
  7. data/bin/ghcurl +584 -525
  8. data/ghcurl.gemspec +40 -0
  9. data/lib/ghcurl.rb +78 -71
  10. metadata +10 -8
data/bin/ghcurl CHANGED
@@ -1,525 +1,584 @@
1
- #!/usr/bin/env ruby
2
- # ------------------------------------------------------
3
- # File : ghcurl.rb
4
- # Authors : ccmywish <ccmywish@qq.com>
5
- # Created on : <2022-04-12>
6
- # Last modified : <2022-04-30>
7
- #
8
- # ghcurl:
9
- #
10
- # Download files and install from Github releases
11
- #
12
- # ------------------------------------------------------
13
-
14
- require 'ghcurl'
15
- require 'highline'
16
- require 'cliswitch'
17
- require 'fileutils'
18
-
19
- module Ghcurl
20
-
21
- WAREHOUSE = File.expand_path("~/.cache/ghcurl")
22
- BIN_PATH = ENV['GHCURL_BIN_PATH'].chomp('/') || "/usr/local/bin"
23
- HL = HighLine.new
24
-
25
- class Error < StandardError; end
26
-
27
-
28
- def bold(str) "\e[1m#{str}\e[0m" end
29
- def green(str) "\e[32m#{str}\e[0m" end
30
- def blue(str) "\e[34m#{str}\e[0m" end
31
-
32
-
33
- def log(msg)
34
- puts blue(bold("ghcurl: #{msg}"))
35
- end
36
-
37
-
38
- #
39
- # @return absolute download path
40
- #
41
- def curl(url, name, to)
42
- if to == nil
43
- to = WAREHOUSE + '/' + name
44
- elsif test 'd', to
45
- to = to.chomp('/') + '/' + name
46
- elsif to.include?('/')
47
- # noop
48
- else
49
- # just a rename in WAREHOUSE
50
- to = WAREHOUSE + '/' + name
51
- end
52
-
53
- cmd = "curl -L #{url} --create-dirs -o #{to}"
54
- status = system cmd
55
- if status == false || status.nil?
56
- log "Download error!"
57
- exit
58
- end
59
- log "Downloaded to #{to}"
60
- return to
61
- end
62
-
63
-
64
- def get_filters
65
-
66
- arches = [
67
- ['amd64', 'x86_64', 'x64'],
68
- ['386', 'i686'],
69
- ['arm64', 'aarch64'],
70
- ['armv6', 'arm'],
71
- ]
72
-
73
- oses = [
74
- ['linux'],
75
- ['freebsd'],
76
- ['mac', 'apple', 'darwin'],
77
- ['windows']
78
- ]
79
-
80
- fs = RUBY_PLATFORM.split('-')
81
- os = ""
82
- arch = ""
83
- fs.each do |f|
84
- case f
85
- when 'linux' then os = 'linux'
86
- when 'mac','macOS' then os = 'mac'
87
- when 'ucrt', 'mingw', 'windows' then os = 'windows'
88
- when 'x86_64' then arch = 'x86_64'
89
- when 'x86' then arch = 'x86'
90
- when 'amd64' then arch = 'amd64'
91
- when 'arm64' then arch = 'arm64'
92
- when 'armv6' then arch = 'armv6'
93
- end
94
- end
95
-
96
- approval, refuse = [], []
97
- # [os, arch].each { approval << _1 unless _1.nil? }
98
-
99
- if os
100
- i = oses.each_with_index do |type, index|
101
- break index if type.include? os
102
- end
103
- if !i.nil?
104
- tmp = oses.dup
105
- tmp.delete_at(i)
106
- refuse.concat tmp.flatten
107
- end
108
- end
109
-
110
-
111
- if arch
112
- i = arches.each_with_index do |type, index|
113
- break index if type.include? arch
114
- end
115
- if !i.nil?
116
- tmp = arches.dup
117
- tmp.delete_at(i)
118
- refuse.concat tmp.flatten
119
- end
120
- end
121
-
122
- # Debug
123
- # puts "=> Approval and refuse"
124
- # p [os, arch]
125
- # p refuse
126
-
127
- # Now we only refuse others
128
- return refuse
129
-
130
- end
131
-
132
-
133
- def download(repo, regexp, version: nil, download_to: nil)
134
-
135
- require 'octokit'
136
-
137
- # adjust repo name : user/repo
138
- if repo =~ /^https:\/\/github.com/
139
- require 'uri'
140
- uri = URI(repo)
141
- # index 1, take 2
142
- repo = uri.path.split('/')[1,2].join('/')
143
- elsif !repo.include?('/')
144
- got_repo = DEFAULT_WARES[repo.to_sym]
145
- if not got_repo
146
- user = HL.ask "Who developed the awesome #{repo}? "
147
- repo = user + '/' + repo
148
- else
149
- repo = got_repo
150
- log "Use the popular repo #{repo}"
151
- end
152
- end
153
-
154
- log "checking..."
155
- begin
156
- unless version
157
- doc = Octokit::Client.new.latest_release(repo)
158
- else
159
- doc = Octokit::Client.new.release_for_tag(repo, 'v' + version)
160
- end
161
- rescue Octokit::NotFound
162
- log "Not found #{repo} v#{version} !"
163
- exit
164
- rescue StandardError => e
165
- log e
166
- exit
167
- end
168
-
169
- links = doc.to_hash[:assets]
170
- if links.empty?
171
- log <<~EOE
172
- The search result is empty, check the args!
173
- repo: #{repo}
174
- version: #{version ? version:'nil'}
175
-
176
- Maybe there's no assets in this release
177
- EOE
178
- exit 0
179
- end
180
-
181
- links = links.map { _1[:browser_download_url] }
182
-
183
-
184
- if regexp
185
- filtered = links.select do
186
- _1 =~ /#{regexp}/
187
- end
188
- else
189
- refuse = get_filters()
190
- filtered = links.select do |l|
191
- refuse.all? do |f|
192
- l !~ /#{f}/
193
- end
194
- end
195
- end
196
-
197
-
198
- if filtered.size == 1
199
- link = filtered[0].split('/').last
200
- else
201
- if filtered.size == 0
202
- links_for_user = links.map { _1.split('/').last }
203
- else
204
- links_for_user = filtered.map { _1.split('/').last }
205
- end
206
- link = HL.choose do |menu|
207
- menu.index_color = :rgb_77bbff
208
- menu.prompt = "Which one do you want to download? "
209
- menu.choices( *links_for_user )
210
- end
211
- end
212
-
213
- url = links[0].split('/')[0..-2].join('/') + '/' + link
214
-
215
- log "Downloading #{url}"
216
-
217
- dl_name = link.split('/').last
218
-
219
- return curl(url, dl_name, download_to)
220
- end
221
-
222
-
223
- #
224
- # This function is a little confusing
225
- #
226
- # target: The absolute path to a downloaded file
227
- # name : The software name
228
- #
229
- def install(target, rename_as: nil, install_to: nil)
230
-
231
- if target.end_with?('.deb')
232
- log "Install the deb package"
233
- system "sudo dpkg -i #{target}"
234
- return
235
- end
236
-
237
- if target.end_with?('.rpm')
238
- log "Install the rpm package"
239
- system "sudo rpm -i #{target}"
240
- return
241
- end
242
-
243
-
244
- if install_to.nil?
245
- install_to = BIN_PATH
246
- end
247
-
248
-
249
- #
250
- # Handle zip situation
251
- #
252
- zip_flag = false
253
-
254
- if target.match?(/\.zip$/) or target.match?(/\.tar\.(\w){1,3}/)
255
-
256
- zip_flag = true
257
-
258
- # unzipped to dir
259
- unzip_dir = ""
260
- # unzipped files
261
- files = ""
262
- # the target unzipped file name (this includes the path prefix)
263
- unzip_name = ""
264
-
265
- # unzip
266
- if target.match? /\.zip$/
267
- log "Unzip zip file"
268
- unzip_dir = target.chomp('.zip')
269
- system "unzip -q #{target} -d #{unzip_dir}"
270
- end
271
-
272
- # .gz, .bz2, .xz
273
- if target.match? /\.tar\.(\w){1,3}/
274
- log "Unzip tar file"
275
- unzip_dir = target.split('.')[0..-3].join('.')
276
- FileUtils.mkdir_p(unzip_dir)
277
- system "tar xf #{target} --directory=#{unzip_dir}"
278
- end
279
-
280
-
281
- #
282
- # @return [Array] file names in the dir
283
- #
284
- def _iterate_dir(dir)
285
- result = []
286
- chd = Dir.children(dir)
287
- chd_files = chd.select{|f| File.file? "#{dir}/#{f}"}
288
-
289
- chd_files.each { result << "#{dir}/#{_1}"[2..] }
290
-
291
- chd_dirs = chd.select{|d| File.directory?("#{dir}/#{d}") && ( d != '.git')}
292
- chd_dirs.each do |d|
293
- sub_dir = "#{dir}/#{d}"
294
- result.concat _iterate_dir(sub_dir)
295
- end
296
- result
297
- end
298
-
299
-
300
- Dir.chdir unzip_dir do
301
- files = _iterate_dir('.')
302
- end
303
-
304
-
305
- if files.size > 1
306
- unzip_name = HL.choose do |menu|
307
- menu.index_color = :rgb_77bbff
308
- menu.prompt = "Which one do you want to install? "
309
- menu.choices( *files )
310
- end
311
- else
312
- unzip_name = files[0]
313
- end
314
-
315
- end # end of zipped file handle
316
-
317
-
318
- # Get software real name
319
- if zip_flag
320
- name = unzip_name.split('/').last
321
- else
322
- name = target.split('/').last
323
- end
324
-
325
-
326
- if (name.size > 10 ||
327
- name.include?('.') ||
328
- name.include?('-') ||
329
- name.include?('_')) && (not rename_as)
330
- log "Do you want to rename the '#{name}'?"
331
- re = HL.ask "Input new name, or leave it blank to not rename."
332
- if !re.empty?
333
- rename_as = re
334
- end
335
- end
336
-
337
-
338
- if zip_flag
339
- # Now:
340
- # target is /home/xx/xx.zip
341
- # unzip_dir is /home/xx/xx
342
- # unzip_file is abc/cde/file (new target)
343
- target = unzip_dir + '/' + unzip_name
344
- end
345
-
346
- log "Renamed as '#{rename_as}'" if rename_as
347
-
348
-
349
- case RUBY_PLATFORM
350
- when /ucrt/i, /mingw/i
351
- install_on_windows(target, name, install_to, rename_as)
352
- else
353
- install_on_nix(target, name, install_to, rename_as)
354
- end
355
- end
356
-
357
-
358
- #
359
- # @param target [String] the absulute path of to be installed software
360
- # @param name [String] the name of the software
361
- #
362
- def install_on_nix(target, name, install_to, rename_as)
363
- install_to = install_to.chomp('/')
364
-
365
- if test 'd', install_to
366
- log "Ready to install #{name}"
367
- if rename_as
368
- dest = "#{install_to}/#{rename_as}"
369
- system "sudo cp #{target} " + dest
370
- system "sudo chmod +x " + dest
371
- log "Installed as " + dest
372
- else
373
- cmd = "sudo install -Dt #{install_to} -m 755 #{target} "
374
- system cmd
375
- log "Install #{name} to " + install_to
376
- end
377
- log "Install finish!"
378
- else
379
- log "#{install_to} is not a directory!"
380
- end
381
-
382
- end
383
-
384
-
385
- def install_on_windows(target, name, install_to, rename_as)
386
- log "Sorry, not implemented yet on Windows! Can you help?"
387
- end
388
-
389
-
390
- #
391
- # For -l option
392
- #
393
- def list_wares
394
- FileUtils.mkdir_p(WAREHOUSE)
395
- puts blue("ghcurl: #{WAREHOUSE}")
396
- Dir.children(WAREHOUSE).each_with_index do |dict,i|
397
- puts "#{blue(i+1)}. #{bold(green(dict))}"
398
- end
399
- end
400
-
401
-
402
- #
403
- # For -d option
404
- #
405
- def delete_wares(name)
406
- begin
407
-
408
- if name.nil?
409
- re = HL.ask "Do you want to delete all downloaded files? [Y/n]"
410
- case re.downcase
411
- when '','y','ye','yes','true'
412
- FileUtils.rm_rf WAREHOUSE
413
- log "Delete all done"
414
- end
415
- else
416
- FileUtils.rm WAREHOUSE + '/' + name
417
- log "Delete #{name} done"
418
- end
419
-
420
- rescue Exception => e
421
- puts bold(red("ghcurl: #{e}"))
422
- list_wares
423
- end
424
-
425
- end # end def delete_wares
426
-
427
-
428
- def help
429
- puts <<~EOC
430
- ghcurl (v#{VERSION}): Download files and install from Github releases
431
-
432
- Default install to env 'GHCURL_BIN_PATH' or /usr/local/bin
433
-
434
- usage:
435
- ghcurl [user]/repo [regexp] => Search latest version with regexp to download
436
- ghcurl repo [re] -v tag => Download a specific tag version
437
- ghcurl repo [re] -o [path] => Download into path or rename
438
- ghcurl repo [re] -i [path] => Download and install to path
439
- ghcurl repo [re] -o [path] -i => Download into path and install
440
- ghcurl repo [re] -i -r name => Download and install as 'name'
441
- ghcurl -l => List downloaded files
442
- ghcurl -d [name] => Delete a downloaded file or all
443
- ghcurl -h => Print this help
444
-
445
- example:
446
- ghcurl bat => Search sharkdp/bat the latest
447
- ghcurl cli deb -i => Search cli/cli /deb/
448
- ghcurl rbspy/rbspy 'x86_64.*linux' -v0.11.1
449
- ghcurl dlvhdr/gh-dash linux-amd64 -i -r 'gd'
450
-
451
- EOC
452
- end
453
-
454
- end
455
-
456
-
457
-
458
- ####################
459
- # main: CLI Handling
460
- ####################
461
- extend Ghcurl
462
-
463
- class Ghcurl::CLI < CliSwitch
464
- option name: 'install', short: '-i', arg_required: 'optional'
465
- option name: 'output', short: '-o', long: '--output', arg_required: 'required'
466
- option name: 'rename', short: '-r', arg_required: 'required'
467
- option name: 'version', short: '-v', arg_required: 'required'
468
- option name: 'help', short: '-h', long: '--help', arg_required: 'noarg'
469
- option name: 'list', short: '-l', arg_required: 'noarg'
470
- option name: 'delete', short: '-d', arg_required: 'optional'
471
- end
472
-
473
- args, opts = Ghcurl::CLI.new.parse(ARGV)
474
-
475
- if args.empty? and opts.empty?
476
- help
477
- exit
478
- end
479
-
480
-
481
- repo_or_name, regexp = args[0], args[1]
482
- version = nil
483
- need_install = false
484
- download_to = nil
485
- install_to = nil
486
- rename_as = nil
487
-
488
- opts.each do
489
- case _1.name
490
- when 'help'
491
- help
492
- exit
493
- when 'list'
494
- list_wares
495
- exit
496
- when 'delete'
497
- delete_wares(_1.next_arg)
498
- exit
499
- when 'version'
500
- version = _1.next_arg
501
- when 'output'
502
- download_to = _1.next_arg
503
- when 'install'
504
- need_install = true
505
- install_to = _1.next_arg
506
- when 'rename'
507
- rename_as = _1.next_arg
508
- end
509
- end
510
-
511
- # Debug
512
- # p repo_or_name
513
- # p regexp
514
- # p need_install
515
- # p download_to
516
- # p install_to
517
- # p rename_as
518
-
519
- begin
520
- ware = download(repo_or_name, regexp, version: version, download_to: download_to)
521
- if need_install
522
- install(ware, rename_as: rename_as, install_to: install_to)
523
- end
524
- rescue Interrupt
525
- end
1
+ #!/usr/bin/env ruby
2
+ # ------------------------------------------------------
3
+ # File : ghcurl.rb
4
+ # Authors : ccmywish <ccmywish@qq.com>
5
+ # Created on : <2022-04-12>
6
+ # Last modified : <2022-11-13>
7
+ #
8
+ # ghcurl:
9
+ #
10
+ # Download files and install from Github releases
11
+ #
12
+ # ------------------------------------------------------
13
+
14
+ require 'ghcurl'
15
+ require 'highline'
16
+ require 'cliswitch'
17
+ require 'fileutils'
18
+
19
+ module Ghcurl
20
+
21
+ WAREHOUSE = File.expand_path("~/.cache/ghcurl")
22
+ if ENV['GHCURL_BIN_PATH']
23
+ BIN_PATH = ENV['GHCURL_BIN_PATH'].chomp('/')
24
+ else
25
+ BIN_PATH = "/usr/local/bin"
26
+ end
27
+ HL = HighLine.new
28
+
29
+ class Error < StandardError; end
30
+
31
+
32
+ def bold(str) "\e[1m#{str}\e[0m" end
33
+ def green(str) "\e[32m#{str}\e[0m" end
34
+ def blue(str) "\e[34m#{str}\e[0m" end
35
+
36
+
37
+ def log(msg)
38
+ puts blue(bold("ghcurl: #{msg}"))
39
+ end
40
+
41
+
42
+ #
43
+ # @return absolute download path
44
+ #
45
+ def curl(url, name, to)
46
+ if to == nil
47
+ to = WAREHOUSE + '/' + name
48
+ elsif test 'd', to
49
+ to = to.chomp('/') + '/' + name
50
+ elsif to.include?('/')
51
+ # noop
52
+ else
53
+ # just a rename in WAREHOUSE
54
+ to = WAREHOUSE + '/' + name
55
+ end
56
+
57
+ cmd = "curl -L #{url} --create-dirs -o #{to}"
58
+ status = system cmd
59
+ if status == false || status.nil?
60
+ log "Download error!"
61
+ exit
62
+ end
63
+ log "Downloaded to #{to}"
64
+ return to
65
+ end
66
+
67
+
68
+ def get_filters
69
+
70
+ arches = [
71
+ # NOTE: Here we can't add 'x86' to the first group!
72
+ # Because we finally use simple "string compare" to filter out
73
+ # the improper links.
74
+ # If added, when we filter out the 'x86'(first group),
75
+ # the leading 'x86' will remove 'x86_64'(the second group) too!!!
76
+ ['i386', 'i686' ],
77
+ ['x64', 'x86_64', 'amd64'],
78
+
79
+ ['armv6', 'arm' ],
80
+ ['armv7' ],
81
+ ['armv8', 'arm64', 'aarch64'],
82
+ ]
83
+
84
+ oses = [
85
+ ['linux'],
86
+ ['freebsd'],
87
+ ['mac', 'apple', 'darwin'],
88
+ ['windows']
89
+ ]
90
+
91
+ # OS and Arch filters
92
+ fs = RUBY_PLATFORM.split('-')
93
+ os = ""
94
+ arch = ""
95
+ fs.each do |f|
96
+ case f
97
+ when 'linux' then os = 'linux'
98
+ when 'mac', 'macOS' then os = 'mac'
99
+ when 'freebsd' then os = 'freebsd'
100
+ when 'ucrt', 'mingw', 'windows' then os = 'windows'
101
+ end
102
+ end
103
+
104
+ # Why we choose to REFUSE filters rather than APPROVE filters?
105
+ # The answer is here, you may notice, different developers decide to
106
+ # name their distribution differently according to the same arch.
107
+ #
108
+ # For example, if a bin file is called "ruby_analyse.exe"
109
+ # The developer may call it (ONLY ONE of below):
110
+ # 1. ruby_analyse-windows-x64.exe
111
+ # 2. ruby_analyse-windows-x86_64.exe
112
+ # 3. ruby_analyse-windows-amd64.exe
113
+ #
114
+ # Think of the situation:
115
+ # When we decide the native arch is "x86-64", and the developer gives
116
+ # the name 'xxx-amd64', we will filter the link out wrongly ....
117
+ #
118
+ # So, we can't target the download link via arch. However, we can
119
+ # see them a group of possible names, then REFUSE other name of
120
+ # other arches.
121
+ #
122
+ fs.each do |f|
123
+ case f
124
+ when 'x64', 'x86_64', 'amd64' then arch = 'x86_64'
125
+ when 'x86', 'i386', 'i686' then arch = 'x86'
126
+
127
+ when 'arm64', 'armv8', 'aarch64' then arch = 'arm64'
128
+ when 'armv6', 'arm' then arch = 'armv6'
129
+ when 'armv7' then arch = 'armv7'
130
+ end
131
+ end
132
+
133
+ approval, refuse = [], []
134
+ # [os, arch].each { approval << _1 unless _1.nil? }
135
+
136
+ if os
137
+ i = oses.each_with_index do |type, index|
138
+ break index if type.include? os
139
+ end
140
+ if !i.nil?
141
+ tmp = oses.dup
142
+ tmp.delete_at(i)
143
+ refuse.concat tmp.flatten
144
+ end
145
+ end
146
+
147
+
148
+ if arch
149
+ i = arches.each_with_index do |type, index|
150
+ break index if type.include? arch
151
+ end
152
+ if !i.nil?
153
+ tmp = arches.dup
154
+ tmp.delete_at(i)
155
+ refuse.concat tmp.flatten
156
+ end
157
+ end
158
+
159
+ # DEBUG
160
+ # puts "=> Approval and refuse"
161
+ # p [os, arch]
162
+ # p refuse
163
+
164
+ # Now we only refuse others
165
+ return refuse
166
+
167
+ end
168
+
169
+
170
+ def download(repo, regexp, version: nil, download_to: nil)
171
+
172
+ require 'octokit'
173
+
174
+ # adjust repo name : user/repo
175
+ if repo =~ /^https:\/\/github.com/
176
+ require 'uri'
177
+ uri = URI(repo)
178
+ # index 1, take 2
179
+ repo = uri.path.split('/')[1,2].join('/')
180
+ elsif !repo.include?('/')
181
+ got_repo = DEFAULT_WARES[repo.to_sym]
182
+ if not got_repo
183
+ user = HL.ask "Who developed the awesome #{repo}? "
184
+ repo = user + '/' + repo
185
+ else
186
+ repo = got_repo
187
+ log "Use the popular repo #{repo}"
188
+ end
189
+ end
190
+
191
+ log "checking..."
192
+ begin
193
+ unless version
194
+ doc = Octokit::Client.new.latest_release(repo)
195
+ else
196
+ doc = Octokit::Client.new.release_for_tag(repo, 'v' + version)
197
+ end
198
+ rescue Octokit::NotFound
199
+ log "Not found #{repo} v#{version} !"
200
+ exit
201
+ rescue StandardError => e
202
+ log e
203
+ exit
204
+ end
205
+
206
+ links = doc.to_hash[:assets]
207
+ if links.empty?
208
+ log <<~EOE
209
+ The search result is empty, check the args!
210
+ repo: #{repo}
211
+ version: #{version ? version:'nil'}
212
+
213
+ Maybe there's no assets in this release
214
+ EOE
215
+ exit 0
216
+ end
217
+
218
+ links = links.map { _1[:browser_download_url] }
219
+
220
+
221
+ if regexp
222
+ filtered = links.select do
223
+ _1 =~ /#{regexp}/
224
+ end
225
+ else
226
+ refuse = get_filters()
227
+ filtered = links.select do |l|
228
+ refuse.all? do |f|
229
+ l !~ /#{f}/
230
+ end
231
+ end
232
+ end
233
+
234
+
235
+ if filtered.size == 1
236
+ link = filtered[0].split('/').last
237
+ else
238
+ if filtered.size == 0
239
+ links_for_user = links.map { _1.split('/').last }
240
+ else
241
+ links_for_user = filtered.map { _1.split('/').last }
242
+ end
243
+ link = HL.choose do |menu|
244
+ menu.index_color = :rgb_77bbff
245
+ menu.prompt = "Which one do you want to download? "
246
+ menu.choices( *links_for_user )
247
+ end
248
+ end
249
+
250
+ url = links[0].split('/')[0..-2].join('/') + '/' + link
251
+
252
+ log "Downloading #{url}"
253
+
254
+ dl_name = link.split('/').last
255
+
256
+ return curl(url, dl_name, download_to)
257
+ end
258
+
259
+
260
+ #
261
+ # This function is a little confusing
262
+ #
263
+ # target: The absolute path to a downloaded file
264
+ # name : The software name
265
+ #
266
+ def install(target, rename_as: nil, install_to: nil)
267
+
268
+ if target.end_with?('.deb')
269
+ log "Install the deb package"
270
+ system "sudo dpkg -i #{target}"
271
+ return
272
+ end
273
+
274
+ if target.end_with?('.rpm')
275
+ log "Install the rpm package"
276
+ system "sudo rpm -i #{target}"
277
+ return
278
+ end
279
+
280
+
281
+ if install_to.nil?
282
+ install_to = BIN_PATH
283
+ end
284
+
285
+
286
+ #
287
+ # Handle zip situation
288
+ #
289
+ zip_flag = false
290
+
291
+ if target.match?(/\.zip$/) or target.match?(/\.tar\.(\w){1,3}/)
292
+
293
+ zip_flag = true
294
+
295
+ # unzipped to dir
296
+ unzip_dir = ""
297
+ # unzipped files
298
+ files = ""
299
+ # the target unzipped file name (this includes the path prefix)
300
+ unzip_name = ""
301
+
302
+ # unzip
303
+ if target.match? /\.zip$/
304
+ log "Unzip zip file"
305
+ unzip_dir = target.chomp('.zip')
306
+ system "unzip -q #{target} -d #{unzip_dir}"
307
+ end
308
+
309
+ # .gz, .bz2, .xz
310
+ if target.match? /\.tar\.(\w){1,3}/
311
+ log "Unzip tar file"
312
+ unzip_dir = target.split('.')[0..-3].join('.')
313
+ FileUtils.mkdir_p(unzip_dir)
314
+ system "tar xf #{target} --directory=#{unzip_dir}"
315
+ end
316
+
317
+
318
+ #
319
+ # @return [Array] file names in the dir
320
+ #
321
+ def _iterate_dir(dir)
322
+ result = []
323
+ chd = Dir.children(dir)
324
+ chd_files = chd.select{|f| File.file? "#{dir}/#{f}"}
325
+
326
+ chd_files.each { result << "#{dir}/#{_1}"[2..] }
327
+
328
+ chd_dirs = chd.select{|d| File.directory?("#{dir}/#{d}") && ( d != '.git')}
329
+ chd_dirs.each do |d|
330
+ sub_dir = "#{dir}/#{d}"
331
+ result.concat _iterate_dir(sub_dir)
332
+ end
333
+ result
334
+ end
335
+
336
+
337
+ Dir.chdir unzip_dir do
338
+ files = _iterate_dir('.')
339
+ end
340
+
341
+
342
+ if files.size > 1
343
+ unzip_name = HL.choose do |menu|
344
+ menu.index_color = :rgb_77bbff
345
+ menu.prompt = "Which one do you want to install? "
346
+ menu.choices( *files )
347
+ end
348
+ else
349
+ unzip_name = files[0]
350
+ end
351
+
352
+ end # end of zipped file handle
353
+
354
+
355
+ # Get software real name
356
+ if zip_flag
357
+ name = unzip_name.split('/').last
358
+ else
359
+ name = target.split('/').last
360
+ end
361
+
362
+
363
+ if (name.size > 10 ||
364
+ name.include?('.') ||
365
+ name.include?('-') ||
366
+ name.include?('_')) && (not rename_as)
367
+ log "Do you want to rename the '#{name}'?"
368
+ re = HL.ask "Input new name, or leave it blank to not rename."
369
+ if !re.empty?
370
+ rename_as = re
371
+ end
372
+ end
373
+
374
+
375
+ if zip_flag
376
+ # Now:
377
+ # target is /home/xx/xx.zip
378
+ # unzip_dir is /home/xx/xx
379
+ # unzip_file is abc/cde/file (new target)
380
+ target = unzip_dir + '/' + unzip_name
381
+ end
382
+
383
+ log "Renamed as '#{rename_as}'" if rename_as
384
+
385
+
386
+ case RUBY_PLATFORM
387
+ when /ucrt/i, /mingw/i
388
+ install_on_windows(target, name, install_to, rename_as)
389
+ else
390
+ install_on_nix(target, name, install_to, rename_as)
391
+ end
392
+ end
393
+
394
+
395
+ #
396
+ # @param target [String] the absulute path of to be installed software
397
+ # @param name [String] the name of the software
398
+ #
399
+ def install_on_nix(target, name, install_to, rename_as)
400
+ install_to = install_to.chomp('/')
401
+
402
+ if test 'd', install_to
403
+ log "Ready to install #{name}"
404
+ if rename_as
405
+ dest = "#{install_to}/#{rename_as}"
406
+ system "sudo cp #{target} " + dest
407
+ system "sudo chmod +x " + dest
408
+ log "Installed as " + dest
409
+ else
410
+ cmd = "sudo install -Dt #{install_to} -m 755 #{target} "
411
+ system cmd
412
+ log "Install #{name} to " + install_to
413
+ end
414
+ log "Install finish!"
415
+ else
416
+ log "#{install_to} is not a directory!"
417
+ end
418
+
419
+ end
420
+
421
+
422
+ def install_on_windows(target, name, install_to, rename_as)
423
+ if !ENV['GHCURL_BIN_PATH']
424
+ log "Sorry, you must specify 'GHCURL_BIN_PATH' environment variable to install on Windows!"
425
+ return
426
+ end
427
+
428
+ install_to = install_to.chomp('/')
429
+
430
+ if test 'd', install_to
431
+ log "Ready to install #{name}"
432
+ if rename_as
433
+ rename_as = rename_as + '.exe' unless rename_as.end_with?('.exe')
434
+ dest = "#{install_to}/#{rename_as}"
435
+ FileUtils.cp(target, dest)
436
+ log "Installed as " + dest
437
+ else
438
+ FileUtils.cp(target, install_to)
439
+ log "Install #{name} to " + install_to
440
+ end
441
+ log "Install finish!"
442
+ else
443
+ log "#{install_to} is not a directory!"
444
+ end
445
+
446
+ end
447
+
448
+
449
+ #
450
+ # For -l option
451
+ #
452
+ def list_wares
453
+ FileUtils.mkdir_p(WAREHOUSE)
454
+ puts blue("ghcurl: #{WAREHOUSE}")
455
+ Dir.children(WAREHOUSE).each_with_index do |dict,i|
456
+ puts "#{blue(i+1)}. #{bold(green(dict))}"
457
+ end
458
+ end
459
+
460
+
461
+ #
462
+ # For -d option
463
+ #
464
+ def delete_wares(name)
465
+ begin
466
+
467
+ if name.nil?
468
+ re = HL.ask "Do you want to delete all downloaded files? [Y/n]"
469
+ case re.downcase
470
+ when '','y','ye','yes','true'
471
+ FileUtils.rm_rf WAREHOUSE
472
+ log "Delete all done"
473
+ end
474
+ else
475
+ FileUtils.rm WAREHOUSE + '/' + name
476
+ log "Delete #{name} done"
477
+ end
478
+
479
+ rescue Exception => e
480
+ puts bold(red("ghcurl: #{e}"))
481
+ list_wares
482
+ end
483
+
484
+ end # end def delete_wares
485
+
486
+
487
+ def help
488
+ puts <<~EOC
489
+ ghcurl (v#{VERSION}): Download files and install from Github releases
490
+
491
+ Default install to env 'GHCURL_BIN_PATH' or /usr/local/bin
492
+
493
+ usage:
494
+ ghcurl [user]/repo [regexp] => Search latest version with regexp to download
495
+ ghcurl repo [re] -v tag => Download a specific tag version
496
+ ghcurl repo [re] -o [path] => Download into path or rename
497
+ ghcurl repo [re] -i [path] => Download and install to path
498
+ ghcurl repo [re] -o [path] -i => Download into path and install
499
+ ghcurl repo [re] -i -r name => Download and install as 'name'
500
+ ghcurl -l => List downloaded files
501
+ ghcurl -d [name] => Delete a downloaded file or all
502
+ ghcurl -h => Print this help
503
+
504
+ example:
505
+ ghcurl bat => Search sharkdp/bat the latest
506
+ ghcurl cli deb -i => Search cli/cli /deb/
507
+ ghcurl rbspy/rbspy 'x86_64.*linux' -v0.11.1
508
+ ghcurl dlvhdr/gh-dash linux-amd64 -i -r 'gd'
509
+
510
+ EOC
511
+ end
512
+
513
+ end
514
+
515
+
516
+
517
+ ####################
518
+ # main: CLI Handling
519
+ ####################
520
+ extend Ghcurl
521
+
522
+ class Ghcurl::CLI < CliSwitch
523
+ option name: 'install', short: '-i', arg_required: 'optional'
524
+ option name: 'output', short: '-o', long: '--output', arg_required: 'required'
525
+ option name: 'rename', short: '-r', arg_required: 'required'
526
+ option name: 'version', short: '-v', arg_required: 'required'
527
+ option name: 'help', short: '-h', long: '--help', arg_required: 'noarg'
528
+ option name: 'list', short: '-l', arg_required: 'noarg'
529
+ option name: 'delete', short: '-d', arg_required: 'optional'
530
+ end
531
+
532
+ args, opts = Ghcurl::CLI.new.parse(ARGV)
533
+
534
+ if args.empty? and opts.empty?
535
+ help
536
+ exit
537
+ end
538
+
539
+
540
+ repo_or_name, regexp = args[0], args[1]
541
+ version = nil
542
+ need_install = false
543
+ download_to = nil
544
+ install_to = nil
545
+ rename_as = nil
546
+
547
+ opts.each do
548
+ case _1.name
549
+ when 'help'
550
+ help
551
+ exit
552
+ when 'list'
553
+ list_wares
554
+ exit
555
+ when 'delete'
556
+ delete_wares(_1.next_arg)
557
+ exit
558
+ when 'version'
559
+ version = _1.next_arg
560
+ when 'output'
561
+ download_to = _1.next_arg
562
+ when 'install'
563
+ need_install = true
564
+ install_to = _1.next_arg
565
+ when 'rename'
566
+ rename_as = _1.next_arg
567
+ end
568
+ end
569
+
570
+ # Debug
571
+ # p repo_or_name
572
+ # p regexp
573
+ # p need_install
574
+ # p download_to
575
+ # p install_to
576
+ # p rename_as
577
+
578
+ begin
579
+ ware = download(repo_or_name, regexp, version: version, download_to: download_to)
580
+ if need_install
581
+ install(ware, rename_as: rename_as, install_to: install_to)
582
+ end
583
+ rescue Interrupt
584
+ end