paperback 0.0.3 → 0.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.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/tests.yml +46 -0
  3. data/.rubocop-disables.yml +26 -12
  4. data/CHANGELOG.md +21 -0
  5. data/README.md +27 -6
  6. data/bin/paperback +4 -0
  7. data/lib/paperback/cli.rb +38 -2
  8. data/lib/paperback/document.rb +134 -17
  9. data/lib/paperback/preparer.rb +107 -29
  10. data/lib/paperback/version.rb +2 -1
  11. data/lib/paperback.rb +12 -0
  12. data/paperback.gemspec +10 -7
  13. data/sorbet/config +3 -0
  14. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  15. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  16. data/sorbet/rbi/gems/chunky_png@1.4.0.rbi +4498 -0
  17. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  18. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  19. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  20. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  21. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  22. data/sorbet/rbi/gems/parser@3.2.0.0.rbi +6963 -0
  23. data/sorbet/rbi/gems/pdf-core@0.4.0.rbi +1682 -0
  24. data/sorbet/rbi/gems/prawn@1.3.0.rbi +5567 -0
  25. data/sorbet/rbi/gems/pry@0.14.1.rbi +9990 -0
  26. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +408 -0
  27. data/sorbet/rbi/gems/rake@13.0.6.rbi +3023 -0
  28. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3008 -0
  29. data/sorbet/rbi/gems/regexp_parser@2.6.1.rbi +3481 -0
  30. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  31. data/sorbet/rbi/gems/rqrcode@0.10.1.rbi +617 -0
  32. data/sorbet/rbi/gems/rspec-core@3.12.0.rbi +10791 -0
  33. data/sorbet/rbi/gems/rspec-expectations@3.12.1.rbi +8106 -0
  34. data/sorbet/rbi/gems/rspec-mocks@3.12.1.rbi +5305 -0
  35. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +1617 -0
  36. data/sorbet/rbi/gems/rspec@3.12.0.rbi +88 -0
  37. data/sorbet/rbi/gems/rubocop-ast@1.24.1.rbi +6617 -0
  38. data/sorbet/rbi/gems/rubocop@0.93.1.rbi +40848 -0
  39. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1234 -0
  40. data/sorbet/rbi/gems/sixword@0.4.0.rbi +536 -0
  41. data/sorbet/rbi/gems/spoom@1.1.15.rbi +2383 -0
  42. data/sorbet/rbi/gems/subprocess@1.5.6.rbi +391 -0
  43. data/sorbet/rbi/gems/tapioca@0.10.5.rbi +3207 -0
  44. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  45. data/sorbet/rbi/gems/ttfunk@1.4.0.rbi +1951 -0
  46. data/sorbet/rbi/gems/unicode-display_width@1.8.0.rbi +40 -0
  47. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  48. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2555 -0
  49. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +441 -0
  50. data/sorbet/rbi/gems/yard@0.9.28.rbi +17816 -0
  51. data/sorbet/tapioca/config.yml +13 -0
  52. data/sorbet/tapioca/require.rb +4 -0
  53. data/spec/functional/paperback/cli_spec.rb +195 -0
  54. data/spec/spec_helper.rb +1 -0
  55. data/spec/unit/paperback_spec.rb +1 -0
  56. metadata +91 -7
