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.
- 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
|