neocities 0.0.18 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|