neocities-red 1.0.4 → 1.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd71b524510995f4ec9cf3e946aab60f7de911341097dd9dce5bb2d74764e879
4
- data.tar.gz: 3d34a694215c982f08aa6bc23bb86e313e909a1986bbdaebffe8de7bccedc626
3
+ metadata.gz: '009505183f0da2cfdae7b870a198f2a6c098ef819c0c1a160a1bb68f00e99b4a'
4
+ data.tar.gz: be6aee25e383c296051b564f4875943a98c27131af34d396dd411d3bc6f9e413
5
5
  SHA512:
6
- metadata.gz: 77402864a0cff4d47206f2bd7939969178017f6bc7478663d0b7f2f9b0a6702ed3c16db338f22463fa5aeee8b66fbea908e94cc6bb25be9b257cbcbf7fc7fd0e
7
- data.tar.gz: 989c1691bd1a8ad11396c88be7458bc3c8b1005f83fe9015ff1463de6e5e3ca3565d62ad3820d74fe7c87869eed153e436e1d6d536f7371483f3246072a022cd
6
+ metadata.gz: ce0fa6ce425276dfc280a48ce3d47c7f1729df40258877b4d0f6372cce09d7729577344f73318c8bad866f1dad619ede32fdad7e99507752c0627927a8ac8a05
7
+ data.tar.gz: 1791b52b7bc01f5b7e58b18a728a009cc06659d321dfa68d00be8e150e875ab4f9e167fea578d7a122ff0eeb62d152f0a080e6911afd20f45c9e5805378cb9d8
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ lint:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - name: Checkout repository
13
+ uses: actions/checkout@v4
14
+
15
+ - name: Set up Ruby 4.0.0
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: 4.0.0
19
+
20
+ - name: Install dependencies
21
+ run: |
22
+ bundle config set frozen true
23
+ bundle install
24
+
25
+ - name: Debug versions
26
+ run: |
27
+ ruby -v
28
+ bundle exec rubocop -V
29
+
30
+ - name: Run linter
31
+ run: bundle exec rubocop
32
+
33
+ test:
34
+ runs-on: ubuntu-latest
35
+
36
+ steps:
37
+ - name: Checkout repository
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Set up Ruby 4.0.0
41
+ uses: ruby/setup-ruby@v1
42
+ with:
43
+ ruby-version: 4.0.0
44
+
45
+ - name: Install dependencies
46
+ run: |
47
+ bundle config set frozen true
48
+ bundle install
49
+
50
+ - name: Run tests
51
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
- Gemfile.lock
2
1
  test.rb
3
2
  *.gem
4
3
 
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
 
4
4
  AllCops:
@@ -11,7 +11,7 @@ AllCops:
11
11
  - "spec/fixtures/**/*"
12
12
 
13
13
  Layout/LineLength:
14
- Max: 100
14
+ Max: 150
15
15
 
16
16
  Metrics:
17
17
  Enabled: false
@@ -39,3 +39,6 @@ RSpec/NestedGroups:
39
39
 
40
40
  Gemspec/DevelopmentDependencies:
41
41
  Enabled: false
