git-cipher 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/git-cipher +38 -75
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fafc4d52d9e64f31c6224872f4357038dbde9902
|
4
|
+
data.tar.gz: 95f89526e90879fd8cb111a5489f6d458a3d9609
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
12
|
+
EXECUTABLE_EXTENSIONS = %w[.js .sh]
|
13
13
|
VALID_OPTIONS = %w[force help]
|
14
|
-
VALID_SUBCOMMANDS = %w[decrypt encrypt help log
|
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
|
-
|
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(
|
103
|
+
File.chmod(mode(outfile), outfile)
|
106
104
|
|
107
|
-
#
|
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
|
-
|
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(
|
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
|
-
|
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 '
|
415
|
+
when 'ls'
|
451
416
|
puts strip_heredoc(<<-USAGE)
|
452
|
-
#{command_name}
|
453
|
-
|
454
|
-
Store a passphrase in a running `gpg-agent` agent:
|
417
|
+
#{command_name} ls
|
455
418
|
|
456
|
-
|
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}
|
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.
|
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:
|
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
|