neocities 0.0.17 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/lib/neocities/cli.rb +92 -22
- data/lib/neocities/client.rb +86 -2
- data/lib/neocities/version.rb +1 -1
- metadata +15 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5baf997b1c58eaf0d7b2c7487623ab49966d72f0a637b0bbf32111a0608ad12
|
4
|
+
data.tar.gz: 16b8cf7786e0cf714f41f00e49bb97038ffaf1de1c72ba656e0223b9a3c9aa15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1459d3bff5633233a3968a4fab5033df7b8048435786e5c2642aa5fe4123061ed69f957e186cff1c0f9ba45b0249e188c21a96a56b80df87bacd1cd3532727c
|
7
|
+
data.tar.gz: 64eeed0bbf5fe5381d2eb8d9641cd00832a4be0369e32ad3da4a4ab9544976c4f8574c40274a6a3f64b1146259939fd21e71533628143c0562241735e0bce112
|
data/Gemfile
CHANGED
data/lib/neocities/cli.rb
CHANGED
@@ -3,11 +3,14 @@ require 'pastel'
|
|
3
3
|
require 'tty/table'
|
4
4
|
require 'tty/prompt'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'json' # for reading configs
|
7
|
+
require 'whirly' # for loader spinner
|
8
|
+
|
6
9
|
require File.join(File.dirname(__FILE__), 'client')
|
7
10
|
|
8
11
|
module Neocities
|
9
12
|
class CLI
|
10
|
-
SUBCOMMANDS = %w{upload delete list info push logout pizza}
|
13
|
+
SUBCOMMANDS = %w{upload delete list info push logout pizza pull}
|
11
14
|
HELP_SUBCOMMANDS = ['-h', '--help', 'help']
|
12
15
|
PENELOPE_MOUTHS = %w{^ o ~ - v U}
|
13
16
|
PENELOPE_EYES = %w{o ~ O}
|
@@ -19,7 +22,7 @@ module Neocities
|
|
19
22
|
@subargs = @argv[1..@argv.length]
|
20
23
|
@prompt = TTY::Prompt.new
|
21
24
|
@api_key = ENV['NEOCITIES_API_KEY'] || nil
|
22
|
-
@app_config_path = File.join self.class.app_config_path('neocities'), 'config'
|
25
|
+
@app_config_path = File.join self.class.app_config_path('neocities'), 'config.json' # added json extension
|
23
26
|
end
|
24
27
|
|
25
28
|
def display_response(resp)
|
@@ -42,16 +45,28 @@ module Neocities
|
|
42
45
|
exit
|
43
46
|
end
|
44
47
|
|
45
|
-
|
46
|
-
|
48
|
+
if HELP_SUBCOMMANDS.include?(@subcmd) && SUBCOMMANDS.include?(@subargs[0])
|
49
|
+
send "display_#{@subargs[0]}_help_and_exit"
|
50
|
+
elsif @subcmd.nil? || !SUBCOMMANDS.include?(@subcmd)
|
51
|
+
display_help_and_exit
|
52
|
+
elsif @subargs.join("").match(HELP_SUBCOMMANDS.join('|')) && @subcmd != "info"
|
53
|
+
send "display_#{@subcmd}_help_and_exit"
|
54
|
+
end
|
47
55
|
|
48
56
|
if !@api_key
|
49
57
|
begin
|
50
|
-
|
58
|
+
file = File.read @app_config_path
|
59
|
+
data = JSON.load file
|
60
|
+
|
61
|
+
if data
|
62
|
+
@api_key = data["API_KEY"].strip # Remove any trailing whitespace causing HTTP requests to fail
|
63
|
+
@sitename = data["SITENAME"] # Store the sitename to be able to reference it later
|
64
|
+
@last_pull = data["LAST_PULL"] # Store the last time a pull was performed so that we only fetch from updated files
|
65
|
+
end
|
51
66
|
rescue Errno::ENOENT
|
52
67
|
@api_key = nil
|
53
68
|
end
|
54
|
-
end
|
69
|
+
end
|
55
70
|
|
56
71
|
if @api_key.nil?
|
57
72
|
puts "Please login to get your API key:"
|
@@ -65,8 +80,14 @@ module Neocities
|
|
65
80
|
|
66
81
|
resp = @client.key
|
67
82
|
if resp[:api_key]
|
83
|
+
conf = {
|
84
|
+
"API_KEY": resp[:api_key],
|
85
|
+
"SITENAME": @sitename,
|
86
|
+
}
|
87
|
+
|
68
88
|
FileUtils.mkdir_p Pathname(@app_config_path).dirname
|
69
|
-
File.write @app_config_path,
|
89
|
+
File.write @app_config_path, conf.to_json
|
90
|
+
|
70
91
|
puts "The api key for #{@pastel.bold @sitename} has been stored in #{@pastel.bold @app_config_path}."
|
71
92
|
else
|
72
93
|
display_response resp
|
@@ -80,6 +101,7 @@ module Neocities
|
|
80
101
|
end
|
81
102
|
|
82
103
|
def delete
|
104
|
+
display_delete_help_and_exit if @subargs.empty?
|
83
105
|
@subargs.each do |file|
|
84
106
|
puts @pastel.bold("Deleting #{file} ...")
|
85
107
|
resp = @client.delete file
|
@@ -90,16 +112,18 @@ module Neocities
|
|
90
112
|
|
91
113
|
def logout
|
92
114
|
confirmed = false
|
93
|
-
loop
|
115
|
+
loop do
|
94
116
|
case @subargs[0]
|
95
117
|
when '-y' then @subargs.shift; confirmed = true
|
96
|
-
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}"));
|
118
|
+
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); break
|
97
119
|
else break
|
98
120
|
end
|
99
|
-
|
121
|
+
end
|
100
122
|
if confirmed
|
101
123
|
FileUtils.rm @app_config_path
|
102
124
|
puts @pastel.bold("Your api key has been removed.")
|
125
|
+
else
|
126
|
+
display_logout_help_and_exit
|
103
127
|
end
|
104
128
|
end
|
105
129
|
|
@@ -113,7 +137,7 @@ module Neocities
|
|
113
137
|
|
114
138
|
out = []
|
115
139
|
|
116
|
-
resp[:info].each do |k,v|
|
140
|
+
resp[:info].each do |k, v|
|
117
141
|
v = Time.parse(v).localtime if v && (k == :created_at || k == :last_updated)
|
118
142
|
out.push [@pastel.bold(k), v]
|
119
143
|
end
|
@@ -123,6 +147,7 @@ module Neocities
|
|
123
147
|
end
|
124
148
|
|
125
149
|
def list
|
150
|
+
display_list_help_and_exit if @subargs.empty?
|
126
151
|
if @subargs.delete('-d') == '-d'
|
127
152
|
@detail = true
|
128
153
|
end
|
@@ -159,11 +184,12 @@ module Neocities
|
|
159
184
|
end
|
160
185
|
|
161
186
|
def push
|
187
|
+
display_push_help_and_exit if @subargs.empty?
|
162
188
|
@no_gitignore = false
|
163
189
|
@excluded_files = []
|
164
190
|
@dry_run = false
|
165
191
|
@prune = false
|
166
|
-
loop
|
192
|
+
loop do
|
167
193
|
case @subargs[0]
|
168
194
|
when '--no-gitignore' then @subargs.shift; @no_gitignore = true
|
169
195
|
when '-e' then @subargs.shift; @excluded_files.push(@subargs.shift)
|
@@ -172,7 +198,7 @@ module Neocities
|
|
172
198
|
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); display_push_help_and_exit
|
173
199
|
else break
|
174
200
|
end
|
175
|
-
|
201
|
+
end
|
176
202
|
|
177
203
|
if @dry_run
|
178
204
|
puts @pastel.green.bold("Doing a dry run, not actually pushing anything")
|
@@ -213,7 +239,7 @@ module Neocities
|
|
213
239
|
end
|
214
240
|
|
215
241
|
Dir.chdir(root_path) do
|
216
|
-
paths = Dir.glob(File.join('**', '*'))
|
242
|
+
paths = Dir.glob(File.join('**', '*'), File::FNM_DOTMATCH)
|
217
243
|
|
218
244
|
if @no_gitignore == false
|
219
245
|
begin
|
@@ -235,13 +261,11 @@ module Neocities
|
|
235
261
|
end
|
236
262
|
end
|
237
263
|
|
238
|
-
paths.select! {|p| !@excluded_files.include?(p)}
|
264
|
+
paths.select! { |p| !@excluded_files.include?(p) }
|
239
265
|
|
240
|
-
paths.select! {|p|
|
241
|
-
!@excluded_files.include?(Pathname.new(p).dirname.to_s)
|
242
|
-
}
|
266
|
+
paths.select! { |p| !@excluded_files.include?(Pathname.new(p).dirname.to_s) }
|
243
267
|
|
244
|
-
paths.collect! {|path| Pathname path}
|
268
|
+
paths.collect! { |path| Pathname path }
|
245
269
|
|
246
270
|
paths.each do |path|
|
247
271
|
next if path.directory?
|
@@ -264,13 +288,13 @@ module Neocities
|
|
264
288
|
display_upload_help_and_exit if @subargs.empty?
|
265
289
|
@dir = ''
|
266
290
|
|
267
|
-
loop
|
291
|
+
loop do
|
268
292
|
case @subargs[0]
|
269
293
|
when '-d' then @subargs.shift; @dir = @subargs.shift
|
270
294
|
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); display_upload_help_and_exit
|
271
295
|
else break
|
272
296
|
end
|
273
|
-
|
297
|
+
end
|
274
298
|
|
275
299
|
@subargs.each do |path|
|
276
300
|
path = Pathname path
|
@@ -293,6 +317,39 @@ module Neocities
|
|
293
317
|
end
|
294
318
|
end
|
295
319
|
|
320
|
+
def pull
|
321
|
+
begin
|
322
|
+
quiet = !(['--log', '-l'].include? @subargs[0])
|
323
|
+
|
324
|
+
file = File.read @app_config_path
|
325
|
+
data = JSON.load file
|
326
|
+
|
327
|
+
last_pull_time = data["LAST_PULL"] ? data["LAST_PULL"]["time"] : nil
|
328
|
+
last_pull_loc = data["LAST_PULL"] ? data["LAST_PULL"]["loc"] : nil
|
329
|
+
|
330
|
+
Whirly.start spinner: ["😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"], status: "Retrieving files for #{@pastel.bold @sitename}" if quiet
|
331
|
+
resp = @client.pull @sitename, last_pull_time, last_pull_loc, quiet
|
332
|
+
|
333
|
+
# write last pull data to file (not necessarily the best way to do this, but better than cloning every time)
|
334
|
+
data["LAST_PULL"] = {
|
335
|
+
"time": Time.now,
|
336
|
+
"loc": Dir.pwd
|
337
|
+
}
|
338
|
+
|
339
|
+
File.write @app_config_path, data.to_json
|
340
|
+
rescue StandardError => ex
|
341
|
+
Whirly.stop if quiet
|
342
|
+
puts @pastel.red.bold "\nA fatal error occurred :-("
|
343
|
+
puts @pastel.red ex
|
344
|
+
ensure
|
345
|
+
exit
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def pizza
|
350
|
+
display_pizza_help_and_exit
|
351
|
+
end
|
352
|
+
|
296
353
|
def display_pizza_help_and_exit
|
297
354
|
puts "Sorry, we're fresh out of dough today. Try again tomorrow."
|
298
355
|
exit
|
@@ -346,6 +403,16 @@ HERE
|
|
346
403
|
|
347
404
|
#{@pastel.green '$ neocities upload -d images img.jpg'} Upload img.jpg to the 'images' directory on your site
|
348
405
|
|
406
|
+
HERE
|
407
|
+
exit
|
408
|
+
end
|
409
|
+
|
410
|
+
def display_pull_help_and_exit
|
411
|
+
display_banner
|
412
|
+
|
413
|
+
puts <<HERE
|
414
|
+
#{@pastel.magenta.bold 'pull'} - Get the most recent version of files from your site
|
415
|
+
|
349
416
|
HERE
|
350
417
|
exit
|
351
418
|
end
|
@@ -366,6 +433,8 @@ HERE
|
|
366
433
|
|
367
434
|
#{@pastel.green '$ neocities push --dry-run .'} Just show what would be uploaded
|
368
435
|
|
436
|
+
#{@pastel.green '$ neocities push --prune .'} Delete site files not in dir (be careful!)
|
437
|
+
|
369
438
|
HERE
|
370
439
|
exit
|
371
440
|
end
|
@@ -419,6 +488,7 @@ HERE
|
|
419
488
|
info Information and stats for your site
|
420
489
|
logout Remove the site api key from the config
|
421
490
|
version Unceremoniously display version and self destruct
|
491
|
+
pull Get the most recent version of files from your site
|
422
492
|
pizza Order a free pizza
|
423
493
|
|
424
494
|
HERE
|
@@ -464,4 +534,4 @@ HERE
|
|
464
534
|
end
|
465
535
|
end
|
466
536
|
end
|
467
|
-
end
|
537
|
+
end
|
data/lib/neocities/client.rb
CHANGED
@@ -8,6 +8,10 @@ require 'pathname'
|
|
8
8
|
require 'uri'
|
9
9
|
require 'digest'
|
10
10
|
require 'httpclient'
|
11
|
+
require 'pastel'
|
12
|
+
require 'date'
|
13
|
+
|
14
|
+
require 'whirly'
|
11
15
|
|
12
16
|
module Neocities
|
13
17
|
class Client
|
@@ -17,6 +21,7 @@ module Neocities
|
|
17
21
|
@uri = URI.parse API_URI
|
18
22
|
@http = HTTPClient.new force_basic_auth: true
|
19
23
|
@opts = opts
|
24
|
+
@pastel = Pastel.new eachline: "\n"
|
20
25
|
|
21
26
|
unless opts[:api_key] || (opts[:sitename] && opts[:password])
|
22
27
|
raise ArgumentError, 'client requires a login (sitename/password) or an api_key'
|
@@ -27,13 +32,91 @@ module Neocities
|
|
27
32
|
else
|
28
33
|
@http.set_auth API_URI, opts[:sitename], opts[:password]
|
29
34
|
end
|
30
|
-
|
31
35
|
end
|
32
36
|
|
33
37
|
def list(path=nil)
|
34
38
|
get 'list', :path => path
|
35
39
|
end
|
36
40
|
|
41
|
+
def pull(sitename, last_pull_time=nil, last_pull_loc=nil, quiet=true)
|
42
|
+
site_info = get 'info', sitename: sitename
|
43
|
+
|
44
|
+
if site_info[:result] == 'error'
|
45
|
+
raise ArgumentError, site_info[:message]
|
46
|
+
end
|
47
|
+
|
48
|
+
# handle custom domains for supporter accounts
|
49
|
+
if site_info[:info][:domain] && site_info[:info][:domain] != ""
|
50
|
+
domain = "https://#{site_info[:info][:domain]}/"
|
51
|
+
else
|
52
|
+
domain = "https://#{sitename}.neocities.org/"
|
53
|
+
end
|
54
|
+
|
55
|
+
# start stats
|
56
|
+
success_loaded = 0
|
57
|
+
start_time = Time.now
|
58
|
+
curr_dir = Dir.pwd
|
59
|
+
|
60
|
+
# get list of files
|
61
|
+
resp = get 'list'
|
62
|
+
|
63
|
+
if resp[:result] == 'error'
|
64
|
+
raise ArgumentError, resp[:message]
|
65
|
+
end
|
66
|
+
|
67
|
+
# fetch each file
|
68
|
+
resp[:files].each do |file|
|
69
|
+
if !file[:is_directory]
|
70
|
+
print @pastel.bold("Loading #{file[:path]} ... ") if !quiet
|
71
|
+
|
72
|
+
if
|
73
|
+
last_pull_time && \
|
74
|
+
last_pull_loc && \
|
75
|
+
Time.parse(file[:updated_at]) <= Time.parse(last_pull_time) && \
|
76
|
+
last_pull_loc == curr_dir && \
|
77
|
+
File.exist?(file[:path]) # case when user deletes file
|
78
|
+
# case when file hasn't been updated since last
|
79
|
+
print "#{@pastel.yellow.bold "NO NEW UPDATES"}\n" if !quiet
|
80
|
+
next
|
81
|
+
end
|
82
|
+
|
83
|
+
pathtotry = domain + file[:path]
|
84
|
+
fileconts = @http.get pathtotry
|
85
|
+
|
86
|
+
# follow redirects
|
87
|
+
while fileconts.status == 301
|
88
|
+
new_path = fileconts.header['location'][0]
|
89
|
+
print "\n#{@pastel.red "Fetch from #{pathtotry} failed."}\nTrying #{new_path} instead..." if !quiet
|
90
|
+
|
91
|
+
pathtotry = new_path
|
92
|
+
fileconts = @http.get pathtotry
|
93
|
+
end
|
94
|
+
|
95
|
+
if fileconts.ok?
|
96
|
+
print "#{@pastel.green.bold 'SUCCESS'}\n" if !quiet
|
97
|
+
success_loaded += 1
|
98
|
+
|
99
|
+
File.open("#{file[:path]}", "w") do |f|
|
100
|
+
f.write(fileconts.body)
|
101
|
+
end
|
102
|
+
else
|
103
|
+
print "#{@pastel.red.bold 'FAIL'}\n" if !quiet
|
104
|
+
end
|
105
|
+
else
|
106
|
+
FileUtils.mkdir_p "#{file[:path]}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# calculate time command took
|
111
|
+
total_time = Time.now - start_time
|
112
|
+
|
113
|
+
# stop the spinner, if there is one
|
114
|
+
Whirly.stop if quiet
|
115
|
+
|
116
|
+
# display stats
|
117
|
+
puts @pastel.green "\nSuccessfully fetched #{success_loaded} files in #{total_time} seconds"
|
118
|
+
end
|
119
|
+
|
37
120
|
def key
|
38
121
|
get 'key'
|
39
122
|
end
|
@@ -86,6 +169,7 @@ module Neocities
|
|
86
169
|
uri = @uri+path
|
87
170
|
uri.query = URI.encode_www_form params
|
88
171
|
resp = @http.get uri
|
172
|
+
|
89
173
|
JSON.parse resp.body, symbolize_names: true
|
90
174
|
end
|
91
175
|
|
@@ -95,4 +179,4 @@ module Neocities
|
|
95
179
|
JSON.parse resp.body, symbolize_names: true
|
96
180
|
end
|
97
181
|
end
|
98
|
-
end
|
182
|
+
end
|
data/lib/neocities/version.rb
CHANGED
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neocities
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyle Drake
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-table
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - '='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.10.0
|
20
17
|
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '0.10'
|
20
|
+
- - '='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.10.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - '='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 0.10.0
|
30
27
|
- - "~>"
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '0.10'
|
30
|
+
- - '='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.10.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: tty-prompt
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - '='
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 0.12.0
|
40
37
|
- - "~>"
|
41
38
|
- !ruby/object:Gem::Version
|
42
39
|
version: '0.12'
|
40
|
+
- - '='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.12.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - '='
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 0.12.0
|
50
47
|
- - "~>"
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '0.12'
|
50
|
+
- - '='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.12.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: pastel
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
148
|
- !ruby/object:Gem::Version
|
149
149
|
version: '0'
|
150
150
|
requirements: []
|
151
|
-
rubygems_version: 3.
|
151
|
+
rubygems_version: 3.5.13
|
152
152
|
signing_key:
|
153
153
|
specification_version: 4
|
154
154
|
summary: Neocities.org CLI and API client
|