42
+
43
+ RSpec/MultipleMemoizedHelpers:
44
+ Enabled: false
data/Gemfile.lock ADDED
@@ -0,0 +1,161 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ neocities-red (1.0.5)
5
+ faraday (~> 2.3, >= 2.14.0)
6
+ faraday-follow_redirects
7
+ faraday-multipart
8
+ pastel (~> 0.8, = 0.8.0)
9
+ rake (~> 13, >= 13.3.0)
10
+ tty-prompt (~> 0.23, = 0.23.1)
11
+ tty-table (~> 0.12, = 0.12.0)
12
+ whirly (~> 0.3, >= 0.3.0)
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ ast (2.4.3)
18
+ diff-lcs (1.6.2)
19
+ faraday (2.14.1)
20
+ faraday-net_http (>= 2.0, < 3.5)
21
+ json
22
+ logger
23
+ faraday-follow_redirects (0.5.0)
24
+ faraday (>= 1, < 3)
25
+ faraday-multipart (1.2.0)
26
+ multipart-post (~> 2.0)
27
+ faraday-net_http (3.4.2)
28
+ net-http (~> 0.5)
29
+ json (2.18.1)
30
+ language_server-protocol (3.17.0.5)
31
+ lint_roller (1.1.0)
32
+ logger (1.7.0)
33
+ multipart-post (2.4.1)
34
+ net-http (0.9.1)
35
+ uri (>= 0.11.1)
36
+ parallel (1.27.0)
37
+ parser (3.3.10.1)
38
+ ast (~> 2.4.1)
39
+ racc
40
+ pastel (0.8.0)
41
+ tty-color (~> 0.5)
42
+ prism (1.9.0)
43
+ racc (1.8.1)
44
+ rainbow (3.1.1)
45
+ rake (13.3.1)
46
+ regexp_parser (2.11.3)
47
+ rspec (3.13.2)
48
+ rspec-core (~> 3.13.0)
49
+ rspec-expectations (~> 3.13.0)
50
+ rspec-mocks (~> 3.13.0)
51
+ rspec-core (3.13.6)
52
+ rspec-support (~> 3.13.0)
53
+ rspec-expectations (3.13.5)
54
+ diff-lcs (>= 1.2.0, < 2.0)
55
+ rspec-support (~> 3.13.0)
56
+ rspec-mocks (3.13.7)
57
+ diff-lcs (>= 1.2.0, < 2.0)
58
+ rspec-support (~> 3.13.0)
59
+ rspec-support (3.13.7)
60
+ rubocop (1.84.1)
61
+ json (~> 2.3)
62
+ language_server-protocol (~> 3.17.0.2)
63
+ lint_roller (~> 1.1.0)
64
+ parallel (~> 1.10)
65
+ parser (>= 3.3.0.2)
66
+ rainbow (>= 2.2.2, < 4.0)
67
+ regexp_parser (>= 2.9.3, < 3.0)
68
+ rubocop-ast (>= 1.49.0, < 2.0)
69
+ ruby-progressbar (~> 1.7)
70
+ unicode-display_width (>= 2.4.0, < 4.0)
71
+ rubocop-ast (1.49.0)
72
+ parser (>= 3.3.7.2)
73
+ prism (~> 1.7)
74
+ rubocop-rspec (3.9.0)
75
+ lint_roller (~> 1.1)
76
+ rubocop (~> 1.81)
77
+ ruby-progressbar (1.13.0)
78
+ strings (0.2.1)
79
+ strings-ansi (~> 0.2)
80
+ unicode-display_width (>= 1.5, < 3.0)
81
+ unicode_utils (~> 1.4)
82
+ strings-ansi (0.2.0)
83
+ tty-color (0.6.0)
84
+ tty-cursor (0.7.1)
85
+ tty-prompt (0.23.1)
86
+ pastel (~> 0.8)
87
+ tty-reader (~> 0.8)
88
+ tty-reader (0.9.0)
89
+ tty-cursor (~> 0.7)
90
+ tty-screen (~> 0.8)
91
+ wisper (~> 2.0)
92
+ tty-screen (0.8.2)
93
+ tty-table (0.12.0)
94
+ pastel (~> 0.8)
95
+ strings (~> 0.2.0)
96
+ tty-screen (~> 0.8)
97
+ unicode-display_width (2.6.0)
98
+ unicode_utils (1.4.0)
99
+ uri (1.1.1)
100
+ whirly (0.4.0)
101
+ json
102
+ unicode-display_width (>= 1.1)
103
+ wisper (2.0.1)
104
+
105
+ PLATFORMS
106
+ ruby
107
+ x86_64-linux
108
+
109
+ DEPENDENCIES
110
+ neocities-red!
111
+ rspec
112
+ rubocop (~> 1.82)
113
+ rubocop-rspec (~> 3.8)
114
+
115
+ CHECKSUMS
116
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
117
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
118
+ faraday (2.14.1) sha256=a43cceedc1e39d188f4d2cdd360a8aaa6a11da0c407052e426ba8d3fb42ef61c
119
+ faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad
120
+ faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757
121
+ faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c
122
+ json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986
123
+ language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
124
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
125
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
126
+ multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8
127
+ neocities-red (1.0.5)
128
+ net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
129
+ parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
130
+ parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
131
+ pastel (0.8.0) sha256=481da9fb7d2f6e6b1a08faf11fa10363172dc40fd47848f096ae21209f805a75
132
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
133
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
134
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
135
+ rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
136
+ regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
137
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
138
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
139
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
140
+ rspec-mocks (3.13.7) sha256=0979034e64b1d7a838aaaddf12bf065ea4dc40ef3d4c39f01f93ae2c66c62b1c
141
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
142
+ rubocop (1.84.1) sha256=14cc626f355141f5a2ef53c10a68d66b13bb30639b26370a76559096cc6bcc1a
143
+ rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
144
+ rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
145
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
146
+ strings (0.2.1) sha256=933293b3c95cf85b81eb44b3cf673e3087661ba739bbadfeadf442083158d6fb
147
+ strings-ansi (0.2.0) sha256=90262d760ea4a94cc2ae8d58205277a343409c288cbe7c29416b1826bd511c88
148
+ tty-color (0.6.0) sha256=6f9c37ca3a4e2367fb2e6d09722762647d6f455c111f05b59f35730eeb24332a
149
+ tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48
150
+ tty-prompt (0.23.1) sha256=fcdbce905238993f27eecfdf67597a636bc839d92192f6a0eef22b8166449ec8
151
+ tty-reader (0.9.0) sha256=c62972c985c0b1566f0e56743b6a7882f979d3dc32ff491ed490a076f899c2b1
152
+ tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50
153
+ tty-table (0.12.0) sha256=fdc27a4750835c1a16efe19a0b857e3ced3652cc7aceafe6dca94908965b9939
154
+ unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a
155
+ unicode_utils (1.4.0) sha256=b922d0cf2313b6b7136ada6645ce7154ffc86418ca07d53b058efe9eb72f2a40
156
+ uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
157
+ whirly (0.4.0) sha256=3ffdf9097e711097442f6d83c91b8fc431d73224863f75b48f06fb850b3e596e
158
+ wisper (2.0.1) sha256=ce17bc5c3a166f241a2e6613848b025c8146fce2defba505920c1d1f3f88fae6
159
+
160
+ BUNDLED WITH
161
+ 4.0.6
data/README.md CHANGED
@@ -51,6 +51,10 @@ Now, that command is great for users, which uses static site generators (like Je
51
51
  - `neocities push --ignore-dotfiles .` is ignores all files/directories with '.' at the beginning.
52
52
  - `neocities push -e <folder> .` is ignores folders recursively (Ignoring the content that is inside of the target directory).
53
53
 
54
+ ### 3) diff
55
+
56
+ You can compare your local version of website with remote version.
57
+
54
58
  ## TODO'S:
55
59
 
56
60
  1) Refactor `cli.rb` or use `rails/thor` gem instead.