@@ -0,0 +1,13 @@
1
+ gem:
2
+ # Add your `gem` command parameters here:
3
+ #
4
+ # exclude:
5
+ # - gem_name
6
+ # doc: true
7
+ # workers: 5
8
+ dsl:
9
+ # Add your `dsl` command parameters here:
10
+ #
11
+ # exclude:
12
+ # - SomeGeneratorName
13
+ # workers: 5
@@ -0,0 +1,4 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # Add your extra requires here (`bin/tapioca require` can be used to bootstrap this list)
@@ -0,0 +1,195 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'digest/sha2'
5
+ require 'fileutils'
6
+ require 'tmpdir'
7
+
8
+ require 'subprocess'
9
+
10
+ RSpec.describe Paperback::CLI do
11
+ Executable = File.dirname(__FILE__) + '/../../bin/paperback'
12
+
13
+ SupportedPopplerVersions = [
14
+ /\A0\.86\..*\z/,
15
+ /\A22\.02\..*\z/,
16
+ ]
17
+
18
+ def get_pdftotext_version
19
+ output = Subprocess.check_output(
20
+ ['pdftotext', '-v'], stderr: Subprocess::STDOUT
21
+ )
22
+ first_line = output.lines(chomp: true)[0]
23
+ if first_line.nil?
24
+ raise Exception('Unexpected version line from pdftotext: ' + output.inspect)
25
+ end
26
+
27
+ parts = first_line.split(' ')
28
+ if parts[0] != 'pdftotext' || parts[1] != 'version'
29
+ raise Exception('Bad version line from pdftotext: ' + first_line.inspect)
30
+ end
31
+
32
+ return parts[2..].join(' ')
33
+ end
34
+
35
+ # pdftotext from poppler is a test dependency
36
+ it 'finds system pdftotext installed (test dependency)' do
37
+ expect(Subprocess.check_output(%w[which pdftotext])
38
+ ).to match(/pdftotext/)
39
+
40
+ version = get_pdftotext_version
41
+
42
+ expect(version).to match(/\A\d+\..*/)
43
+
44
+ unless SupportedPopplerVersions.any? {|regex| regex.match(version) }
45
+ Paperback.log.warn("#{File.basename(__FILE__)}: Unsupported poppler-utils pdftotext version #{version}, tests may fail!")
46
+ end
47
+ end
48
+
49
+ describe 'end-to-end test:' do
50
+ before(:each) do
51
+ @tmpdir = Dir.mktmpdir('paperback-rspec.')
52
+ @orig_cwd = Dir.pwd
53
+ Dir.chdir(@tmpdir)
54
+ end
55
+
56
+ after(:each) do
57
+ Dir.chdir(@orig_cwd) if @orig_cwd
58
+ FileUtils.rm_r(@tmpdir) if @tmpdir
59
+ @tmpdir = nil
60
+ end
61
+
62
+ it 'backs up unencrypted content correctly' do
63
+ secret = "The quick brown fox jumps over the lazy dog.\n"
64
+ sha = 'b47cc0f104b62d4c7c30bcd68fd8e67613e287dc4ad8c310ef10cbadea9c4380'
65
+
66
+ File.write('fox.txt', secret)
67
+
68
+ expect(Digest::SHA256.hexdigest(File.read('fox.txt'))).to eq(sha)
69
+
70
+ timestamp = Time.now.strftime('%F %T %z')
71
+
72
+ expected = <<-EOM
73
+ This is a paper backup produced by `paperback`. https://github.com/ab/paperback
74
+
75
+ Filename: fox.txt
76
+ Backed up: #{timestamp}
77
+ Mtime: #{timestamp}
78
+ Bytes: 45
79
+ Comment: Some comment
80
+ SHA256: b47cc0f104b62d4c7c30bcd68fd8e67613e287dc4ad8c310ef10cbadea9c4380
81
+ Encrypted: no
82
+
83
+
84
+ 1 of 2
85
+ \fThis sixword text encodes 45 bytes in 6 six-word sentences. Decode with `sixword -d`.
86
+
87
+ 1: beak us ache sour bern lola
88
+ 2: core arc hulk slid drew due
89
+ 3: chub ends bog russ bess mast
90
+ 4: dime arm skew quit kim san
91
+ 5: coat mod hit need knew grim
92
+ 6: cave ella raft fir a act3
93
+
94
+
95
+ This base64 text encodes 45 bytes in 61 characters. Decode with `base64 --decode`.
96
+
97
+ VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4K
98
+
99
+
100
+ 2 of 2
101
+ \f
102
+ EOM
103
+
104
+ # strip trailing newline and extra runs of spaces or newlines
105
+ expected = expected[0...-1].gsub(/ +/, ' ').gsub(/\n\n+/, "\n")
106
+
107
+ # create the backup file!
108
+ Paperback::CLI.create_backup(
109
+ input: 'fox.txt', output: 'fox.pdf', encrypt: false,
110
+ comment: 'Some comment'
111
+ )
112
+
113
+ # make sure we got a PDF
114
+ expect(File.size('fox.pdf')).to be > 2000
115
+ expect(File.read('fox.pdf', 20)).to start_with("%PDF-1.3\n")
116
+
117
+ # convert to text to see what's in it
118
+ Subprocess.check_call(%w[pdftotext -layout fox.pdf fox.pdftotext.txt])
119
+
120
+ expect(File.size('fox.pdftotext.txt')).to be > 100
121
+
122
+ # This test is likely to break given even minor changes to paperback or
123
+ # pdftotext, so it may not be particularly useful. However, it's nice to
124
+ # at least have some kind of end-to-end test.
125
+ converted_text = File.read('fox.pdftotext.txt')
126
+ converted_text_stripped = converted_text.gsub(/ +/, ' ').gsub(/\n\n+/, "\n")
127
+ expect(converted_text_stripped).to eq(expected)
128
+ end
129
+
130
+ it 'backs up encrypted content correctly' do
131
+ secret = 'Ey9V03Iuq/gKLH0aVf2aPTAULWydpWt6G/L+O7LJXHeK'
132
+ sha = '8675e6be1ae84a781b2a81c65b3300ee0611ccb3ad1e0b8e8dad5a94b6d4eef2'
133
+
134
+ File.write('2.txt', secret)
135
+ expect(Digest::SHA256.hexdigest(File.read('2.txt'))).to eq(sha)
136
+
137
+ # create the backup file!
138
+ Paperback::CLI.create_backup(
139
+ input: '2.txt', output: '2.pdf', encrypt: true,
140
+ passphrase_file: '2.passphrase.txt'
141
+ )
142
+
143
+ # make sure we got a PDF
144
+ expect(File.size('2.pdf')).to be > 2000
145
+ expect(File.read('2.pdf', 20)).to start_with("%PDF-1.3\n")
146
+
147
+ # make sure we got a passphrase
148
+ pass = File.read('2.passphrase.txt')
149
+ expect(pass.length).to eq 43
150
+ pass_sha = Digest::SHA256.hexdigest(pass)
151
+
152
+ # convert pdf to text
153
+ Subprocess.check_call(%w[pdftotext 2.pdf 2.pdftotext.txt])
154
+
155
+ expect(File.size('2.pdftotext.txt')).to be > 100
156
+
157
+ content = File.read('2.pdftotext.txt')
158
+
159
+ expect(content).to include('2.txt')
160
+ expect(content).to include(sha)
161
+ expect(content).to include(pass_sha[0...16])
162
+
163
+ # locate sixword content
164
+ expect(content).to include('This sixword text')
165
+ pages = content.split("\f")
166
+ pg2 = pages.fetch(1)
167
+ lines = pg2.split("\n")
168
+ expect(lines.fetch(0)).to start_with('This sixword text')
169
+
170
+ # pull sixword lines until blank line
171
+ sixword_lines = []
172
+ lines[1..-1].each do |line|
173
+ break if line.strip.empty?
174
+ expect(line).to match(/\A\d+: [a-z ]+[0-9]?\z/)
175
+ sixword_lines << line.split(':').fetch(1)
176
+ end
177
+
178
+ # decode sixword content
179
+ decoded = Sixword.decode(sixword_lines.join("\n"), padding_ok: true)
180
+
181
+ # decrypt data
182
+ File.write('2.sixword.gpg', decoded)
183
+ p = Subprocess::Process.new(
184
+ %w[gpg --batch --passphrase-fd 0 --decrypt 2.sixword.gpg],
185
+ stdin: Subprocess::PIPE, stdout: Subprocess::PIPE
186
+ )
187
+ decrypted, _ = p.communicate(pass)
188
+ expect(p.wait.exitstatus).to eq 0
189
+ expect(p.wait.success?).to eq true
190
+
191
+ expect(decrypted).to eq(secret)
192
+ end
193
+
194
+ end
195
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  require_relative '../lib/paperback'
2
3
 
