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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e868d70dd0564036428471413481d66f764cde75087d25a4aef0ec945d5792b
4
- data.tar.gz: 9d180d291b6169253110534aef62a48ec3ee8b43a8f50dc6a4635695d1ce525a
3
+ metadata.gz: c5baf997b1c58eaf0d7b2c7487623ab49966d72f0a637b0bbf32111a0608ad12
4
+ data.tar.gz: 16b8cf7786e0cf714f41f00e49bb97038ffaf1de1c72ba656e0223b9a3c9aa15
5
5
  SHA512:
6
- metadata.gz: d2a884cc70d5ffb3521a472e2f1a582963f44b3e390f171486c6c3d08125041779cf65e562decc6a3aeaafbfa2d7e956efbb22ee77cd7b5cb3e3f096d7d15e11
7
- data.tar.gz: aab95d2837cdcdfa49b35cabf4a4b57fae898f2189bcd16f34b16e5729b9f415491fe4c7a024376d0b0e5eb24c98cea2c5743269fa6d21431876bd2b4f58e157
6
+ metadata.gz: b1459d3bff5633233a3968a4fab5033df7b8048435786e5c2642aa5fe4123061ed69f957e186cff1c0f9ba45b0249e188c21a96a56b80df87bacd1cd3532727c
7
+ data.tar.gz: 64eeed0bbf5fe5381d2eb8d9641cd00832a4be0369e32ad3da4a4ab9544976c4f8574c40274a6a3f64b1146259939fd21e71533628143c0562241735e0bce112
data/Gemfile CHANGED
@@ -1,2 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
+
4
+ # cursor whirlies (for loading)
5
+ gem 'whirly'
6
+ gem 'paint'
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
- display_help_and_exit if @subcmd.nil? || @argv.include?(HELP_SUBCOMMANDS) || !SUBCOMMANDS.include?(@subcmd)
46
- send "display_#{@subcmd}_help_and_exit" if @subargs.empty?
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
- @api_key = File.read @app_config_path
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, resp[:api_key]
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}")); display_logout_help_and_exit
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Neocities
2
- VERSION = '0.0.17'
2
+ VERSION = '0.0.19'
3
3
  end
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.17
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: 2022-05-02 00:00:00.000000000 Z
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.0.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