data/ext/mkrf_conf.rb CHANGED
@@ -5,7 +5,8 @@ require "rubygems/command"
5
5
  require "rubygems/dependency_installer"
6
6
  begin
7
7
  Gem::Command.build_args = ARGV
8
- rescue NoMethodError
8
+ rescue NoMethodError => e
9
+ warn "Gem::Command.build_args= not available: #{e.message}"
9
10
  end
10
11
  inst = Gem::DependencyInstaller.new
11
12
  begin
@@ -16,7 +16,7 @@ MAX_THREADS = 5
16
16
 
17
17
  module NeocitiesRed
18
18
  class CLI
19
- SUBCOMMANDS = %w[upload delete list info push logout pizza pull purge].freeze
19
+ SUBCOMMANDS = %w[upload delete list info push logout pizza pull purge diff].freeze
20
20
  HELP_SUBCOMMANDS = ["-h", "--help", "help"].freeze
21
21
  PENELOPE_MOUTHS = %w[^ o ~ - v U].freeze
22
22
  PENELOPE_EYES = %w[o ~ O].freeze
@@ -113,6 +113,75 @@ module NeocitiesRed
113
113
  send @subcmd
114
114
  end
115
115
 
116
+ def diff
117
+ display_diff_help_and_exit if @subargs.empty?
118
+
119
+ @ignore_dotfiles = false
120
+ @path = "."
121
+ @exclude = []
122
+
123
+ loop do
124
+ arg = @subargs[0]
125
+ break if arg.nil?
126
+
127
+ if arg == "--ignore-dotfiles"
128
+ @subargs.shift
129
+ @ignore_dotfiles = true
130
+
131
+ elsif arg == "-e"
132
+ @subargs.shift
133
+
134
+ base = Pathname.new(@path).expand_path
135
+ target = Pathname.new(@subargs[0]).expand_path
136
+ filepath = target.relative_path_from(base).to_s
137
+
138
+ if File.file?(target)
139
+ @exclude << filepath
140
+ elsif File.directory?(target)
141
+ @exclude += Dir.glob(
142
+ File.join(target, "**", "*"),
143
+ File::FNM_DOTMATCH
144
+ ).map do |path|
145
+ Pathname.new(path).expand_path.relative_path_from(base).to_s
146
+ end
147
+
148
+ @exclude << filepath
149
+ end
150
+
151
+ @subargs.shift
152
+
153
+ elsif File.directory?(arg)
154
+ @path = arg
155
+ @subargs.shift
156
+ end
157
+ end
158
+
159
+ added, modified, removed = Services::SiteDifference.new(
160
+ @client,
161
+ path: @path,
162
+ detail: false,
163
+ ignore_dotfiles: @ignore_dotfiles,
164
+ exclude: @exclude
165
+ ).show
166
+
167
+ # rubocop:disable Style/GuardClause
168
+ if removed.any?
169
+ puts @pastel.bold.red("Removed files")
170
+ puts removed
171
+ end
172
+
173
+ if modified.any?
174
+ puts @pastel.bold.yellow("Modified files")
175
+ puts modified
176
+ end
177
+
178
+ if added.any?
179
+ puts @pastel.bold.green("New files")
180
+ puts added
181
+ end
182
+ # rubocop:enable Style/GuardClause
183
+ end
184
+
116
185
  def delete
117
186
  display_delete_help_and_exit if @subargs.empty?
118
187
 
@@ -148,7 +217,7 @@ module NeocitiesRed
148
217
  def info
149
218
  profile_info = Services::ProfileInfo.new(@client, @subargs, @sitename).pretty_print
150
219
  puts TTY::Table.new(profile_info)
151
- rescue Exception => e
220
+ rescue StandardError => e
152
221
  display_response(e)
153
222
  end
154
223
 
@@ -161,7 +230,7 @@ module NeocitiesRed
161
230
 
162
231
  path = @subargs[0]
163
232
 
164
- Services::FileList.new(@client, path, @detail).show
233
+ puts Services::FileList.new(@client, path, @detail).show
165
234
  end
166
235
 
167
236
  def push
@@ -252,24 +321,17 @@ module NeocitiesRed
252
321
  Dir.chdir(root_path) do
253
322
  paths = Dir.glob(File.join("**", "*"), File::FNM_DOTMATCH)
254
323
 
255
- if @no_gitignore == false
256
- begin
257
- ignores = File.readlines(".gitignore").collect! do |ignore|
258
- ignore.strip!
259
- File.directory?(ignore) ? "#{ignore}**" : ignore
260
- end
261
- paths.select! do |path|
262
- res = true
263
- ignores.each do |ignore|
264
- if File.fnmatch?(ignore.strip, path)
265
- res = false
266
- break
267
- end
268
- end
269
- end
270
- puts "Not pushing .gitignore entries (--no-gitignore to disable)"
271
- rescue Errno::ENOENT
324
+ if @no_gitignore == false && File.exist?(".gitignore")
325
+ ignores = File.readlines(".gitignore").map do |ignore|
326
+ ignore = ignore.strip
327
+ File.directory?(ignore) ? "#{ignore}**" : ignore
328
+ end
329
+
330
+ paths.select! do |path|
331
+ ignores.none? { |ignore| File.fnmatch?(ignore, path) }
272
332
  end
333
+
334
+ puts "Not pushing .gitignore entries (--no-gitignore to disable)"
273
335
  end
274
336
 
275
337
  @excluded_files += paths.select { |path| path.start_with?(".") } if @ignore_dotfiles
