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
104
105
106
107
108
109
110
111
112
113
114
115
|
diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..964e36915d5a66fbfccf4c2fad782f31d4b2398b
--- /dev/null
+++ b/cmd/cmd_test.go
@@ -0,0 +1,109 @@
+package cmd
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/matryer/is"
+)
+
+const (
+ ageIntro = "age-encryption.org/v1"
+ ageSecretContent = "Super duper secret age text!"
+ sshSecretContent = "Super duper secret ssh text!"
+ newAgeSecretContent = "Super duper secret age text!!"
+)
+
+func TestGitAge(t *testing.T) {
+ assert := is.New(t)
+
+ gitDir, err := gitBaseDir()
+ assert.NoErr(err) // Should get git base dir
+ tmp := t.TempDir()
+ clone := exec.Command("git", "clone", gitDir, tmp)
+ clone.Dir = tmp
+ assert.NoErr(clone.Run()) // Should clone project to temp dir
+
+ ageSecretPath := filepath.Join(tmp, "secrets", "age.txt")
+ assertEncrypted(assert, ageSecretPath) // Age secret should be encrypted before init
+ sshSecretPath := filepath.Join(tmp, "secrets", "ssh.txt")
+ assertEncrypted(assert, sshSecretPath) // SSH secret should be encrypted before init
+
+ err = os.Chdir(tmp)
+ assert.NoErr(err) // Should change to temp dir
+ build := exec.Command("go", "build")
+ build.Dir = tmp
+ err = build.Run()
+ assert.NoErr(err) // Should build git-age
+ binPath := filepath.Join(tmp, "git-age")
+ if runtime.GOOS == "windows" {
+ binPath += ".exe"
+ }
+ bin := func(args ...string) error {
+ c := exec.Command(binPath, args...)
+ c.Dir = tmp
+ return c.Run()
+ }
+ assertGitCatFileEncrypted(assert) // cat-file should always be encrypted (initial clone)
+
+ // Init should do nothing at first
+ err = bin("init")
+ assert.NoErr(err) // Should successfully run init
+ assertEncrypted(assert, ageSecretPath) // Age secret should be encrypted on init without identities
+ assertEncrypted(assert, sshSecretPath) // SSH secret should be encrypted on init without identities
+
+ // Add identities
+ err = bin("ident", "key.txt")
+ assert.NoErr(err) // Should add age identity
+ err = bin("ident", "ssh")
+ assert.NoErr(err) // Should add ssh identity
+
+ // Init should work now
+ err = bin("init")
+ assert.NoErr(err) // Should successfully run init
+ ageContent, err := os.ReadFile(ageSecretPath)
+ assert.NoErr(err) // Should read age secret file
+ assert.True(string(ageContent) == ageSecretContent) // Age secret content should match constant
+ sshContent, err := os.ReadFile(sshSecretPath)
+ assert.NoErr(err) // Should read ssh secret file
+ assert.True(string(sshContent) == sshSecretContent) // SSH secret content should match constant
+ assertGitCatFileEncrypted(assert) // cat-file should always be encrypted (after git-age init)
+
+ err = os.WriteFile(ageSecretPath, []byte(newAgeSecretContent), os.ModePerm)
+ assert.NoErr(err) // Should be able to write the file
+
+ git := func(args ...string) error {
+ args = append([]string{"-c", "user.name=foo", "-c", "user.email=baz@bar.bux", "-c", "commit.gpgsign=false"}, args...)
+ c := exec.Command("git", args...)
+ c.Dir = tmp
+ return c.Run()
+ }
+
+ err = git("add", ageSecretPath)
+ assert.NoErr(err) // Git add should succeed
+ err = git("commit", "-m", "feat!: YOLO")
+ assert.NoErr(err) // Commit should succeed
+
+ assertGitCatFileEncrypted(assert) // cat-file should always be encrypted (after commit)
+}
+
+func assertGitCatFileEncrypted(t *is.I) {
+ t.Helper()
+
+ out, err := exec.Command("git", "cat-file", "blob", "HEAD:secrets/age.txt").Output()
+ t.NoErr(err)
+ t.True(strings.HasPrefix(string(out), ageIntro))
+}
+
+func assertEncrypted(t *is.I, fp string) {
+ t.Helper()
+
+ content, err := os.ReadFile(fp)
+ t.NoErr(err)
+
+ t.True(strings.HasPrefix(string(content), ageIntro))
+}
|