ghcurl 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile +6 -6
- data/LICENSE +22 -22
- data/README.md +102 -103
- data/Rakefile +3 -3
- data/bin/ghcurl +584 -525
- data/ghcurl.gemspec +40 -0
- data/lib/ghcurl.rb +79 -71
- 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-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
122
|
-
|
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
|
-
end
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
end
|
316
|
-
|
317
|
-
|
318
|
-
#
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
else
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
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
|