@@ -322,7 +384,7 @@ module NeocitiesRed
322
384
  Services::FileUploader.new(@client, @subargs[0], @subargs[1]).upload
323
385
  elsif File.directory?(@subargs[0])
324
386
  folder_uploader = Services::FolderUploader.new(@client, @subargs[0], @subargs[1])
325
- files_list = folder_uploader.get_files
387
+ files_list = folder_uploader.files
326
388
  folder_uploader.upload(files_list)
327
389
  end
328
390
  end
@@ -370,11 +432,11 @@ module NeocitiesRed
370
432
 
371
433
  #{@pastel.dim 'Examples:'}
372
434
 
373
- #{@pastel.green '$ neocities list /'} List files in your root directory
435
+ #{@pastel.green '$ neocities-red list /'} List files in your root directory
374
436
 
375
- #{@pastel.green '$ neocities list -a'} Recursively display all files and directories
437
+ #{@pastel.green '$ neocities-red list -a'} Recursively display all files and directories
376
438
 
377
- #{@pastel.green '$ neocities list -d /mydir'} Show detailed information on /mydir
439
+ #{@pastel.green '$ neocities-red list -d /mydir'} Show detailed information on /mydir
378
440
 
379
441
  HERE
380
442
  exit
@@ -388,11 +450,11 @@ HERE
388
450
 
389
451
  #{@pastel.dim 'Examples:'}
390
452
 
391
- #{@pastel.green '$ neocities delete myfile.jpg'} Delete myfile.jpg
453
+ #{@pastel.green '$ neocities-red delete myfile.jpg'} Delete myfile.jpg
392
454
 
393
- #{@pastel.green '$ neocities delete myfile.jpg myfile2.jpg'} Delete myfile.jpg and myfile2.jpg
455
+ #{@pastel.green '$ neocities-red delete myfile.jpg myfile2.jpg'} Delete myfile.jpg and myfile2.jpg
394
456
 
395
- #{@pastel.green '$ neocities delete mydir'} Deletes mydir and everything inside it (be careful!)
457
+ #{@pastel.green '$ neocities-red delete mydir'} Deletes mydir and everything inside it (be careful!)
396
458
 
397
459
  HERE
398
460
  exit
@@ -406,9 +468,9 @@ HERE
406
468
 
407
469
  #{@pastel.dim 'Examples:'}
408
470
 
409
- #{@pastel.green '$ neocities upload ./img.jpg ./images/img2.jpg'} Upload img.jpg to /images folder and with img2.jpg name
471
+ #{@pastel.green '$ neocities-red upload ./img.jpg ./images/img2.jpg'} Upload img.jpg to /images folder and with img2.jpg name
410
472
 
411
- #{@pastel.green '$ neocities upload images/ images/'} Upload images folder with their content to /images folder
473
+ #{@pastel.green '$ neocities-red upload images/ images/'} Upload images folder with their content to /images folder
412
474
 
413
475
  HERE
414
476
  exit
@@ -432,20 +494,39 @@ HERE
432
494
 
433
495
  #{@pastel.dim 'Examples:'}
434
496
 
435
- #{@pastel.green '$ neocities push .'} Recursively upload current directory.
497
+ #{@pastel.green '$ neocities-red push .'} Recursively upload current directory.
498
+
499
+ #{@pastel.green '$ neocities-red push -e node_modules -e secret.txt .'} Exclude certain files from push
500
+
501
+ #{@pastel.green '$ neocities-red push --no-gitignore .'} Don't use .gitignore to exclude files
436
502
 
437
- #{@pastel.green '$ neocities push -e node_modules -e secret.txt .'} Exclude certain files from push
503
+ #{@pastel.green '$ neocities-red push --ignore-dotfiles .'} Ignore files with '.' at the beginning (for example, '.git/')
438
504
 
439
- #{@pastel.green '$ neocities push --no-gitignore .'} Don't use .gitignore to exclude files
505
+ #{@pastel.green '$ neocities-red push --dry-run .'} Just show what would be uploaded
440
506
 
441
- #{@pastel.green '$ neocities push --ignore-dotfiles .'} Ignore files with '.' at the beginning (for example, '.git/')
507
+ #{@pastel.green '$ neocities-red push --optimized .'} Do not upload unchanged files.#{' '}
508
+
509
+ #{@pastel.green '$ neocities-red push --prune .'} Delete site files not in dir (be careful!)
510
+
511
+ HERE
512
+ exit
513
+ end
514
+
515
+ def display_diff_help_and_exit
516
+ display_banner
517
+
518
+ puts <<HERE
519
+ #{@pastel.green.bold 'diff'} - Compare local files with remote and show differences.
520
+
521
+ #{@pastel.dim 'Examples:'}
442
522
 
443
- #{@pastel.green '$ neocities push --dry-run .'} Just show what would be uploaded
523
+ #{@pastel.green '$ neocities-red diff .'} Compare your current path with remote
444
524
 
445
- #{@pastel.green '$ neocities push --optimized .'} Do not upload unchanged files.#{' '}
525
+ #{@pastel.green '$ neocities-red diff ./my-website'} Compare ./my-website folder with remote
446
526
 
447
- #{@pastel.green '$ neocities push --prune .'} Delete site files not in dir (be careful!)
527
+ #{@pastel.green '$ neocities-red diff . --ignore-dotfile'} Compare your current path with remote without any files starts with '.'
448
528
 
529
+ #{@pastel.green '$ neocities-red diff . -e file.png'} Compare your current path with remote without file.png
449
530
  HERE
450
531
  exit
451
532
  end
