neocities 0.0.18 → 0.0.19
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/Gemfile +4 -0
- data/lib/neocities/cli.rb +90 -24
- data/lib/neocities/client.rb +86 -2
- data/lib/neocities/version.rb +1 -1
- metadata +3 -3
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,18 +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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
53
66
|
rescue Errno::ENOENT
|
|
54
67
|
@api_key = nil
|
|
55
68
|
end
|
|
56
|
-
end
|
|
69
|
+
end
|
|
57
70
|
|
|
58
71
|
if @api_key.nil?
|
|
59
72
|
puts "Please login to get your API key:"
|
|
@@ -67,8 +80,14 @@ module Neocities
|
|
|
67
80
|
|
|
68
81
|
resp = @client.key
|
|
69
82
|
if resp[:api_key]
|
|
83
|
+
conf = {
|
|
84
|
+
"API_KEY": resp[:api_key],
|
|
85
|
+
"SITENAME": @sitename,
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
FileUtils.mkdir_p Pathname(@app_config_path).dirname
|
|
71
|
-
File.write @app_config_path,
|
|
89
|
+
File.write @app_config_path, conf.to_json
|
|
90
|
+
|
|
72
91
|
puts "The api key for #{@pastel.bold @sitename} has been stored in #{@pastel.bold @app_config_path}."
|
|
73
92
|
else
|
|
74
93
|
display_response resp
|
|
@@ -82,6 +101,7 @@ module Neocities
|
|
|
82
101
|
end
|
|
83
102
|
|
|
84
103
|
def delete
|
|
104
|
+
display_delete_help_and_exit if @subargs.empty?
|
|
85
105
|
@subargs.each do |file|
|
|
86
106
|
puts @pastel.bold("Deleting #{file} ...")
|
|
87
107
|
resp = @client.delete file
|
|
@@ -92,16 +112,18 @@ module Neocities
|
|
|
92
112
|
|
|
93
113
|
def logout
|
|
94
114
|
confirmed = false
|
|
95
|
-
loop
|
|
115
|
+
loop do
|
|
96
116
|
case @subargs[0]
|
|
97
117
|
when '-y' then @subargs.shift; confirmed = true
|
|
98
|
-
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}"));
|
|
118
|
+
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); break
|
|
99
119
|
else break
|
|
100
120
|
end
|
|
101
|
-
|
|
121
|
+
end
|
|
102
122
|
if confirmed
|
|
103
123
|
FileUtils.rm @app_config_path
|
|
104
124
|
puts @pastel.bold("Your api key has been removed.")
|
|
125
|
+
else
|
|
126
|
+
display_logout_help_and_exit
|
|
105
127
|
end
|
|
106
128
|
end
|
|
107
129
|
|
|
@@ -115,7 +137,7 @@ module Neocities
|
|
|
115
137
|
|
|
116
138
|
out = []
|
|
117
139
|
|
|
118
|
-
resp[:info].each do |k,v|
|
|
140
|
+
resp[:info].each do |k, v|
|
|
119
141
|
v = Time.parse(v).localtime if v && (k == :created_at || k == :last_updated)
|
|
120
142
|
out.push [@pastel.bold(k), v]
|
|
121
143
|
end
|
|
@@ -125,6 +147,7 @@ module Neocities
|
|
|
125
147
|
end
|
|
126
148
|
|
|
127
149
|
def list
|
|
150
|
+
display_list_help_and_exit if @subargs.empty?
|
|
128
151
|
if @subargs.delete('-d') == '-d'
|
|
129
152
|
@detail = true
|
|
130
153
|
end
|
|
@@ -161,11 +184,12 @@ module Neocities
|
|
|
161
184
|
end
|
|
162
185
|
|
|
163
186
|
def push
|
|
187
|
+
display_push_help_and_exit if @subargs.empty?
|
|
164
188
|
@no_gitignore = false
|
|
165
189
|
@excluded_files = []
|
|
166
190
|
@dry_run = false
|
|
167
191
|
@prune = false
|
|
168
|
-
loop
|
|
192
|
+
loop do
|
|
169
193
|
case @subargs[0]
|
|
170
194
|
when '--no-gitignore' then @subargs.shift; @no_gitignore = true
|
|
171
195
|
when '-e' then @subargs.shift; @excluded_files.push(@subargs.shift)
|
|
@@ -174,7 +198,7 @@ module Neocities
|
|
|
174
198
|
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); display_push_help_and_exit
|
|
175
199
|
else break
|
|
176
200
|
end
|
|
177
|
-
|
|
201
|
+
end
|
|
178
202
|
|
|
179
203
|
if @dry_run
|
|
180
204
|
puts @pastel.green.bold("Doing a dry run, not actually pushing anything")
|
|
@@ -237,13 +261,11 @@ module Neocities
|
|
|
237
261
|
end
|
|
238
262
|
end
|
|
239
263
|
|
|
240
|
-
paths.select! {|p| !@excluded_files.include?(p)}
|
|
264
|
+
paths.select! { |p| !@excluded_files.include?(p) }
|
|
241
265
|
|
|
242
|
-
paths.select! {|p|
|
|
243
|
-
!@excluded_files.include?(Pathname.new(p).dirname.to_s)
|
|
244
|
-
}
|
|
266
|
+
paths.select! { |p| !@excluded_files.include?(Pathname.new(p).dirname.to_s) }
|
|
245
267
|
|
|
246
|
-
paths.collect! {|path| Pathname path}
|
|
268
|
+
paths.collect! { |path| Pathname path }
|
|
247
269
|
|
|
248
270
|
paths.each do |path|
|
|
249
271
|
next if path.directory?
|
|
@@ -266,13 +288,13 @@ module Neocities
|
|
|
266
288
|
display_upload_help_and_exit if @subargs.empty?
|
|
267
289
|
@dir = ''
|
|
268
290
|
|
|
269
|
-
loop
|
|
291
|
+
loop do
|
|
270
292
|
case @subargs[0]
|
|
271
293
|
when '-d' then @subargs.shift; @dir = @subargs.shift
|
|
272
294
|
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); display_upload_help_and_exit
|
|
273
295
|
else break
|
|
274
296
|
end
|
|
275
|
-
|
|
297
|
+
end
|
|
276
298
|
|
|
277
299
|
@subargs.each do |path|
|
|
278
300
|
path = Pathname path
|
|
@@ -295,6 +317,39 @@ module Neocities
|
|
|
295
317
|
end
|
|
296
318
|
end
|
|
297
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
|
+
|
|
298
353
|
def display_pizza_help_and_exit
|
|
299
354
|
puts "Sorry, we're fresh out of dough today. Try again tomorrow."
|
|
300
355
|
exit
|
|
@@ -348,6 +403,16 @@ HERE
|
|
|
348
403
|
|
|
349
404
|
#{@pastel.green '$ neocities upload -d images img.jpg'} Upload img.jpg to the 'images' directory on your site
|
|
350
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
|
+
|
|
351
416
|
HERE
|
|
352
417
|
exit
|
|
353
418
|
end
|
|
@@ -367,7 +432,7 @@ HERE
|
|
|
367
432
|
#{@pastel.green '$ neocities push --no-gitignore .'} Don't use .gitignore to exclude files
|
|
368
433
|
|
|
369
434
|
#{@pastel.green '$ neocities push --dry-run .'} Just show what would be uploaded
|
|
370
|
-
|
|
435
|
+
|
|
371
436
|
#{@pastel.green '$ neocities push --prune .'} Delete site files not in dir (be careful!)
|
|
372
437
|
|
|
373
438
|
HERE
|
|
@@ -423,6 +488,7 @@ HERE
|
|
|
423
488
|
info Information and stats for your site
|
|
424
489
|
logout Remove the site api key from the config
|
|
425
490
|
version Unceremoniously display version and self destruct
|
|
491
|
+
pull Get the most recent version of files from your site
|
|
426
492
|
pizza Order a free pizza
|
|
427
493
|
|
|
428
494
|
HERE
|
|
@@ -468,4 +534,4 @@ HERE
|
|
|
468
534
|
end
|
|
469
535
|
end
|
|
470
536
|
end
|
|
471
|
-
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,14 +1,14 @@
|
|
|
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
|
|
@@ -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
|