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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 322b624aea971dd7a650eac8a9b6011e0f3d13ff8e4a5bb2f1a423b87bb446ac
4
- data.tar.gz: c28f4d3296f36bb8a556fa210cc79a19a765e511fbf3b45111f93738b26014d4
3
+ metadata.gz: 5031613f2ead49cb8b0d897ba928dca50121afbc65b0f624afd89038c6f97c3e
4
+ data.tar.gz: 3f6a7813f75c2d0d667700258a5f2a9a60b74176b69cee5c1d26d95892970a91
5
5
  SHA512:
6
- metadata.gz: ec195af25579bd74b8763ec1bc2d997ede505157fb2e6da9e27bfc4854a03526c57d8d05c593e9a10bfa8c0597649f75be03962d338933cbf763c9c75be9e110
7
- data.tar.gz: 45ea7e5ff1bc0addafe30ca5bb9a8e3b23dabd168b01b0169ab254895d8041bb098687cfb982c5161d2d5a9d4ed2634937064c87b1f3b0bcb76b8ea1d944d031
6
+ metadata.gz: b1ae46e13488bbdd244da5e1f3eb005002f6a2e9a687c9682fc2055ccaf054c76de1f9386ed578b7462745acf1c53d7fac81957b41b474b2f0422ab5823d3f09
7
+ data.tar.gz: a72a64b9bc2ea15a3c7c6ad01f8f405802c5476e96b8975081d3e7dbef531543838eb029fcba108c7cc7a96e89725fac8855500c0c8ffd8922235f05a86fb9b9
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
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
- 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
51
- # Remove any trailing whitespace causing HTTP requests to fail
52
- @api_key = @api_key.strip
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, resp[:api_key]
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}")); display_logout_help_and_exit
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 @dry_run
180
- puts @pastel.green.bold("Doing a dry run, not actually pushing anything")
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Neocities
2
- VERSION = '0.0.18'
2
+ VERSION = '0.0.20'
3
3
  end
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', '>= 12.3.1'
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.18
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: 2023-07-07 00:00:00.000000000 Z
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.3.26
171
+ rubygems_version: 3.5.13
152
172
  signing_key:
153
173
  specification_version: 4
154
174
  summary: Neocities.org CLI and API client