@@ -458,7 +539,7 @@ HERE
458
539
 
459
540
  #{@pastel.dim 'Examples:'}
460
541
 
461
- #{@pastel.green '$ neocities info fauux'} Gets info for 'fauux' site
542
+ #{@pastel.green '$ neocities-red info fauux'} Gets info for 'fauux' site
462
543
 
463
544
  HERE
464
545
  exit
@@ -472,7 +553,7 @@ HERE
472
553
 
473
554
  #{@pastel.dim 'Examples:'}
474
555
 
475
- #{@pastel.green '$ neocities logout -y'}
556
+ #{@pastel.green '$ neocities-red logout -y'}
476
557
 
477
558
  HERE
478
559
  exit
@@ -495,6 +576,7 @@ HERE
495
576
  push Recursively upload a local directory to your site
496
577
  upload Upload individual files to your Neocities site
497
578
  delete Delete files from your Neocities site
579
+ diff Compare your local directory with your Neocities siteя
498
580
  list List files from your Neocities site
499
581
  info Information and stats for your site
500
582
  logout Remove the site api key from the config
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
4
- require "openssl/win/root" if Gem.win_platform?
5
- rescue StandardError
6
- end
7
-
3
+ require "openssl/win/root" if Gem.win_platform?
8
4
  require "json"
9
5
  require "pathname"
10
6
  require "uri"
@@ -32,9 +28,7 @@ module NeocitiesRed
32
28
  conn.response :follow_redirects
33
29
  end
34
30
 
35
- unless opts[:api_key] || (opts[:sitename] && opts[:password])
36
- raise ArgumentError, "client requires a login (sitename/password) or an api_key"
37
- end
31
+ raise ArgumentError, "client requires a login (sitename/password) or an api_key" unless opts[:api_key] || (opts[:sitename] && opts[:password])
38
32
 
39
33
  if opts[:api_key]
40
34
  @conn.request(:authorization, "Bearer", opts[:api_key])
@@ -47,7 +41,7 @@ module NeocitiesRed
47
41
  get "list", path: path
48
42
  end
49
43
 
50
- def pull(sitename, last_pull_time = nil, last_pull_loc = nil, quiet = true)
44
+ def pull(sitename, last_pull_time = nil, last_pull_loc = nil, quiet: true)
51
45
  site_info = get "info", sitename: sitename
52
46
 
53
47
  raise ArgumentError, site_info[:message] if site_info[:result] == "error"
@@ -121,7 +115,7 @@ module NeocitiesRed
121
115
  post "upload_hash", remote_path => sha1_hash
122
116
  end
123
117
 
124
- def upload(path, remote_path = nil, dry_run = false)
118
+ def upload(path, remote_path = nil, dry_run: false)
125
119
  path = Pathname path
126
120
  raise ArgumentError, "#{path} does not exist." unless path.exist?
127
121
 
@@ -143,7 +137,7 @@ module NeocitiesRed
143
137
  end
144
138
  end
145
139
 
146
- def delete_wrapper_with_dry_run(paths, dry_run = false)
140
+ def delete_wrapper_with_dry_run(paths, dry_run: false)
147
141
  return { result: "success" } if dry_run
148
142
 
149
143
  delete(paths)
@@ -13,6 +13,17 @@ module NeocitiesRed
13
13
  @pastel = Pastel.new(eachline: "\n")
14
14
  end
15
15
 
16
+ def list
17
+ resp = @client.list(@path)
18
+
19
+ if resp[:result] == "error"
20
+ display_response resp
21
+ exit
22
+ end
23
+
24
+ resp[:files]
25
+ end
26
+
16
27
  def show
17
28
  resp = @client.list(@path)
18
29
 
@@ -39,9 +50,11 @@ module NeocitiesRed
39
50
  puts TTY::Table.new(out)
40
51
  end
41
52
 
42
- resp[:files].each do |file|
43
- puts @pastel.send(file[:is_directory] ? :blue : :green).bold(file[:path])
53
+ resp[:files].map do |file|
54
+ @pastel.send(file[:is_directory] ? :blue : :green).bold(file[:path])
44
55
  end
56
+
57
+ resp[:files]
45
58
  end
46
59
  end
47
60
  end
@@ -19,7 +19,7 @@ module NeocitiesRed
19
19
  @pastel = Pastel.new(eachline: "\n")
20
20
  end
21
21
 
22
- def get_files
22
+ def files
23
23
  path = Pathname.new(File.expand_path(@filepath))
24
24
 
25
25
  raise FileIsNotExists, "#{path} does not exist locally." unless path.exist?
@@ -20,71 +20,85 @@ module NeocitiesRed
20
20
  "Today's special: disappointment. Pizza unavailable.",
21
21
  "Our last pizza became a perpetual motion machine, left the atmosphere and is flying through the heavens.",
22
22
  "Ran out of oregano and optimism. See you next time.",
23
- "WAR AND PEACE, BY LEO TOLSTOY, BOOK ONE: 1805, CHAPTER I “Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes. But I warn you, if you don’t tell me that this means war, if you still try to defend the infamies and horrors perpetrated by that Antichrist—I really believe he is Antichrist—I will have nothing more to do with you and you are no longer my friend, no longer my ‘faithful slave,’ as you call yourself! But how do you do? I see I have frightened you—sit down and tell me all the news.”
23
+ <<~TEXT
24
+ WAR AND PEACE, BY LEO TOLSTOY, BOOK ONE: 1805, CHAPTER I
24
25
 
