git-cipher 0.1 → 0.2
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 +113 -19
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3d52bb65c34660958a91e70fef43d596610bede9
         | 
| 4 | 
            +
              data.tar.gz: 7d83c6b13437a4fa8c1d166b76fda86abdf9a990
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 53dec9da0d8e7bed2874793c25bde8e9fad254059bdccee796dec7b131a7191a6a863ced6873e797007167236de4a5f25dfc562b33e01a7506d98da32d7872cb
         | 
| 7 | 
            +
              data.tar.gz: e1c65440edaeb32687e7b124f6c497bd8e9413f4ef7c7f92a23d55d13ab29691a820eed6918ca819f6273e4308fb8c1a4d9cabd5939b4661da25ee785766e2f2
         | 
    
        data/bin/git-cipher
    CHANGED
    
    | @@ -4,13 +4,14 @@ | |
| 4 4 | 
             
            require 'fileutils'
         | 
| 5 5 | 
             
            require 'pathname'
         | 
| 6 6 | 
             
            require 'shellwords'
         | 
| 7 | 
            +
            require 'tempfile'
         | 
| 7 8 |  | 
| 8 9 | 
             
            class Cipher
         | 
| 9 10 | 
             
              EXTENSION = 'encrypted'
         | 
| 10 11 | 
             
              DEFAULT_GPG_USER  = 'greg@hurrell.net'
         | 
| 11 12 | 
             
              DEFAULT_GPG_PRESET_COMMAND = '/usr/local/opt/gpg-agent/libexec/gpg-preset-passphrase'
         | 
| 12 13 | 
             
              VALID_OPTIONS = %w[force help]
         | 
| 13 | 
            -
              VALID_SUBCOMMANDS = %w[decrypt encrypt help preset forget]
         | 
| 14 | 
            +
              VALID_SUBCOMMANDS = %w[decrypt encrypt help log preset forget]
         | 
| 14 15 |  | 
| 15 16 | 
             
              def run
         | 
| 16 17 | 
             
                send @subcommand
         | 
| @@ -69,14 +70,7 @@ private | |
| 69 70 | 
             
              end
         | 
| 70 71 |  | 
| 71 72 | 
             
              def decrypt!(file)
         | 
| 72 | 
            -
                 | 
| 73 | 
            -
                  die <<-MSG
         | 
| 74 | 
            -
                    GPG_AGENT_INFO not present in the environment.
         | 
| 75 | 
            -
                    Try running:
         | 
| 76 | 
            -
                      eval $(gpg-agent --daemon)
         | 
| 77 | 
            -
                      #{command_name} preset
         | 
| 78 | 
            -
                  MSG
         | 
| 79 | 
            -
                end
         | 
| 73 | 
            +
                require_agent
         | 
| 80 74 |  | 
| 81 75 | 
             
                pathname = Pathname.new(file)
         | 
| 82 76 | 
             
                basename = pathname.basename.to_s
         | 
| @@ -104,16 +98,7 @@ private | |
| 104 98 | 
             
                  puts red('[warning: plain-text newer than ciphertext; skipping]')
         | 
| 105 99 | 
             
                else
         | 
| 106 100 | 
             
                  print green('[decrypting ...')
         | 
| 107 | 
            -
                   | 
| 108 | 
            -
                    #{escape command_path('gpg')}
         | 
| 109 | 
            -
                      -q
         | 
| 110 | 
            -
                      --yes
         | 
| 111 | 
            -
                      --batch
         | 
| 112 | 
            -
                      --no-tty
         | 
| 113 | 
            -
                      --use-agent
         | 
| 114 | 
            -
                      -o #{escape outfile}
         | 
| 115 | 
            -
                      -d #{escape file}
         | 
| 116 | 
            -
                  })
         | 
| 101 | 
            +
                  gpg_decrypt(file, outfile)
         | 
| 117 102 | 
             
                  if $?.success?
         | 
| 118 103 | 
             
                    puts green(' done]')
         | 
