neocities 0.0.18 → 0.0.20
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 +1 -0
- data/lib/neocities/cli.rb +97 -26
- data/lib/neocities/client.rb +87 -2
- data/lib/neocities/version.rb +1 -1
- data/neocities.gemspec +2 -1
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5031613f2ead49cb8b0d897ba928dca50121afbc65b0f624afd89038c6f97c3e
|
4
|
+
data.tar.gz: 3f6a7813f75c2d0d667700258a5f2a9a60b74176b69cee5c1d26d95892970a91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1ae46e13488bbdd244da5e1f3eb005002f6a2e9a687c9682fc2055ccaf054c76de1f9386ed578b7462745acf1c53d7fac81957b41b474b2f0422ab5823d3f09
|
7
|
+
data.tar.gz: a72a64b9bc2ea15a3c7c6ad01f8f405802c5476e96b8975081d3e7dbef531543838eb029fcba108c7cc7a96e89725fac8855500c0c8ffd8922235f05a86fb9b9
|
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,10 +198,11 @@ 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
|
-
if @
|
180
|
-
|
203
|
+
if @subargs[0].nil?
|
204
|
+
display_response result: 'error', message: "no local path provided"
|
205
|
+
display_push_help_and_exit
|
181
206
|
end
|
182
207
|
|
183
208
|
root_path = Pathname @subargs[0]
|
@@ -192,6 +217,10 @@ module Neocities
|
|
192
217
|
display_push_help_and_exit
|
193
218
|
end
|
194
219
|
|
220
|
+
if @dry_run
|
221
|
+
puts @pastel.green.bold("Doing a dry run, not actually pushing anything")
|
222
|
+
end
|
223
|
+
|
195
224
|
if @prune
|
196
225
|
pruned_dirs = []
|
197
226
|
resp = @client.list
|
@@ -237,13 +266,11 @@ module Neocities
|
|
237
266
|
end
|
238
267
|
end
|
239
268
|
|
240
|
-
paths.select! {|p| !@excluded_files.include?(p)}
|
269
|
+
paths.select! { |p| !@excluded_files.include?(p) }
|
241
270
|
|
242
|
-
paths.select! {|p|
|
243
|
-
!@excluded_files.include?(Pathname.new(p).dirname.to_s)
|
244
|
-
}
|
271
|
+
paths.select! { |p| !@excluded_files.include?(Pathname.new(p).dirname.to_s) }
|
245
272
|
|
246
|
-
paths.collect! {|path| Pathname path}
|
273
|
+
paths.collect! { |path| Pathname path }
|
247
274
|
|
248
275
|
paths.each do |path|
|
249
276
|
next if path.directory?
|
@@ -266,13 +293,13 @@ module Neocities
|
|
266
293
|
display_upload_help_and_exit if @subargs.empty?
|
267
294
|
@dir = ''
|
268
295
|
|
269
|
-
loop
|
296
|
+
loop do
|
270
297
|
case @subargs[0]
|
271
298
|
when '-d' then @subargs.shift; @dir = @subargs.shift
|
272
299
|
when /^-/ then puts(@pastel.red.bold("Unknown option: #{@subargs[0].inspect}")); display_upload_help_and_exit
|
273
300
|
else break
|
274
301
|
end
|
275
|
-
|
302
|
+
end
|
276
303
|
|
277
304
|
@subargs.each do |path|
|
278
305
|
path = Pathname path
|
@@ -295,6 +322,39 @@ module Neocities
|
|
295
322
|
end
|
296
323
|
end
|
297
324
|
|
325
|
+
def pull
|
326
|
+
begin
|
327
|
+
quiet = !(['--log', '-l'].include? @subargs[0])
|
328
|
+
|
329
|
+
file = File.read @app_config_path
|
330
|
+
data = JSON.load file
|
331
|
+
|
332
|
+
last_pull_time = data["LAST_PULL"] ? data["LAST_PULL"]["time"] : nil
|
333
|
+
last_pull_loc = data["LAST_PULL"] ? data["LAST_PULL"]["loc"] : nil
|
334
|
+
|
335
|
+
Whirly.start spinner: ["😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"], status: "Retrieving files for #{@pastel.bold @sitename}" if quiet
|
336
|
+
resp = @client.pull @sitename, last_pull_time, last_pull_loc, quiet
|
337
|
+
|
338
|
+
# write last pull data to file (not necessarily the best way to do this, but better than cloning every time)
|
339
|
+
data["LAST_PULL"] = {
|
340
|
+
"time": Time.now,
|
341
|
+
"loc": Dir.pwd
|
342
|
+
}
|
343
|
+
|
344
|
+
File.write @app_config_path, data.to_json
|
345
|
+
rescue StandardError => ex
|
346
|
+
Whirly.stop if quiet
|
347
|
+
puts @pastel.red.bold "\nA fatal error occurred :-("
|
348
|
+
puts @pastel.red ex
|
349
|
+
ensure
|
350
|
+
exit
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def pizza
|
355
|
+
display_pizza_help_and_exit
|
356
|
+
end
|
357
|
+
|
298
358
|
def display_pizza_help_and_exit
|
299
359
|
puts "Sorry, we're fresh out of dough today. Try again tomorrow."
|
300
360
|
exit
|
@@ -348,6 +408,16 @@ HERE
|
|
348
408
|
|
349
409
|
#{@pastel.green '$ neocities upload -d images img.jpg'} Upload img.jpg to the 'images' directory on your site
|
350
410
|
|
411
|
+
HERE
|
412
|
+
exit
|
413
|
+
end
|
414
|
+
|
415
|
+
def display_pull_help_and_exit
|
416
|
+
display_banner
|
417
|
+
|
418
|
+
puts <<HERE
|
419
|
+
#{@pastel.magenta.bold 'pull'} - Get the most recent version of files from your site
|
420
|
+
|
351
421
|
HERE
|
352
422
|
exit
|
353
423
|
end
|
@@ -367,7 +437,7 @@ HERE
|
|
367
437
|
#{@pastel.green '$ neocities push --no-gitignore .'} Don't use .gitignore to exclude files
|
368
438
|
|
369
439
|
#{@pastel.green '$ neocities push --dry-run .'} Just show what would be uploaded
|
370
|
-
|
440
|
+
|
371
441
|
#{@pastel.green '$ neocities push --prune .'} Delete site files not in dir (be careful!)
|
372
442
|
|
373
443
|
HERE
|
@@ -423,6 +493,7 @@ HERE
|
|
423
493
|
info Information and stats for your site
|
424
494
|
logout Remove the site api key from the config
|
425
495
|
version Unceremoniously display version and self destruct
|
496
|
+
pull Get the most recent version of files from your site
|
426
497
|
pizza Order a free pizza
|
427
498
|
|
428
499
|
HERE
|
@@ -468,4 +539,4 @@ HERE
|
|
468
539
|
end
|
469
540
|
end
|
470
541
|
end
|
471
|
-
end
|
542
|
+
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,92 @@ 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
|
+
uri_parser = URI::Parser.new
|
69
|
+
resp[:files].each do |file|
|
70
|
+
if !file[:is_directory]
|
71
|
+
print @pastel.bold("Loading #{file[:path]} ... ") if !quiet
|
72
|
+
|
73
|
+
if
|
74
|
+
last_pull_time && \
|
75
|
+
last_pull_loc && \
|
76
|
+
Time.parse(file[:updated_at]) <= Time.parse(last_pull_time) && \
|
77
|
+
last_pull_loc == curr_dir && \
|
78
|
+
File.exist?(file[:path]) # case when user deletes file
|
79
|
+
# case when file hasn't been updated since last
|
80
|
+
print "#{@pastel.yellow.bold "NO NEW UPDATES"}\n" if !quiet
|
81
|
+
next
|
82
|
+
end
|
83
|
+
|
84
|
+
pathtotry = uri_parser.escape(domain + file[:path])
|
85
|
+
fileconts = @http.get pathtotry
|
86
|
+
|
87
|
+
# follow redirects
|
88
|
+
while fileconts.status == 301
|
89
|
+
new_path = fileconts.header['location'][0]
|
90
|
+
print "\n#{@pastel.red "Fetch from #{pathtotry} failed."}\nTrying #{new_path} instead..." if !quiet
|
91
|
+
|
92
|
+
pathtotry = new_path
|
93
|
+
fileconts = @http.get pathtotry
|
94
|
+
end
|
95
|
+
|
96
|
+
if fileconts.ok?
|
97
|
+
print "#{@pastel.green.bold 'SUCCESS'}\n" if !quiet
|
98
|
+
success_loaded += 1
|
99
|
+
|
100
|
+
File.open("#{file[:path]}", "w") do |f|
|
101
|
+
f.write(fileconts.body)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
print "#{@pastel.red.bold 'FAIL'}\n" if !quiet
|
105
|
+
end
|
106
|
+
else
|
107
|
+
FileUtils.mkdir_p "#{file[:path]}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# calculate time command took
|
112
|
+
total_time = Time.now - start_time
|
113
|
+
|
114
|
+
# stop the spinner, if there is one
|
115
|
+
Whirly.stop if quiet
|
116
|
+
|
117
|
+
# display stats
|
118
|
+
puts @pastel.green "\nSuccessfully fetched #{success_loaded} files in #{total_time} seconds"
|
119
|
+
end
|
120
|
+
|
37
121
|
def key
|
38
122
|
get 'key'
|
39
123
|
end
|
@@ -86,6 +170,7 @@ module Neocities
|
|
86
170
|
uri = @uri+path
|
87
171
|
uri.query = URI.encode_www_form params
|
88
172
|
resp = @http.get uri
|
173
|
+
|
89
174
|
JSON.parse resp.body, symbolize_names: true
|
90
175
|
end
|
91
176
|
|
@@ -95,4 +180,4 @@ module Neocities
|
|
95
180
|
JSON.parse resp.body, symbolize_names: true
|
96
181
|
end
|
97
182
|
end
|
98
|
-
end
|
183
|
+
end
|
data/lib/neocities/version.rb
CHANGED
data/neocities.gemspec
CHANGED
@@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_dependency 'tty-prompt', '~> 0.12', '= 0.12.0'
|
22
22
|
spec.add_dependency 'pastel', '~> 0.7', '= 0.7.2'
|
23
23
|
spec.add_dependency 'httpclient-fixcerts', '~> 2.8', '>= 2.8.5'
|
24
|
-
spec.add_dependency 'rake', '~> 12.3',
|
24
|
+
spec.add_dependency 'rake', '~> 12.3', '>= 12.3.1'
|
25
|
+
spec.add_dependency 'whirly', '~> 0.3', '>= 0.3.0'
|
25
26
|
end
|
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.20
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-table
|
@@ -110,6 +110,26 @@ dependencies:
|
|
110
110
|
- - ">="
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: 12.3.1
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: whirly
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0.3'
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 0.3.0
|
123
|
+
type: :runtime
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0.3'
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 0.3.0
|
113
133
|
description:
|
114
134
|
email:
|
115
135
|
- contact@neocities.org
|
@@ -148,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
168
|
- !ruby/object:Gem::Version
|
149
169
|
version: '0'
|
150
170
|
requirements: []
|
151
|
-
rubygems_version: 3.
|
171
|
+
rubygems_version: 3.5.13
|
152
172
|
signing_key:
|
153
173
|
specification_version: 4
|
154
174
|
summary: Neocities.org CLI and API client
|