25
- It was in July, 1805, and the speaker was the well-known Anna Pávlovna Schérer, maid of honor and favorite of the Empress Márya Fëdorovna. With these words she greeted Prince Vasíli Kurágin, a man of high rank and importance, who was the first to arrive at her reception. Anna Pávlovna had had a cough for some days. She was, as she said, suffering from la grippe; grippe being then a new word in St. Petersburg, used only by the elite.
26
+ “Well, Prince, so Genoa and Lucca are now just family estates of the Buonapartes.
27
+ But I warn you, if you don’t tell me that this means war, if you still try to defend
28
+ the infamies and horrors perpetrated by that Antichrist—I really believe he is
29
+ Antichrist—I will have nothing more to do with you and you are no longer my friend,
30
+ no longer my ‘faithful slave,’ as you call yourself! But how do you do? I see I have
31
+ frightened you—sit down and tell me all the news.”
26
32
 
27
- All her invitations without exception, written in French, and delivered by a scarlet-liveried footman that morning, ran as follows:
33
+ It was in July, 1805, and the speaker was the well-known Anna Pávlovna Schérer,
34
+ maid of honor and favorite of the Empress Márya Fëdorovna. With these words she
35
+ greeted Prince Vasíli Kurágin, a man of high rank and importance, who was the first
36
+ to arrive at her reception.
28
37
 
29
- “If you have nothing better to do, Count (or Prince), and if the prospect of spending an evening with a poor invalid is not too terrible, I shall be very charmed to see you tonight between 7 and 10—Annette Schérer.”
38
+ Anna Pávlovna had had a cough for some days. She was, as she said, suffering from
39
+ la grippe; grippe being then a new word in St. Petersburg, used only by the elite.
30
40
 
31
- “Heavens! what a virulent attack!” replied the prince, not in the least disconcerted by this reception. He had just entered, wearing an embroidered court uniform, knee breeches, and shoes, and had stars on his breast and a serene expression on his flat face. He spoke in that refined French in which our grandfathers not only spoke but thought, and with the gentle, patronizing intonation natural to a man of importance who had grown old in society and at court. He went up to Anna Pávlovna, kissed her hand, presenting to her his bald, scented, and shining head, and complacently seated himself on the sofa.
41
+ All her invitations without exception, written in French, and delivered by a
42
+ scarlet-liveried footman that morning, ran as follows:
32
43
 
33
- First of all, dear friend, tell me how you are. Set your friend’s mind at rest,” said he without altering his tone, beneath the politeness and affected sympathy of which indifference and even irony could be discerned.
44
+ If you have nothing better to do, Count (or Prince), and if the prospect of
45
+ spending an evening with a poor invalid is not too terrible, I shall be very
46
+ charmed to see you tonight between 7 and 10—Annette Schérer.”
34
47
 
35
- Can one be well while suffering morally? Can one be calm in times like these if one has any feeling?” said Anna Pávlovna. “You are staying the whole evening, I hope?”
48
+ Heavens! what a virulent attack!” replied the prince, not in the least disconcerted
49
+ by this reception.
36
50
 
37
- “And the fete at the English ambassador’s? Today is Wednesday. I must put in an appearance there,” said the prince. “My daughter is coming for me to take me there.”
51
+ He had just entered, wearing an embroidered court uniform, knee breeches, and
52
+ shoes, and had stars on his breast and a serene expression on his flat face.
38
53
 
39
- “I thought today’s fete had been canceled. I confess all these festivities and fireworks are becoming wearisome.”
54
+ He spoke in that refined French in which our grandfathers not only spoke but
55
+ thought, and with the gentle, patronizing intonation natural to a man of importance
56
+ who had grown old in society and at court.
40
57
 
41
- “If they had known that you wished it, the entertainment would have been put off,” said the prince, who, like a wound-up clock, by force of habit said things he did not even wish to be believed.
58
+ He went up to Anna Pávlovna, kissed her hand, presenting to her his bald, scented,
59
+ and shining head, and complacently seated himself on the sofa.
42
60
 
43
- Don’t tease! Well, and what has been decided about Novosíltsev’s dispatch? You know everything.”
61
+ First of all, dear friend, tell me how you are. Set your friend’s mind at rest,”
62
+ said he without altering his tone, beneath the politeness and affected sympathy of
63
+ which indifference and even irony could be discerned.
44
64
 
45
- What can one say about it?” replied the prince in a cold, listless tone. “What has been decided? They have decided that Buonaparte has burnt his boats, and I believe that we are ready to burn ours.”
65
+ Can one be well while suffering morally? Can one be calm in times like these if
66
+ one has any feeling?” said Anna Pávlovna. “You are staying the whole evening, I hope?”
46
67
 
47
- Prince Vasíli always spoke languidly, like an actor repeating a stale part. Anna Pávlovna Schérer on the contrary, despite her forty years, overflowed with animation and impulsiveness. To be an enthusiast had become her social vocation and, sometimes even when she did not feel like it, she became enthusiastic in order not to disappoint the expectations of those who knew her. The subdued smile which, though it did not suit her faded features, always played round her lips expressed, as in a spoiled child, a continual consciousness of her charming defect, which she neither wished, nor could, nor considered it necessary, to correct.
68
+ “And the fete at the English ambassador’s? Today is Wednesday. I must put in an
69
+ appearance there,” said the prince. “My daughter is coming for me to take me there.”
48
70
 
49
- In the midst of a conversation on political matters Anna Pávlovna burst out:
71
+ “I thought today’s fete had been canceled. I confess all these festivities and
72
+ fireworks are becoming wearisome.”
50
73
 
