git-cipher 0.2 → 0.3

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 (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/git-cipher +38 -75
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d52bb65c34660958a91e70fef43d596610bede9
4
- data.tar.gz: 7d83c6b13437a4fa8c1d166b76fda86abdf9a990
3
+ metadata.gz: fafc4d52d9e64f31c6224872f4357038dbde9902
4
+ data.tar.gz: 95f89526e90879fd8cb111a5489f6d458a3d9609
5
5
  SHA512:
6
- metadata.gz: 53dec9da0d8e7bed2874793c25bde8e9fad254059bdccee796dec7b131a7191a6a863ced6873e797007167236de4a5f25dfc562b33e01a7506d98da32d7872cb
7
- data.tar.gz: e1c65440edaeb32687e7b124f6c497bd8e9413f4ef7c7f92a23d55d13ab29691a820eed6918ca819f6273e4308fb8c1a4d9cabd5939b4661da25ee785766e2f2
6
+ metadata.gz: 433b3749ae3175d9c4927a83bdd321fbe72b5cc0cc8d462d77ba0bb37a2b9cfdc8ce0652f37e64309c0b6f78a000e4a92217e39b93332eba68d153d7c8dec9f4
7
+ data.tar.gz: 2c8157fab0532f359d2acaf2ebed1eae6e9719467ec6c5e37e5f2b8344c7ff479c263d28146db0daae67df6093eb3f83bf7f50b5764647f9dd633b4939323254
data/bin/git-cipher CHANGED
@@ -9,9 +9,9 @@ require 'tempfile'
9
9
  class Cipher
10
10
  EXTENSION = 'encrypted'
11
11
  DEFAULT_GPG_USER = 'greg@hurrell.net'
12
- DEFAULT_GPG_PRESET_COMMAND = '/usr/local/opt/gpg-agent/libexec/gpg-preset-passphrase'
12
+ EXECUTABLE_EXTENSIONS = %w[.js .sh]
13
13
  VALID_OPTIONS = %w[force help]
14
- VALID_SUBCOMMANDS = %w[decrypt encrypt help log preset forget]
14
+ VALID_SUBCOMMANDS = %w[decrypt encrypt help log ls]
15
15
 
16
16
  def run
17
17
  send @subcommand
@@ -63,15 +63,13 @@ private
63
63
  def decrypt
64
64
  if @files.empty?
65
65
  puts 'No explicit paths supplied: decrypting all matching files'
66
- Dir["**/.*.#{EXTENSION}"].each { |file| decrypt!(file) }
66
+ matching.each { |file| decrypt!(file) }
67
67
  else
68
68
  @files.each { |file| decrypt!(file) }
69
69
  end
70
70
  end
71
71
 
72
72
  def decrypt!(file)
73
- require_agent
74
-
75
73
  pathname = Pathname.new(file)
76
74
  basename = pathname.basename.to_s
77
75
  unless basename.start_with?('.')
@@ -102,11 +100,11 @@ private
102
100
  if $?.success?
103
101
  puts green(' done]')
104
102
 
105
- File.chmod(0600, outfile)
103
+ File.chmod(mode(outfile), outfile)
106
104
 
107
- # mark plain-text as older than ciphertext, this will prevent a
105
+ # Mark plain-text as older than ciphertext, this will prevent a
108
106
  # bin/encrypt run from needlessly changing the contents of the ciphertext
109
- # (as the encryption is non-deterministic)
107
+ # (as the encryption is non-deterministic).
110
108
  time = File.mtime(file) - 1
111
109
  File.utime(time, time, outfile)
112
110
  else
@@ -127,7 +125,7 @@ private
127
125
  def encrypt
128
126
  if @files.empty?
129
127
  puts 'No explicit paths supplied: encrypting all matching files'
130
- Dir["**/.*.#{EXTENSION}"].each do |file|
128
+ matching.each do |file|
131
129
  file = Pathname.new(file)
132
130
  encrypt!(
133
131
  file.dirname +
@@ -171,7 +169,7 @@ private
171
169
  end
172
170
  end
173
171
 
174
- File.chmod(0600, file)
172
+ File.chmod(mode(file), file)
175
173
  check_ignored(file)
176
174
  end
177
175
 
@@ -183,11 +181,6 @@ private
183
181
  %x{#{string.gsub("\n", ' ')}}
184
182
  end
185
183
 
186
- def forget
187
- `#{escape command_path(gpg_preset_command)} --forget #{keygrip}`
188
- die 'gpg-preset-passphrase failed' unless $?.success?
189
- end
190
-
191
184
  def get_config(key)
192
185
  value = `#{escape command_path('git')} config cipher.#{key}`.chomp
193
186
  return if value.empty?
@@ -217,10 +210,6 @@ private
217
210
  })
218
211
  end
219
212
 
220
- def gpg_preset_command
221
- ENV['GPG_PRESET_COMMAND'] || get_config('presetcommand') || DEFAULT_GPG_PRESET_COMMAND
222
- end
223
-
224
213
  def gpg_user
225
214
  ENV['GPG_USER'] || get_config('gpguser') || DEFAULT_GPG_USER
226
215
  end
@@ -229,17 +218,6 @@ private
229
218
  colorize(string, 32)
230
219
  end
231
220
 
232
- def keygrip
233
- # get the subkey fingerprint and convert it into a 40-char hex code
234
- @keygrip ||= execute(%{
235
- #{escape command_path('gpg')} --fingerprint #{escape gpg_user} |
236
- grep fingerprint |
237
- tail -1 |
238
- cut -d= -f2 |
239
- sed -e 's/ //g'
240
- })
241
- end
242
-
243
221
  def kill_line
244
222
  # 2K deletes the line, 0G moves to column 0
245
223
  # see: http://en.wikipedia.org/wiki/ANSI_escape_code
@@ -250,19 +228,18 @@ private
250
228
  if @files.empty?
251
229
  # TODO: would be nice to interleave these instead of doing them serially.
252
230
  puts 'No explicit paths supplied: logging all matching files'
253
- Dir["**/.*.#{EXTENSION}"].each { |file| log!(file) }
231
+ matching.each { |file| log!(file) }
254
232
  else
255
233
  @files.each { |file| log!(file) }
256
234
  end
257
235
  end
258
236
 
259
237
  def log!(file)
260
- require_agent
261
-
262
238
  commits = execute(%{
263
239
  #{escape command_path('git')} log
264
240
  --pretty=format:%H -- #{escape file}
265
241
  }).split
242
+ suffix = "-#{File.basename(file)}"
266
243
 
267
244
  commits.each do |commit|
268
245
  files = []
@@ -270,13 +247,14 @@ private
270
247
  # Get plaintext "post" image.
271
248
  files.push(post = temp_write(show(file, commit)))
272
249
  files.push(
273
- post_plaintext = temp_write(gpg_decrypt(post.path, '-'))
250
+ post_plaintext = temp_write(gpg_decrypt(post.path, '-'), suffix)
274
251
  )
275
252
 
276
253
  # Get plaintext "pre" image.
277
254
  files.push(pre = temp_write(show(file, "#{commit}~")))
278
255
  files.push(pre_plaintext = temp_write(
279
- pre.size.zero? ? '' : gpg_decrypt(pre.path, '-')
256
+ pre.size.zero? ? '' : gpg_decrypt(pre.path, '-'),
257
+ suffix
280
258
  ))
281
259
 
282
260
  # Print commit message.
@@ -304,6 +282,24 @@ private
304
282
  end
305
283
  end
306
284
 
285
+ def ls
286
+ matching.each { |file| puts file }
287
+ end
288
+
289
+ def matching
290
+ Dir.glob("**/*.#{EXTENSION}", File::FNM_DOTMATCH)
291
+ end
292
+
293
+ # Determine the appropriate mode for the given decrypted plaintext
294
+ # `file` based on its file extension.
295
+ def mode(file)
296
+ if EXECUTABLE_EXTENSIONS.include?(Pathname.new(file).extname)
297
+ 0700
298
+ else
299
+ 0600
300
+ end
301
+ end
302
+
307
303
  def normalize_option(option)
308
304
  normal = option.dup
309
305
 
@@ -318,17 +314,6 @@ private
318
314
  found
319
315
  end
320
316
 
321
- def preset
322
- passphrase = get_passphrase
323
- command = "#{escape command_path(gpg_preset_command)} --preset #{keygrip}"
324
- IO.popen(command, 'w+') do |io|
325
- io.puts passphrase
326
- io.close_write
327
- puts io.read # usually silent
328
- end
329
- die 'gpg-preset-passphrase failed' unless $?.success?
330
- end
331
-
332
317
  def process_args
333
318
  options, files = ARGV.partition { |arg| arg.start_with?('-') }
334
319
  subcommand = files.shift
@@ -351,17 +336,6 @@ private
351
336
  colorize(string, 31)
352
337
  end
353
338
 
354
- def require_agent
355
- unless ENV['GPG_AGENT_INFO']
356
- die <<-MSG
357
- GPG_AGENT_INFO not present in the environment.
358
- Try running this before retrying `#{command_name} #{@subcommand}`:
359
- eval $(gpg-agent --daemon)
360
- #{command_name} preset
361
- MSG
362
- end
363
- end
364
-
365
339
  def show(file, commit)
366
340
  # Redirect stderr to /dev/null because the file might not have existed prior
367
341
  # to this commit.
@@ -377,9 +351,8 @@ private
377
351
  doc.gsub(/^[ \t]{#{indent}}/, '')
378
352
  end
379
353
 
380
- def temp_write(contents)
381
- file = Tempfile.new('git-cipher-')
382
- file.chmod(0600)
354
+ def temp_write(contents, suffix = '')
355
+ file = Tempfile.new(['git-cipher-', suffix])
383
356
  file.write(contents)
384
357
  file.flush
385
358
  file
@@ -430,14 +403,6 @@ private
430
403
  #{command_name} encrypt -f
431
404
  #{command_name} encrypt --force # (alternative syntax)
432
405
  USAGE
433
- when 'forget'
434
- puts strip_heredoc(<<-USAGE)
435
- #{command_name} forget
436
-
437
- Forget passphrase previously stored using `#{command_name} preset`:
438
-
439
- #{command_name} forget
440
- USAGE
441
406
  when 'log'
442
407
  puts strip_heredoc(<<-USAGE)
443
408
  #{command_name} log FILE
@@ -447,13 +412,12 @@ private
447
412
 
448
413
  #{command_name} log foo
449
414
  USAGE
450
- when 'preset'
415
+ when 'ls'
451
416
  puts strip_heredoc(<<-USAGE)
452
- #{command_name} preset
453
-
454
- Store a passphrase in a running `gpg-agent` agent:
417
+ #{command_name} ls
455
418
 
456
- #{command_name} preset
419
+ Lists the encrypted files in the current directory and
420
+ its subdirectories.
457
421
  USAGE
458
422
  else
459
423
  puts strip_heredoc(<<-USAGE)
@@ -461,9 +425,8 @@ private
461
425
 
462
426
  #{command_name} decrypt
463
427
  #{command_name} encrypt
464
- #{command_name} forget
465
428
  #{command_name} log
466
- #{command_name} preset
429
+ #{command_name} ls
467
430
  USAGE
468
431
  end
469
432
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-cipher
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.2'
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Hurrell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-08 00:00:00.000000000 Z
11
+ date: 2017-04-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: "\n Provides a convenient workflow for working with encrypted files
14
14
  in a public\n Git repo. Delegates the underlying work of encryption/decryption
@@ -43,7 +43,7 @@ requirements:
43
43
  - Git
44
44
  - GnuPG
45
45
  rubyforge_project:
46
- rubygems_version: 2.0.14
46
+ rubygems_version: 2.0.14.1
47
47
  signing_key:
48
48
  specification_version: 4
49
49
  summary: Manages encrypted content in a Git repo