3
4
  # Given that it is always loaded, you are encouraged to keep this file as
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  RSpec.describe Paperback do
2
3
  it 'should have a version number' do
3
4
  expect(Paperback::VERSION).to be_a(String)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Brody
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-15 00:00:00.000000000 Z
11
+ date: 2023-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.50'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sorbet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: tapioca
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.10'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.10'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: yard
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +164,20 @@ dependencies:
136
164
  - - "~>"
137
165
  - !ruby/object:Gem::Version
138
166
  version: '0.3'
167
+ - !ruby/object:Gem::Dependency
168
+ name: sorbet-runtime
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.5'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.5'
139
181
  - !ruby/object:Gem::Dependency
140
182
  name: subprocess
141
183
  requirement: !ruby/object:Gem::Requirement
@@ -160,6 +202,7 @@ executables:
160
202
  extensions: []
161
203
  extra_rdoc_files: []
162
204
  files:
205
+ - ".github/workflows/tests.yml"
163
206
  - ".gitignore"
164
207
  - ".rspec"
165
208
  - ".rubocop-disables.yml"
@@ -177,6 +220,47 @@ files:
177
220
  - lib/paperback/preparer.rb
178
221
  - lib/paperback/version.rb
179
222
  - paperback.gemspec
223
+ - sorbet/config
224
+ - sorbet/rbi/annotations/rainbow.rbi
225
+ - sorbet/rbi/gems/ast@2.4.2.rbi
226
+ - sorbet/rbi/gems/chunky_png@1.4.0.rbi
227
+ - sorbet/rbi/gems/coderay@1.1.3.rbi
228
+ - sorbet/rbi/gems/diff-lcs@1.5.0.rbi
229
+ - sorbet/rbi/gems/method_source@1.0.0.rbi
230
+ - sorbet/rbi/gems/netrc@0.11.0.rbi
231
+ - sorbet/rbi/gems/parallel@1.22.1.rbi
232
+ - sorbet/rbi/gems/parser@3.2.0.0.rbi
233
+ - sorbet/rbi/gems/pdf-core@0.4.0.rbi
234
+ - sorbet/rbi/gems/prawn@1.3.0.rbi
235
+ - sorbet/rbi/gems/pry@0.14.1.rbi
236
+ - sorbet/rbi/gems/rainbow@3.1.1.rbi
237
+ - sorbet/rbi/gems/rake@13.0.6.rbi
238
+ - sorbet/rbi/gems/rbi@0.0.16.rbi
239
+ - sorbet/rbi/gems/regexp_parser@2.6.1.rbi
240
+ - sorbet/rbi/gems/rexml@3.2.5.rbi
241
+ - sorbet/rbi/gems/rqrcode@0.10.1.rbi
242
+ - sorbet/rbi/gems/rspec-core@3.12.0.rbi
243
+ - sorbet/rbi/gems/rspec-expectations@3.12.1.rbi
244
+ - sorbet/rbi/gems/rspec-mocks@3.12.1.rbi
245
+ - sorbet/rbi/gems/rspec-support@3.12.0.rbi
246
+ - sorbet/rbi/gems/rspec@3.12.0.rbi
247
+ - sorbet/rbi/gems/rubocop-ast@1.24.1.rbi
248
+ - sorbet/rbi/gems/rubocop@0.93.1.rbi
249
+ - sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi
250
+ - sorbet/rbi/gems/sixword@0.4.0.rbi
251
+ - sorbet/rbi/gems/spoom@1.1.15.rbi
252
+ - sorbet/rbi/gems/subprocess@1.5.6.rbi
253
+ - sorbet/rbi/gems/tapioca@0.10.5.rbi
254
+ - sorbet/rbi/gems/thor@1.2.1.rbi
255
+ - sorbet/rbi/gems/ttfunk@1.4.0.rbi
256
+ - sorbet/rbi/gems/unicode-display_width@1.8.0.rbi
257
+ - sorbet/rbi/gems/unparser@0.6.7.rbi
258
+ - sorbet/rbi/gems/webrick@1.7.0.rbi
259
+ - sorbet/rbi/gems/yard-sorbet@0.8.0.rbi
260
+ - sorbet/rbi/gems/yard@0.9.28.rbi
261
+ - sorbet/tapioca/config.yml
262
+ - sorbet/tapioca/require.rb
263
+ - spec/functional/paperback/cli_spec.rb
180
264
  - spec/spec_helper.rb
181
265
  - spec/unit/paperback_spec.rb
182
266
  homepage: https://github.com/ab/paperback
@@ -191,18 +275,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
275
  requirements:
192
276
  - - ">="
193
277
  - !ruby/object:Gem::Version
194
- version: '2.0'
278
+ version: '2.7'
195
279
  required_rubygems_version: !ruby/object:Gem::Requirement
196
280
  requirements:
197
281
  - - ">="
198
282
  - !ruby/object:Gem::Version
199
283
  version: '0'
200
284
  requirements: []
201
- rubyforge_project:
202
- rubygems_version: 2.5.2.3
285
+ rubygems_version: 3.3.26
203
286
  signing_key:
204
287
  specification_version: 4
205
288
  summary: Create paper backups of sensitive data
206
289
  test_files:
290
+ - spec/functional/paperback/cli_spec.rb
207
291
  - spec/spec_helper.rb
208
292
  - spec/unit/paperback_spec.rb