git-age @main -
refs -
log -
-
https://git.jolheiser.com/git-age.git
Signature
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEgqEQpE3xoo1QwJO/uFOtpdp7v3oFAmTpAkEACgkQuFOtpdp7
v3pH2g/+JoggvI19Wg6/BdeH3s3eNcdUTXAmUWWrA+7X3VtpaeWdCwcUEFs5hon3
8dowkJCUOsyvJT/7l3Yqo5cmtAMrb01Sg5WOn8TbZn9ndyg5Y/CtwvGKeaPkRl6n
9b6a5wKS8G7gx279NYl4h7KEkOnRxXjb6u4URe5vC2b7KOSBXq3nNcak7jHzGdPv
Q7WHKExR30IU8DzDr7LKyblXoFlHNUEdQk0v6TVLTmnkGrv5OGBgN1YjGleG0V8j
gaNz6AW/+u4NMYWyqOlIB40R5i/d4Qg/axijNKkSuPmAiScB+3WlxWPZ1T7wvUL+
HPT4KohvclniAoug/lRQQrb8Dr47YKLeJcEGGJDSO2HuxAx0NJQhf4jhBp13w8oe
OraGEIyemC3IetYoHQXeWhhHJa5K8oxM0QPKQbCb4VHeUh0PIl0d6xvuFBYf9Yxb
gHen+tImLOWqzDMdUtjx1UtnoC3/VFXdn9f+o+rD/0hR9SVgnMYYfPFlkZoJwNbZ
nkoQ59ADXz0tzpl0XyGewKRNKxEbvG9zMI4bcH2rftzkusDKJkJsm7CzsxQzhtSr
BJUVyPged2mNMeO56RFAWYLd8JkXzqtZYnpeLJfNGqxDv2xcLY71UF+aHl3Cc9J3
0iUMN6TmrJi0o/nAMAwgBUfy7VWgMbUhYkrl48B85VZ4M8VXnP8=
=xxxy
-----END PGP SIGNATURE-----
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))
+}
diff --git a/go.mod b/go.mod
index 01a95d6c0a1fa06c0d8895162525ec359ace65c8..28b17852ef173bb6576f676e9fd87c8e32a0cc15 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@
require (
filippo.io/age v1.1.1
github.com/bmatcuk/doublestar/v4 v4.6.0
+ github.com/matryer/is v1.4.1
github.com/urfave/cli/v2 v2.25.7
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
diff --git a/go.sum b/go.sum
index cd9cf09d0829d7e8e1fe4937d4e6187a19934206..53e9d209e568f2b50e221903166038ef2187316d 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=