| 119 104 |  | 
| @@ -219,6 +204,19 @@ private | |
| 219 204 | 
             
                puts
         | 
| 220 205 | 
             
              end
         | 
| 221 206 |  | 
| 207 | 
            +
              def gpg_decrypt(file, outfile)
         | 
| 208 | 
            +
                execute(%{
         | 
| 209 | 
            +
                  #{escape command_path('gpg')}
         | 
| 210 | 
            +
                    -q
         | 
| 211 | 
            +
                    --yes
         | 
| 212 | 
            +
                    --batch
         | 
| 213 | 
            +
                    --no-tty
         | 
| 214 | 
            +
                    --use-agent
         | 
| 215 | 
            +
                    -o #{escape outfile}
         | 
| 216 | 
            +
                    -d #{escape file}
         | 
| 217 | 
            +
                })
         | 
| 218 | 
            +
              end
         | 
| 219 | 
            +
             | 
| 222 220 | 
             
              def gpg_preset_command
         | 
| 223 221 | 
             
                ENV['GPG_PRESET_COMMAND'] || get_config('presetcommand') || DEFAULT_GPG_PRESET_COMMAND
         | 
| 224 222 | 
             
              end
         | 
| @@ -248,6 +246,64 @@ private | |
| 248 246 | 
             
                "\e[2K\e[0G"
         | 
| 249 247 | 
             
              end
         | 
| 250 248 |  | 
| 249 | 
            +
              def log
         | 
| 250 | 
            +
                if @files.empty?
         | 
| 251 | 
            +
                  # TODO: would be nice to interleave these instead of doing them serially.
         | 
| 252 | 
            +
                  puts 'No explicit paths supplied: logging all matching files'
         | 
| 253 | 
            +
                  Dir["**/.*.#{EXTENSION}"].each { |file| log!(file) }
         | 
| 254 | 
            +
                else
         | 
| 255 | 
            +
                  @files.each { |file| log!(file) }
         | 
| 256 | 
            +
                end
         | 
| 257 | 
            +
              end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
              def log!(file)
         | 
| 260 | 
            +
                require_agent
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                commits = execute(%{
         | 
| 263 | 
            +
                  #{escape command_path('git')} log
         | 
| 264 | 
            +
                  --pretty=format:%H -- #{escape file}
         | 
| 265 | 
            +
                }).split
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                commits.each do |commit|
         | 
| 268 | 
            +
                  files = []
         | 
| 269 | 
            +
                  begin
         | 
| 270 | 
            +
                    # Get plaintext "post" image.
         | 
| 271 | 
            +
                    files.push(post = temp_write(show(file, commit)))
         | 
| 272 | 
            +
                    files.push(
         | 
| 273 | 
            +
                      post_plaintext = temp_write(gpg_decrypt(post.path, '-'))
         | 
| 274 | 
            +
                    )
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                    # Get plaintext "pre" image.
         | 
| 277 | 
            +
                    files.push(pre = temp_write(show(file, "#{commit}~")))
         | 
| 278 | 
            +
                    files.push(pre_plaintext = temp_write(
         | 
| 279 | 
            +
                      pre.size.zero? ? '' : gpg_decrypt(pre.path, '-')
         | 
| 280 | 
            +
                    ))
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                    # Print commit message.
         | 
| 283 | 
            +
                    puts execute(%{
         | 
| 284 | 
            +
                      #{escape command_path('git')} --no-pager log
         | 
| 285 | 
            +
                      --color=always -1 #{commit}
         | 
| 286 | 
            +
                    })
         | 
| 287 | 
            +
                    puts
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                    # Print pre-to-post diff.
         | 
| 290 | 
            +
                    puts execute(%{
         | 
| 291 | 
            +
                      #{escape command_path('git')} --no-pager diff
         | 
| 292 | 
            +
                      --color=always
         | 
| 293 | 
            +
                      #{escape pre_plaintext.path}
         | 
| 294 | 
            +
                      #{escape post_plaintext.path}
         | 
| 295 | 
            +
                    })
         | 