51
- Oh, don’t speak to me of Austria. Perhaps I don’t understand things, but Austria never has wished, and does not wish, for war. She is betraying us! Russia alone must save Europe. Our gracious sovereign recognizes his high vocation and will be true to it. That is the one thing I have faith in! Our good and wonderful sovereign has to perform the noblest role on earth, and he is so virtuous and noble that God will not forsake him. He will fulfill his vocation and crush the hydra of revolution, which has become more terrible than ever in the person of this murderer and villain! We alone must avenge the blood of the just one.... Whom, I ask you, can we rely on?... England with her commercial spirit will not and cannot understand the Emperor Alexander’s loftiness of soul. She has refused to evacuate Malta. She wanted to find, and still seeks, some secret motive in our actions. What answer did Novosíltsev get? None. The English have not understood and cannot understand the self-abnegation of our Emperor who wants nothing for himself, but only desires the good of mankind. And what have they promised? Nothing! And what little they have promised they will not perform! Prussia has always declared that Buonaparte is invincible, and that all Europe is powerless before him.... And I don’t believe a word that Hardenburg says, or Haugwitz either. This famous Prussian neutrality is just a trap. I have faith only in God and the lofty destiny of our adored monarch. He will save Europe!”
74
+ If they had known that you wished it, the entertainment would have been put off,”
75
+ said the prince, who, like a wound-up clock, by force of habit said things he did
76
+ not even wish to be believed.
52
77
 
53
- She suddenly paused, smiling at her own impetuosity.
78
+ “Don’t tease! Well, and what has been decided about Novosíltsev’s dispatch?
79
+ You know everything.”
54
80
 
55
- I think,” said the prince with a smile, “that if you had been sent instead of our dear Wintzingerode you would have captured the King of Prussia’s consent by assault. You are so eloquent. Will you give me a cup of tea?”
81
+ What can one say about it?” replied the prince in a cold, listless tone.
82
+ “What has been decided? They have decided that Buonaparte has burnt his boats,
83
+ and I believe that we are ready to burn ours.”
56
84
 
57
- “In a moment. À propos,” she added, becoming calm again, “I am expecting two very interesting men tonight, le Vicomte de Mortemart, who is connected with the Montmorencys through the Rohans, one of the best French families. He is one of the genuine émigrés, the good ones. And also the Abbé Morio. Do you know that profound thinker? He has been received by the Emperor. Had you heard?”
85
+ Prince Vasíli always spoke languidly, like an actor repeating a stale part.
86
+ Anna Pávlovna Schérer on the contrary, despite her forty years, overflowed with
87
+ animation and impulsiveness.
58
88
 
59
- “I shall be delighted to meet them,” said the prince. “But tell me,” he added with studied carelessness as if it had only just occurred to him, though the question he was about to ask was the chief motive of his visit, “is it true that the Dowager Empress wants Baron Funke to be appointed first secretary at Vienna? The baron by all accounts is a poor creature.”
89
+ To be an enthusiast had become her social vocation and, sometimes even when she
90
+ did not feel like it, she became enthusiastic in order not to disappoint the
91
+ expectations of those who knew her.
60
92
 
61
- Prince Vasíli wished to obtain this post for his son, but others were trying through the Dowager Empress Márya Fëdorovna to secure it for the baron.
62
-
63
- Anna Pávlovna almost closed her eyes to indicate that neither she nor anyone else had a right to criticize what the Empress desired or was pleased with.
64
-
65
- “Baron Funke has been recommended to the Dowager Empress by her sister,” was all she said, in a dry and mournful tone.
66
-
67
- As she named the Empress, Anna Pávlovna’s face suddenly assumed an expression of profound and sincere devotion and respect mingled with sadness, and this occurred every time she mentioned her illustrious patroness. She added that Her Majesty had deigned to show Baron Funke beaucoup d’estime, and again her face clouded over with sadness.
68
-
69
- The prince was silent and looked indifferent. But, with the womanly and courtierlike quickness and tact habitual to her, Anna Pávlovna wished both to rebuke him (for daring to speak as he had done of a man recommended to the Empress) and at the same time to console him, so she said:
70
-
71
- “Now about your family. Do you know that since your daughter came out everyone has been enraptured by her? They say she is amazingly beautiful.”
72
-
73
- The prince bowed to signify his respect and gratitude.
74
-
75
- “I often think,” she continued after a short pause, drawing nearer to the prince and smiling amiably at him as if to show that political and social topics were ended and the time had come for intimate conversation—“I often think how unfairly sometimes the joys of life are distributed. Why has fate given you two such splendid children? I don’t speak of Anatole, your youngest. I don’t like him,” she added in a tone admitting of no rejoinder and raising her eyebrows. “Two such charming children. And really you appreciate them less than anyone, and so you don’t deserve to have them.”
76
-
77
- And she smiled her ecstatic smile.
78
-
79
- “I can’t help it,” said the prince. “Lavater would have said I lack the bump of paternity.”
80
-
81
- “Don’t joke; I mean to have a serious talk with you. Do you know I am dissatisfied with your younger son? Between ourselves” (and her face assumed its melancholy expression), “he was mentioned at Her Majesty’s and you were pitied....”
82
-
83
- The prince answered nothing, but she looked at him significantly, awaiting a reply. He frowned."
93
+ The subdued smile which, though it did not suit her faded features, always played
94
+ round her lips expressed, as in a spoiled child, a continual consciousness of her
95
+ charming defect, which she neither wished, nor could, nor considered it necessary,
96
+ to correct.
97
+ TEXT
84
98
  ].freeze
