ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

let us peek into secret file add add gemeni key (#478)

* let us peek into secret file add add gemeni key

* fix tests

* lean on assert

* tell agent to use testify

---------

Co-authored-by: paul miller <paul.miller>

authored by

Paul Miller
paul miller
and committed by
GitHub
49352436 cda71a6a

+114 -71
+2 -1
AGENTS.md
··· 26 26 - `tailwind\generate.sh`: run when ever you change css or html 27 27 28 28 ## Coding Style & Naming Conventions 29 - - Go 1.24; always format Go changes with `task fmt` or `gofumpt`, and keep code `gofumpt`-clean before review. Favor small, focused functions and table-driven tests. 29 + - Go 1.26; always format Go changes with `task fmt` or `gofumpt`, and keep code `gofumpt`-clean before review. Favor small, focused functions and table-driven tests. 30 30 - Exported identifiers in `CamelCase`; package-private helpers in `lowerCamel`. Template names mirror file names in `internal/templates`. 31 31 - Prefer standard library first; add dependencies sparingly and record rationale in PR description if new. 32 + - For tests perfer testify/assert or testify/require to limit verboseness 32 33 - Prefer simple html to javascript frameworks 33 34 - For UI copy, prefer plain culinary language over technical terms (example: use "Try again, chef" instead of "Regenerate", and "make it vegetarian" instead of "prefer vegetarian"). 34 35 - Nothing is used outside of this repository so if a method is only used in tests it can be removed even if its public
+36 -5
cmd/kage/main.go
··· 28 28 managedByAnnotationKey = "managed-by" 29 29 managedByAnnotationValue = "github.com/paulgmiller/kage" 30 30 secretCommentPrefix = "secret:" 31 + minSecretValueLength = 5 31 32 ) 32 33 34 + // kage is my dumbed down vesion of https://github.com/getsops/sops 33 35 func main() { 34 36 path := flag.String("secret-file", "secrets/envtest", "encrypted file to apply to k8s namespace") 35 37 namespace := flag.String("ns", "", "k8s namespace") 38 + check := flag.Bool("check", false, "dump secret names") 36 39 flag.Parse() 37 40 ctx := context.Background() 38 41 ··· 52 55 if err != nil { 53 56 log.Fatalf("decrypt file %q: %s", *path, err) 54 57 } 58 + 55 59 secrets, err := secrets(reader) 56 60 if err != nil { 57 61 panic(err) 58 62 } 59 63 64 + // adding gets tricky to retain comments 65 + if *check { 66 + for name, secret := range secrets { 67 + fmt.Println(name) 68 + for key, value := range secret { 69 + fmt.Printf(" %s=%s\n", key, maskedSecretValue(value)) 70 + } 71 + fmt.Println() 72 + } 73 + return 74 + } 75 + 76 + secretsK8s := toK8s(secrets) 77 + 60 78 cfg, err := clientcmd.BuildConfigFromFlags( 61 79 "", 62 80 filepath.Join(os.Getenv("HOME"), ".kube", "config"), ··· 70 88 panic(err) 71 89 } 72 90 secretapi := clientset.CoreV1().Secrets(*namespace) 73 - for _, secret := range secrets { 91 + for _, secret := range secretsK8s { 74 92 current, err := secretapi.Get(ctx, secret.Name, metav1.GetOptions{}) 75 93 if apierrors.IsNotFound(err) { 76 94 _, err = secretapi.Create(ctx, secret, metav1.CreateOptions{}) ··· 108 126 return false 109 127 } 110 128 111 - func secrets(r io.Reader) ([]*corev1.Secret, error) { 129 + type secret map[string]string 130 + 131 + func secrets(r io.Reader) (map[string]secret, error) { 112 132 sc := bufio.NewScanner(r) 113 133 var currentSecret string 114 - secretVals := map[string]map[string]string{} 134 + secretVals := map[string]secret{} 115 135 for sc.Scan() { 116 136 line := strings.TrimSpace(sc.Text()) 117 137 if comment, found := strings.CutPrefix(line, "#"); found { ··· 120 140 if _, found := secretVals[currentSecret]; found { 121 141 return nil, fmt.Errorf("duplicate secret comment %s", currentSecret) 122 142 } 123 - secretVals[currentSecret] = map[string]string{} 143 + secretVals[currentSecret] = secret{} 124 144 } 125 145 continue 126 146 } ··· 137 157 secret := secretVals[currentSecret] 138 158 if _, found := secret[key]; found { 139 159 return nil, fmt.Errorf("duplicate secret key %s", key) 160 + } 161 + if len(value) < minSecretValueLength { 162 + return nil, fmt.Errorf("secret %s/%s must be at least %d characters", currentSecret, key, minSecretValueLength) 140 163 } 141 164 secret[key] = value 142 165 } 143 166 if err := sc.Err(); err != nil { 144 167 return nil, err 145 168 } 169 + return secretVals, nil 170 + } 146 171 172 + func toK8s(secretVals map[string]secret) []*corev1.Secret { 147 173 var secrets []*corev1.Secret 148 174 for name, vals := range secretVals { 149 175 secret := &corev1.Secret{ ··· 158 184 } 159 185 secrets = append(secrets, secret) 160 186 } 161 - return secrets, nil 187 + return secrets 162 188 } 163 189 164 190 func parseSecretLine(line string) (string, string, error) { ··· 190 216 } 191 217 192 218 return key, value, nil 219 + } 220 + 221 + func maskedSecretValue(value string) string { 222 + // invariant is value must be 5 or more characters, so this is safe 223 + return fmt.Sprintf("%s[%d]%s", value[:1], len(value), value[len(value)-1:]) 193 224 } 194 225 195 226 func stripInlineComment(value string) string {
+50 -60
cmd/kage/main_test.go
··· 4 4 "strings" 5 5 "testing" 6 6 7 + "github.com/stretchr/testify/assert" 8 + "github.com/stretchr/testify/require" 7 9 corev1 "k8s.io/api/core/v1" 8 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 11 ) ··· 165 167 tt := tt 166 168 t.Run(tt.name, func(t *testing.T) { 167 169 t.Parallel() 168 - got := secretNeedsUpdate(tt.current, tt.desired) 169 - if got != tt.want { 170 - t.Fatalf("secretNeedsUpdate() = %v, want %v", got, tt.want) 171 - } 170 + assert.Equal(t, tt.want, secretNeedsUpdate(tt.current, tt.desired)) 172 171 }) 173 172 } 174 173 } ··· 182 181 input := strings.NewReader(` 183 182 #secret:first 184 183 API_KEY=alpha 185 - TOKEN=beta 184 + TOKEN=bravo 186 185 187 186 #secret:second 188 187 ZIP=98101 189 - `) 188 + `) 190 189 191 190 got, err := secrets(input) 192 - if err != nil { 193 - t.Fatalf("secrets() error = %v", err) 194 - } 195 - if len(got) != 2 { 196 - t.Fatalf("len(secrets()) = %d, want 2", len(got)) 197 - } 191 + require.NoError(t, err) 192 + require.Len(t, got, 2) 193 + require.Contains(t, got, "first") 194 + require.Contains(t, got, "second") 195 + assert.Equal(t, "alpha", got["first"]["API_KEY"]) 196 + assert.Equal(t, "bravo", got["first"]["TOKEN"]) 197 + assert.Equal(t, "98101", got["second"]["ZIP"]) 198 198 199 + secretsK8s := toK8s(got) 199 200 byName := map[string]*corev1.Secret{} 200 - for _, secret := range got { 201 + for _, secret := range secretsK8s { 201 202 byName[secret.Name] = secret 202 203 } 203 204 205 + require.Contains(t, byName, "first") 204 206 first := byName["first"] 205 - if first == nil { 206 - t.Fatalf("missing secret %q", "first") 207 - } 208 - if first.Type != corev1.SecretTypeOpaque { 209 - t.Fatalf("first.Type = %q, want %q", first.Type, corev1.SecretTypeOpaque) 210 - } 211 - if first.Annotations[managedByAnnotationKey] != managedByAnnotationValue { 212 - t.Fatalf("first managed-by = %q", first.Annotations[managedByAnnotationKey]) 213 - } 214 - if first.StringData["API_KEY"] != "alpha" { 215 - t.Fatalf("first API_KEY = %q, want %q", first.StringData["API_KEY"], "alpha") 216 - } 217 - if first.StringData["TOKEN"] != "beta" { 218 - t.Fatalf("first TOKEN = %q, want %q", first.StringData["TOKEN"], "beta") 219 - } 207 + require.NotNil(t, first) 208 + assert.Equal(t, corev1.SecretTypeOpaque, first.Type) 209 + assert.Equal(t, managedByAnnotationValue, first.Annotations[managedByAnnotationKey]) 210 + assert.Equal(t, "alpha", first.StringData["API_KEY"]) 211 + assert.Equal(t, "bravo", first.StringData["TOKEN"]) 220 212 213 + require.Contains(t, byName, "second") 221 214 second := byName["second"] 222 - if second == nil { 223 - t.Fatalf("missing secret %q", "second") 224 - } 225 - if second.StringData["ZIP"] != "98101" { 226 - t.Fatalf("second ZIP = %q, want %q", second.StringData["ZIP"], "98101") 227 - } 215 + require.NotNil(t, second) 216 + assert.Equal(t, "98101", second.StringData["ZIP"]) 228 217 }) 229 218 230 219 t.Run("handles end of line comments", func(t *testing.T) { ··· 236 225 TOKEN="beta # still value" # comment 237 226 PATH=with#hash 238 227 `)) 239 - if err != nil { 240 - t.Fatalf("secrets() error = %v", err) 241 - } 242 - if len(got) != 1 { 243 - t.Fatalf("len(secrets()) = %d, want 1", len(got)) 244 - } 228 + require.NoError(t, err) 229 + require.Len(t, got, 1) 230 + require.Contains(t, got, "first") 245 231 246 - first := got[0] 247 - if first.StringData["API_KEY"] != "alpha" { 248 - t.Fatalf("API_KEY = %q, want %q", first.StringData["API_KEY"], "alpha") 249 - } 250 - if first.StringData["TOKEN"] != "beta # still value" { 251 - t.Fatalf("TOKEN = %q, want %q", first.StringData["TOKEN"], "beta # still value") 252 - } 253 - if first.StringData["PATH"] != "with#hash" { 254 - t.Fatalf("PATH = %q, want %q", first.StringData["PATH"], "with#hash") 255 - } 232 + first := got["first"] 233 + assert.Equal(t, "alpha", first["API_KEY"]) 234 + assert.Equal(t, "beta # still value", first["TOKEN"]) 235 + assert.Equal(t, "with#hash", first["PATH"]) 236 + }) 237 + 238 + t.Run("rejects short values", func(t *testing.T) { 239 + t.Parallel() 240 + 241 + _, err := secrets(strings.NewReader(` 242 + #secret:first 243 + EMPTY= 244 + NON_EMPTY=value 245 + `)) 246 + require.Error(t, err) 256 247 }) 257 248 258 249 t.Run("duplicate secret comment returns error", func(t *testing.T) { ··· 264 255 #secret:first 265 256 TOKEN=beta 266 257 `)) 267 - if err == nil { 268 - t.Fatal("secrets() error = nil, want duplicate secret comment error") 269 - } 258 + require.Error(t, err) 270 259 }) 271 260 272 261 t.Run("duplicate key returns error", func(t *testing.T) { ··· 275 264 _, err := secrets(strings.NewReader(` 276 265 #secret:first 277 266 API_KEY=alpha 278 - API_KEY=beta 267 + API_KEY=bravo 279 268 `)) 280 - if err == nil { 281 - t.Fatal("secrets() error = nil, want duplicate secret key error") 282 - } 269 + require.Error(t, err) 283 270 }) 284 271 285 272 t.Run("invalid entry returns error", func(t *testing.T) { ··· 289 276 #secret:first 290 277 not-an-env-line 291 278 `)) 292 - if err == nil { 293 - t.Fatal("secrets() error = nil, want invalid secret entry error") 294 - } 279 + require.Error(t, err) 295 280 }) 296 281 } 282 + 283 + func TestMaskedSecretValue(t *testing.T) { 284 + t.Parallel() 285 + assert.Equal(t, "a[5]a", maskedSecretValue("alpha")) 286 + }
+2
go.mod
··· 20 20 github.com/openclosed-dev/slogan v0.2.0 21 21 github.com/sendgrid/rest v2.6.9+incompatible 22 22 github.com/sendgrid/sendgrid-go v3.16.1+incompatible 23 + github.com/stretchr/testify v1.11.1 23 24 golang.org/x/crypto v0.49.0 24 25 golang.org/x/net v0.52.0 25 26 golang.org/x/sync v0.20.0 ··· 49 50 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 50 51 github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect 51 52 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 53 + github.com/pmezard/go-difflib v1.0.0 // indirect 52 54 github.com/spf13/pflag v1.0.9 // indirect 53 55 github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect 54 56 github.com/woodsbury/decimal128 v1.3.0 // indirect
+24 -5
go.sum
··· 1 + c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= 2 + c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= 1 3 code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c h1:5eeuG0BHx1+DHeT3AP+ISKZ2ht1UjGhm581ljqYpVeQ= 2 4 code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= 3 5 filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o= ··· 16 18 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew= 17 19 github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= 18 20 github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= 21 + github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= 22 + github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 19 23 github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= 20 24 github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= 21 25 github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= ··· 38 42 github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= 39 43 github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= 40 44 github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 45 + github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 46 + github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 41 47 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 42 48 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 43 49 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= ··· 57 63 github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 58 64 github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= 59 65 github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= 66 + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= 60 67 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 68 + github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 69 + github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 61 70 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= 62 71 github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 63 72 github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= ··· 86 95 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 87 96 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 88 97 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 89 - github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 90 - github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 91 98 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 99 + github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 92 100 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 93 101 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 102 + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= 103 + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 94 104 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 95 105 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 96 106 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 97 107 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 108 + github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 109 + github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 98 110 github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= 99 111 github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 100 112 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= ··· 120 132 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 121 133 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 122 134 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 135 + github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 136 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 137 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 138 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 123 139 github.com/microsoft/ApplicationInsights-Go v0.4.4 h1:G4+H9WNs6ygSCe6sUyxRc2U81TI5Es90b2t/MwX5KqY= 124 140 github.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U= 125 141 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= ··· 150 166 github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 151 167 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 152 168 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 169 + github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= 170 + github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= 153 171 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 154 172 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 155 173 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 156 174 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 157 175 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 158 - github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= 159 176 github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= 160 177 github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= 178 + github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= 161 179 github.com/openai/openai-go/v3 v3.29.0 h1:dZNJ0w7DxwpgppzKQjSKfLebW27KrtGqgSy4ipJS0U8= 162 180 github.com/openai/openai-go/v3 v3.29.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= 163 181 github.com/openclosed-dev/slogan v0.2.0 h1:Nh1z0IJ366ADFqu5pZY7SdMcYeONaeCx2J5Od9xHSfs= ··· 168 186 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 169 187 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 170 188 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 171 - github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 172 - github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 173 189 github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 190 + github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 174 191 github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= 175 192 github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= 176 193 github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= ··· 189 206 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 190 207 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 191 208 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 209 + github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 210 + github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 192 211 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 193 212 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 194 213 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
secrets/envtest

This is a binary file and will not be displayed.