| 296 | 
            +
                    puts
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                  ensure
         | 
| 299 | 
            +
                    files.each do |tempfile|
         | 
| 300 | 
            +
                      tempfile.close
         | 
| 301 | 
            +
                      tempfile.unlink
         | 
| 302 | 
            +
                    end
         | 
| 303 | 
            +
                  end
         | 
| 304 | 
            +
                end
         | 
| 305 | 
            +
              end
         | 
| 306 | 
            +
             | 
| 251 307 | 
             
              def normalize_option(option)
         | 
| 252 308 | 
             
                normal = option.dup
         | 
| 253 309 |  | 
| @@ -295,12 +351,40 @@ private | |
| 295 351 | 
             
                colorize(string, 31)
         | 
| 296 352 | 
             
              end
         | 
| 297 353 |  | 
| 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 | 
            +
              def show(file, commit)
         | 
| 366 | 
            +
                # Redirect stderr to /dev/null because the file might not have existed prior
         | 
| 367 | 
            +
                # to this commit.
         | 
| 368 | 
            +
                execute(%{
         | 
| 369 | 
            +
                  #{escape command_path('git')} show
         | 
| 370 | 
            +
                  #{commit}:#{escape file} 2> /dev/null
         | 
| 371 | 
            +
                })
         | 
| 372 | 
            +
              end
         | 
| 373 | 
            +
             | 
| 298 374 | 
             
              def strip_heredoc(doc)
         | 
| 299 375 | 
             
                # based on method of same name from Rails
         | 
| 300 376 | 
             
                indent = doc.scan(/^[ \t]*(?=\S)/).map(&:size).min || 0
         | 
| 301 377 | 
             
                doc.gsub(/^[ \t]{#{indent}}/, '')
         | 
| 302 378 | 
             
              end
         | 
| 303 379 |  | 
| 380 | 
            +
              def temp_write(contents)
         | 
| 381 | 
            +
                file = Tempfile.new('git-cipher-')
         | 
| 382 | 
            +
                file.chmod(0600)
         | 
| 383 | 
            +
                file.write(contents)
         | 
| 384 | 
            +
                file.flush
         | 
| 385 | 
            +
                file
         | 
| 386 | 
            +
              end
         | 
| 387 | 
            +
             | 
| 304 388 | 
             
              # Print usage information and exit.
         | 
| 305 389 | 
             
              def usage(subcommand)
         | 
| 306 390 | 
             
                case subcommand
         | 
| @@ -354,6 +438,15 @@ private | |
| 354 438 |  | 
| 355 439 | 
             
                          #{command_name} forget
         | 
| 356 440 | 
             
                  USAGE
         | 
| 441 | 
            +
                when 'log'
         | 
| 442 | 
            +
                  puts strip_heredoc(<<-USAGE)
         | 
| 443 | 
            +
                    #{command_name} log FILE
         | 
| 444 | 
            +
             | 
| 445 | 
            +
                      Shows the log message and decrypted diff for FILE
         | 
| 446 | 
            +
                      (analogous to `git log -p -- FILE`).
         | 
| 447 | 
            +
             | 
| 448 | 
            +
                          #{command_name} log foo
         | 
| 449 | 
            +
                  USAGE
         | 
| 357 450 | 
             
                when 'preset'
         | 
| 358 451 | 
             
                  puts strip_heredoc(<<-USAGE)
         | 
| 359 452 | 
             
                    #{command_name} preset
         | 
| @@ -369,6 +462,7 @@ private | |
| 369 462 | 
             
                        #{command_name} decrypt
         | 
| 370 463 | 
             
                        #{command_name} encrypt
         | 
| 371 464 | 
             
                        #{command_name} forget
         | 
| 465 | 
            +
                        #{command_name} log
         | 
| 372 466 | 
             
                        #{command_name} preset
         | 
| 373 467 | 
             
                  USAGE
         | 
| 374 468 | 
             
                end
         | 
    
        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.2'
         | 
| 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: 2016-02-08 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
         |