85
99
 
86
100
  def make_order
87
- Pastel.new.bright_red(EXCUSES.sample)
101
+ Pastel.new(enabled: true).bright_red(EXCUSES.sample)
88
102
  end
89
103
  end
90
104
  end
@@ -17,7 +17,7 @@ module NeocitiesRed
17
17
  @pastel = Pastel.new(eachline: "\n")
18
18
  end
19
19
 
20
- def get_stats
20
+ def stats
21
21
  response = @client.info(@subargs[0] || @sitename)
22
22
 
23
23
  raise NeocitiesRed::Services::ClientError, response if response[:result] == "error"
@@ -28,7 +28,7 @@ module NeocitiesRed
28
28
  def pretty_print
29
29
  out = []
30
30
 
31
- get_stats[:info].each do |k, v|
31
+ stats[:info].each do |k, v|
32
32
  v = Time.parse(v).localtime if v && %i[created_at last_updated].include?(k)
33
33
 
34
34
  out << [@pastel.bold(k.to_s), v]
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+ require "tty/table"
5
+
6
+ module NeocitiesRed
7
+ module Services
8
+ class SiteDifference
9
+ def initialize(client, path: ".", detail: false, ignore_dotfiles: false, exclude: [])
10
+ @client = client
11
+ @path = path
12
+ @detail = detail || false
13
+ @ignore_dotfiles = ignore_dotfiles || false
14
+ @exclude = exclude || []
15
+ @pastel = Pastel.new(eachline: "\n")
16
+ end
17
+
18
+ def show
19
+ server_files = Services::FileList.new(@client, nil, @detail)
20
+ .show
21
+
22
+ root_path = Pathname(@path)
23
+
24
+ added_paths = []
25
+ removed_paths = []
26
+ modified_paths = []
27
+
28
+ Dir.chdir(root_path) do
29
+ paths = Dir.glob(File.join("**", "*"), File::FNM_DOTMATCH)
30
+
31
+ local_paths = paths.reject { |path| path.start_with?(".") }
32
+ local_files = local_paths.select { |path| File.file?(path) }
33
+ .map do |path|
34
+ {
35
+ path: path,
36
+ sha1_hash: Digest::SHA1.file(path).hexdigest
37
+ }
38
+ end
39
+ server_paths = server_files.map { |n| n[:path] }
40
+
41
+ server_file_map = server_files.each_with_object({}) do |file, hash|
42
+ hash[file[:path]] = file[:sha1_hash]
43
+ end
44
+
45
+ if @ignore_dotfiles
46
+ server_paths = server_paths.reject { |path| path.start_with?(".") }
47
+ local_paths = local_paths.reject { |path| path.start_with?(".") }
48
+ end
49
+
50
+ if @exclude.any?
51
+ server_paths -= @exclude
52
+ local_paths -= @exclude
53
+ end
54
+
55
+ removed_paths = server_paths - local_paths
56
+ removed_paths.map! { |file| @pastel.red(file) }
57
+
58
+ added_paths = local_paths - server_paths
59
+ added_paths.map! { |file| @pastel.green(file) }
60
+
61
+ modified_paths = local_files.select do |file|
62
+ server_hash = server_file_map[file[:path]]
63
+ server_hash && server_hash != file[:sha1_hash]
64
+ end
65
+ modified_paths.map! { |file| @pastel.yellow(file[:path]) }
66
+ end
67
+
68
+ [
69
+ added_paths,
70
+ modified_paths,
71
+ removed_paths
72
+ ]
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NeocitiesRed
4
- VERSION = "1.0.4"
4
+ VERSION = "1.0.5"
5
5
  end
data/lib/neocities_red.rb CHANGED
@@ -9,6 +9,7 @@ require File.join(File.dirname(__FILE__), "neocities_red", "services", "folder_u
9
9
  require File.join(File.dirname(__FILE__), "neocities_red", "services", "file_remover")
10
10
  require File.join(File.dirname(__FILE__), "neocities_red", "services", "file_list")
11
11
 
12
+ require File.join(File.dirname(__FILE__), "neocities_red", "services", "site_difference")
12
13
  require File.join(File.dirname(__FILE__), "neocities_red", "services", "profile_info")
13
14
  require File.join(File.dirname(__FILE__), "neocities_red", "services", "site_exporter")
14
15
  require File.join(File.dirname(__FILE__), "neocities_red", "services", "pizza")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neocities-red
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Drake
@@ -167,12 +167,14 @@ files:
167
167
  - ".github/ISSUE_TEMPLATE/bug_report.md"
168
168
  - ".github/PULL_REQUEST_TEMPLATE.md"
169
169
  - ".github/dependabot.yml"
170
+ - ".github/workflows/ci.yml"
170
171
  - ".gitignore"
171
172
  - ".rspec"
172
173
  - ".rubocop.yml"
173
174
  - CODE_OF_CONDUCT.md
174
175
  - CONTRIBUTING.md
175
176
  - Gemfile
177
+ - Gemfile.lock
176
178
  - LICENSE
177
179
  - README.md
178
180
  - SECURITY.md
@@ -187,6 +189,7 @@ files:
187
189
  - lib/neocities_red/services/folder_uploader.rb
188
190
  - lib/neocities_red/services/pizza.rb
189
191
  - lib/neocities_red/services/profile_info.rb
192
+ - lib/neocities_red/services/site_difference.rb
190
193
  - lib/neocities_red/services/site_exporter.rb
191
194
  - lib/neocities_red/version.rb
192
195
  - neocities-red.gemspec