-
Notifications
You must be signed in to change notification settings - Fork 6
/
envcrypt.go
103 lines (89 loc) · 2.34 KB
/
envcrypt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright 2014 Will Maier <[email protected]>. All rights reserved.
// See LICENSE for licensing information.
package main
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"path"
"strings"
)
var (
version string // this is set at build/install time; see Makefile
flagVersion = flag.Bool("v", false, "print program version")
)
// The decrypt function uses gpg to open and decrypt a file.
// Stdout is captured for later parsing, but stderr is allowed to pass through.
func decrypt(path string) ([]byte, error) {
cmd := exec.Command("gpg", "-q", "--batch", "-d", path)
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
return nil, err
}
return out, nil
}
func isNewLine(r rune) bool {
switch r {
case '\n':
return true
}
return false
}
// The parse function splits a byte array on whitespace.
func parse(data []byte) ([]string, error) {
return strings.FieldsFunc(string(data), isNewLine), nil
}
// The run function runs a command in an environment.
// Stdout and stderr are preserved.
func run(command []string, env []string) error {
cmd := exec.Command(command[0], command[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = env
return cmd.Run()
}
func usage() {
self := path.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "usage: %s PATH COMMAND [ARGS...]\n\n", self)
fmt.Fprint(os.Stderr, "Set environment variables defined in encrypted file PATH and run COMMAND.\n\n")
fmt.Fprintln(os.Stderr, " -v print program version")
fmt.Fprintln(os.Stderr, "Arguments:")
fmt.Fprintln(os.Stderr, " PATH path to a gpg-encrypted file that can be read with eg `gpg -d PATH`")
fmt.Fprintln(os.Stderr, " COMMAND command to be invoked in the context of the environment defined in PATH")
os.Exit(2)
}
func main() {
flag.Usage = usage
flag.Parse()
args := flag.Args()
if *flagVersion {
fmt.Fprintln(os.Stdout, version)
os.Exit(0)
}
if len(args) < 2 {
usage()
}
path := args[0]
command := args[1:]
env := []string{}
out, err := decrypt(path)
if err != nil {
log.Fatal(err)
}
fields, err := parse(out)
if err != nil {
log.Fatal(err)
}
// Construct a new environment where values from the decrypted file
// supercede values from our own environment.
env = append(env, fields...)
env = append(env, os.Environ()...)
err = run(command, env)
if err != nil {
log.Fatal(err)
}
}