From e4250c765ddaae76755bc09b5a98d3f775cb0c0f Mon Sep 17 00:00:00 2001 From: Evgenii Uskov Date: Wed, 17 Apr 2024 19:01:20 +0200 Subject: [PATCH 01/20] refactoring --- .gitignore | 5 + .idea/.gitignore | 27 ----- .idea/vcs.xml | 2 +- app/app.go | 33 +++++ app/client/http_client.go | 39 ++++++ app/commands/commands.go | 126 +++++++++++++++++++ app/environment/environment.go | 53 ++++++++ app/services/secrets.go | 64 ++++++++++ dcs.go | 216 ++++++--------------------------- go.mod | 6 +- go.sum | 43 +++++++ 11 files changed, 404 insertions(+), 210 deletions(-) create mode 100644 .gitignore delete mode 100644 .idea/.gitignore create mode 100644 app/app.go create mode 100644 app/client/http_client.go create mode 100644 app/commands/commands.go create mode 100644 app/environment/environment.go create mode 100644 app/services/secrets.go create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e2879 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea + +# compiled app names: +/docker-compose-secrets +/dcs diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 62ad517..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -/discord.xml - -# Editor-based HTTP Client requests -/httpRequests/ - -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml - -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..4b1f498 --- /dev/null +++ b/app/app.go @@ -0,0 +1,33 @@ +package app + +import ( + "docker-compose-secrets/app/commands" + "log" +) + +type Application struct { + commandService *commands.Service +} + +func NewApplication(commandService *commands.Service) *Application { + return &Application{commandService} +} + +func (app *Application) Run() { + currentCommand := app.commandService.GetCurrentCommand() + if currentCommand == "" { + log.Fatal("no command specified") + } + + switch currentCommand { + case commands.CommandStart: + app.commandService.ExecuteStart() + case commands.CommandStop: + app.commandService.ExecuteStop() + case commands.CommandRestart: + app.commandService.ExecuteRestart() + case commands.CommandUpdate: + app.commandService.ExecuteUpdate() + app.commandService.ExecuteRestart() + } +} diff --git a/app/client/http_client.go b/app/client/http_client.go new file mode 100644 index 0000000..aeb566e --- /dev/null +++ b/app/client/http_client.go @@ -0,0 +1,39 @@ +package client + +import ( + "github.com/go-resty/resty/v2" +) + +type HttpClient struct { + client *resty.Client +} + +func NewHttpClient() *HttpClient { + return &HttpClient{ + client: resty.New(), + } +} + +func (h *HttpClient) Get(url string, headers map[string]string) ([]byte, error) { + resp, err := h.client.R(). + SetHeaders(headers). + Get(url) + if err != nil { + return nil, err + } + + return resp.Body(), nil +} + +func (h *HttpClient) BuildHeaders(customHeaders map[string]string) map[string]string { + headers := make(map[string]string) + + headers["Accept"] = "application/json" + headers["Content-Type"] = "application/json" + + for name, value := range customHeaders { + headers[name] = value + } + + return headers +} diff --git a/app/commands/commands.go b/app/commands/commands.go new file mode 100644 index 0000000..87f9569 --- /dev/null +++ b/app/commands/commands.go @@ -0,0 +1,126 @@ +package commands + +import ( + "docker-compose-secrets/app/services" + "fmt" + "log" + "os" + "os/exec" +) + +const ( + CommandStart = "start" + CommandStop = "stop" + CommandRestart = "restart" + CommandUpdate = "update" +) + +type Service struct { + secretService *services.SecretService + availableCommands []string + currentCommand string +} + +func NewService(secretService *services.SecretService) *Service { + return &Service{ + secretService: secretService, + availableCommands: []string{ + CommandStart, + CommandStop, + CommandRestart, + CommandUpdate, + }, + } +} + +func (c *Service) CheckCommandName(argCommand string) error { + for _, cmd := range c.availableCommands { + if cmd == argCommand { + return nil + } + } + + return fmt.Errorf("invalid command `%s`", argCommand) +} + +func (c *Service) SetCurrentCommand(argCommand string) { + c.currentCommand = argCommand +} + +func (c *Service) GetCurrentCommand() string { + return c.currentCommand +} + +func (c *Service) ExecuteStart() { + secrets, err := c.secretService.GetSecrets() + if err != nil { + log.Fatal(err) + } + + cmd := buildCommand("compose", "up", "-d") + addEnvironToCmd(cmd, secrets) + + println("Starting docker compose:") + if err := executeCommand(cmd); err != nil { + log.Fatal(err) + } +} + +func (c *Service) ExecuteStop() { + println("stopping docker compose:") + + if err := executeCommand( + buildCommand("compose", "down", "--remove-orphans"), + ); err != nil { + log.Fatal(err) + } +} + +func (c *Service) ExecuteRestart() { + secrets, err := c.secretService.GetSecrets() + if err != nil { + log.Fatal(err) + } + + cmd := buildCommand("compose", "up", "-d", "--force-recreate") + addEnvironToCmd(cmd, secrets) + + println("Starting docker compose:") + if err := executeCommand(cmd); err != nil { + log.Fatal(err) + } +} + +func (c *Service) ExecuteUpdate() { + println("pulling latest docker images:") + + if err := executeCommand( + buildCommand("compose", "pull"), + ); err != nil { + log.Fatal(err) + } +} + +func buildCommand(args ...string) *exec.Cmd { + cmd := exec.Command("docker", args...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd +} + +func addEnvironToCmd(cmd *exec.Cmd, env map[string]string) { + if len(env) == 0 { + println("No secrets found. Continuing without secrets") + return + } + + for key, value := range env { + cmd.Env = append(cmd.Environ(), fmt.Sprintf("%s=%s", key, value)) + } +} + +func executeCommand(cmd *exec.Cmd) error { + return cmd.Run() +} diff --git a/app/environment/environment.go b/app/environment/environment.go new file mode 100644 index 0000000..d7b79a4 --- /dev/null +++ b/app/environment/environment.go @@ -0,0 +1,53 @@ +package environment + +import ( + "errors" + "fmt" + "os" +) + +const ( + envVaultAddr = "VAULT_ADDR" + envVaultToken = "VAULT_TOKEN" + envVaultPath = "VAULT_PATH" +) + +type Service struct { + systemEnv map[string]string +} + +func NewService() *Service { + systemEnv := make(map[string]string) + systemEnv[envVaultAddr] = os.Getenv(envVaultAddr) + systemEnv[envVaultToken] = os.Getenv(envVaultToken) + systemEnv[envVaultPath] = os.Getenv(envVaultPath) + + return &Service{systemEnv} +} + +func (e *Service) CheckExistSystemEnv() error { + for envKey, envVal := range e.systemEnv { + if envVal == "" { + return errors.New( + fmt.Sprintf( + "environment variable `%s` is empty", + envKey, + ), + ) + } + } + + return nil +} + +func (e *Service) GetVaultAddr() string { + return e.systemEnv[envVaultAddr] +} + +func (e *Service) GetVaultToken() string { + return e.systemEnv[envVaultToken] +} + +func (e *Service) GetVaultPath() string { + return e.systemEnv[envVaultPath] +} diff --git a/app/services/secrets.go b/app/services/secrets.go new file mode 100644 index 0000000..3954d9a --- /dev/null +++ b/app/services/secrets.go @@ -0,0 +1,64 @@ +package services + +import ( + "docker-compose-secrets/app/client" + "docker-compose-secrets/app/environment" + "encoding/json" + "errors" + "fmt" + "strings" +) + +const GetSecretsDataPath = "v1/secrets/data" + +type SecretService struct { + httpClient *client.HttpClient + environmentService *environment.Service +} + +func NewSecretService( + httpClient *client.HttpClient, environmentService *environment.Service) *SecretService { + return &SecretService{httpClient, environmentService} +} + +func (s *SecretService) GetSecrets() (map[string]string, error) { + headers := make(map[string]string) + headers["X-Vault-Token"] = s.environmentService.GetVaultToken() + + response, err := s.httpClient.Get( + s.buildUrl(), + s.httpClient.BuildHeaders(headers), + ) + if err != nil { + return nil, err + } + + var result map[string]interface{} + if err := json.Unmarshal(response, &result); err != nil { + return nil, err + } + + if result["errors"] != nil { + var errorMessages []string + for _, e := range result["errors"].([]interface{}) { + errorMessages = append(errorMessages, e.(string)) + } + return nil, errors.New(strings.Join(errorMessages, "; ")) + } + + secrets := make(map[string]string) + for secretKey, secretValue := range result { + secrets[secretKey] = secretValue.(string) + } + + return secrets, nil +} + +func (s *SecretService) buildUrl() string { + return fmt.Sprintf( + "%s/%s/%s", + s.environmentService.GetVaultAddr(), + GetSecretsDataPath, + s.environmentService.GetVaultPath(), + ) +} diff --git a/dcs.go b/dcs.go index cc7ffe9..58b156a 100644 --- a/dcs.go +++ b/dcs.go @@ -1,200 +1,54 @@ package main import ( - "encoding/json" - "fmt" - "io" + "docker-compose-secrets/app" + "docker-compose-secrets/app/client" + "docker-compose-secrets/app/commands" + "docker-compose-secrets/app/environment" + "docker-compose-secrets/app/services" "log" - "net/http" "os" - "os/exec" - "path/filepath" ) func main() { - // Get the arguments passed to the program - args := os.Args[1:] - - // Check if the user passed any arguments - if len(args) == 0 { - invalidCommand() - } - - // Check if the required environment variables are set - if os.Getenv("VAULT_ADDR") == "" || os.Getenv("VAULT_TOKEN") == "" { - log.Fatal("VAULT_ADDR and VAULT_TOKEN must be set in environment") - } - - // Check which command the user passed - switch args[0] { - case "start": - start(false) - case "stop": - stop() - case "restart": - start(true) - case "update": - update() - default: - invalidCommand() - } -} - -func invalidCommand() { - fmt.Println("Invalid command, please use one of the following:") - fmt.Println(" start") - fmt.Println(" stop") - fmt.Println(" restart") - fmt.Println(" update") - fmt.Println("\nExample: dcs start") - os.Exit(1) -} - -func start(restart bool) { - // Get the address of the Vault server from the environment - server := os.Getenv("VAULT_ADDR") - - // Get the folder of the current working directory - dir, err := filepath.Abs(filepath.Dir(os.Args[0])) - if err != nil { - log.Fatal(err) - } - path := filepath.Base(dir) - - // Get the token from the environment - token := os.Getenv("VAULT_TOKEN") - - // Print the settings to the user - fmt.Println("Retrieving secrets from Vault:") - fmt.Println(" Server:", server) - fmt.Println(" Path:", path) - fmt.Println(" Token:", token) - fmt.Println() - - // Initialize the request to the Vault server with the correct path - req, _ := http.NewRequest("GET", server+"/v1/secret/data/"+path, nil) - - // Add the authentication token header to the request - req.Header.Add("X-Vault-Token", token) - - // Send the request to the Vault server - res, _ := http.DefaultClient.Do(req) - - // Close the response body when we're done - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - log.Fatal(err) - } - }(res.Body) - - // Decode the response body - body, _ := io.ReadAll(res.Body) - - // Unmarshal and parse the JSON response into a map - var result map[string]interface{} - err = json.Unmarshal(body, &result) - if err != nil { + environmentService := environment.NewService() + if err := environmentService.CheckExistSystemEnv(); err != nil { log.Fatal(err) } - // Set the command to run depending on if we're restarting or not - var cmd *exec.Cmd - if restart { - cmd = exec.Command("docker", "compose", "up", "-d", "--force-recreate") - } else { - cmd = exec.Command("docker", "compose", "up", "-d") - } - - // secrets := result["data"].(map[string]interface{})["data"].(map[string]interface{}) - - // Extract the secrets from the response and handle errors - s1 := result["data"] - if s1 != nil { - s2 := s1.(map[string]interface{}) - if s2 != nil { - s3 := s2["data"] - if s3 != nil { - s4 := s3.(map[string]interface{}) - if s4 != nil { - fmt.Println("Injecting secrets into process:") - - // Pass all OS environment variables to the command - cmd.Env = os.Environ() - - // Inject all secrets into the command as environment variables and print them to the user - for k, v := range s4 { - cmd.Env = append(cmd.Environ(), fmt.Sprintf("%s=%s", k, v)) - fmt.Printf(" %s: %s\n", k, v) - } - } else { - fmt.Println("No secrets found for \"" + path + "\", continuing without secrets") - } - } else { - fmt.Println("No secrets found for \"" + path + "\", continuing without secrets") - } - } else { - fmt.Println("No secrets found for \"" + path + "\", continuing without secrets") - } - } else { - fmt.Println("No secrets found for \"" + path + "\", continuing without secrets") - } - - fmt.Println() - fmt.Println("Starting docker compose:") - - // Write the output of the command to the terminal - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + args := os.Args[1:] - // Run the command - err = cmd.Run() - if err != nil { - log.Fatal(err) - } + commandService := commands.NewService( + services.NewSecretService( + client.NewHttpClient(), + environmentService, + ), + ) + checkArgument(args, commandService) + commandService.SetCurrentCommand(args[0]) + + application := app.NewApplication(commandService) + application.Run() } -func stop() { - // Set the command to run - cmd := exec.Command("docker", "compose", "down", "--remove-orphans") - - // Pass all OS environment variables to the command - cmd.Env = os.Environ() - - fmt.Println("Stopping docker compose:") - - // Write the output of the command to the terminal - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - // Run the command - err := cmd.Run() - if err != nil { - log.Fatal(err) +func checkArgument(args []string, commands *commands.Service) { + if len(args) == 0 { + showWrongCommandError("invalid command") } -} - -func update() { - // Set the command to run - cmd := exec.Command("docker", "compose", "pull") - // Pass all OS environment variables to the command - cmd.Env = os.Environ() - - fmt.Println("Pulling latest docker images:") - - // Write the output of the command to the terminal - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - // Run the command - err := cmd.Run() - if err != nil { - log.Fatal(err) + if err := commands.CheckCommandName(args[0]); err != nil { + showWrongCommandError(err.Error()) } +} - fmt.Println() - - // Restart the docker compose after updating the images - start(true) +func showWrongCommandError(title string) { + log.Fatalf( + "%s, please use one of the following:\n %s\n %s\n %s\n %s\n%s", + title, + "start", + "stop", + "restart", + "update", + "Example: dcs start", + ) } diff --git a/go.mod b/go.mod index 7651061..d24a346 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module docker-compose-secrets -go 1.19 +go 1.22 + +require github.com/go-resty/resty/v2 v2.11.0 + +require golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cf1df02 --- /dev/null +++ b/go.sum @@ -0,0 +1,43 @@ +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 53aec8edf1b8f8b4bb8d806149bd69283d970972 Mon Sep 17 00:00:00 2001 From: Evgenii Uskov Date: Wed, 17 Apr 2024 19:41:31 +0200 Subject: [PATCH 02/20] add get secrets response model --- app/client/http_client.go | 2 ++ app/models/get_secrets_result.go | 10 ++++++++++ app/services/secrets.go | 24 +++++++++++------------- 3 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 app/models/get_secrets_result.go diff --git a/app/client/http_client.go b/app/client/http_client.go index aeb566e..4c16065 100644 --- a/app/client/http_client.go +++ b/app/client/http_client.go @@ -4,6 +4,8 @@ import ( "github.com/go-resty/resty/v2" ) +const HeaderVaultTokenName = "X-Vault-Token" + type HttpClient struct { client *resty.Client } diff --git a/app/models/get_secrets_result.go b/app/models/get_secrets_result.go new file mode 100644 index 0000000..bb6c09f --- /dev/null +++ b/app/models/get_secrets_result.go @@ -0,0 +1,10 @@ +package models + +type SecretData struct { + Data map[string]string `json:"data"` +} + +type GetSecretsResult struct { + Errors []string `json:"errors,omitempty"` + Data *SecretData `json:"data,omitempty"` +} diff --git a/app/services/secrets.go b/app/services/secrets.go index 3954d9a..8f13fff 100644 --- a/app/services/secrets.go +++ b/app/services/secrets.go @@ -3,13 +3,14 @@ package services import ( "docker-compose-secrets/app/client" "docker-compose-secrets/app/environment" + "docker-compose-secrets/app/models" "encoding/json" "errors" "fmt" "strings" ) -const GetSecretsDataPath = "v1/secrets/data" +const getSecretsDataPath = "v1/secret/data" type SecretService struct { httpClient *client.HttpClient @@ -17,13 +18,14 @@ type SecretService struct { } func NewSecretService( - httpClient *client.HttpClient, environmentService *environment.Service) *SecretService { + httpClient *client.HttpClient, environmentService *environment.Service, +) *SecretService { return &SecretService{httpClient, environmentService} } func (s *SecretService) GetSecrets() (map[string]string, error) { headers := make(map[string]string) - headers["X-Vault-Token"] = s.environmentService.GetVaultToken() + headers[client.HeaderVaultTokenName] = s.environmentService.GetVaultToken() response, err := s.httpClient.Get( s.buildUrl(), @@ -33,22 +35,18 @@ func (s *SecretService) GetSecrets() (map[string]string, error) { return nil, err } - var result map[string]interface{} + var result models.GetSecretsResult if err := json.Unmarshal(response, &result); err != nil { return nil, err } - if result["errors"] != nil { - var errorMessages []string - for _, e := range result["errors"].([]interface{}) { - errorMessages = append(errorMessages, e.(string)) - } - return nil, errors.New(strings.Join(errorMessages, "; ")) + if len(result.Errors) > 0 { + return nil, errors.New(strings.Join(result.Errors, "; ")) } secrets := make(map[string]string) - for secretKey, secretValue := range result { - secrets[secretKey] = secretValue.(string) + for secretKey, secretValue := range result.Data.Data { + secrets[secretKey] = secretValue } return secrets, nil @@ -58,7 +56,7 @@ func (s *SecretService) buildUrl() string { return fmt.Sprintf( "%s/%s/%s", s.environmentService.GetVaultAddr(), - GetSecretsDataPath, + getSecretsDataPath, s.environmentService.GetVaultPath(), ) } From bce67ef1b880cdb5707688938fbd0a7a1993dded Mon Sep 17 00:00:00 2001 From: Evgenii Uskov Date: Thu, 18 Apr 2024 20:38:30 +0200 Subject: [PATCH 03/20] fix when wrong path --- app/services/secrets.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/app/services/secrets.go b/app/services/secrets.go index 8f13fff..22dcd62 100644 --- a/app/services/secrets.go +++ b/app/services/secrets.go @@ -35,15 +35,11 @@ func (s *SecretService) GetSecrets() (map[string]string, error) { return nil, err } - var result models.GetSecretsResult - if err := json.Unmarshal(response, &result); err != nil { + result, err := s.parseResponse(response) + if err != nil { return nil, err } - if len(result.Errors) > 0 { - return nil, errors.New(strings.Join(result.Errors, "; ")) - } - secrets := make(map[string]string) for secretKey, secretValue := range result.Data.Data { secrets[secretKey] = secretValue @@ -60,3 +56,20 @@ func (s *SecretService) buildUrl() string { s.environmentService.GetVaultPath(), ) } + +func (s *SecretService) parseResponse(response []byte) (*models.GetSecretsResult, error) { + var result models.GetSecretsResult + if err := json.Unmarshal(response, &result); err != nil { + return nil, err + } + + if len(result.Errors) > 0 { + return nil, errors.New(strings.Join(result.Errors, "; ")) + } + + if result.Data == nil { + return nil, errors.New("secrets not found") + } + + return &result, nil +} From 7d2a94571f3bc62fee734f2088f8656853bfc79f Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Wed, 24 Apr 2024 15:43:30 +0200 Subject: [PATCH 04/20] up version + delete trash --- .gitignore | 51 ++++++++++++++++++++++++++++++-- .idea/docker-compose-secrets.iml | 9 ------ .idea/modules.xml | 8 ----- .idea/vcs.xml | 6 ---- go.mod | 4 +-- go.sum | 24 +++++++++------ 6 files changed, 66 insertions(+), 36 deletions(-) delete mode 100644 .idea/docker-compose-secrets.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 85e2879..e734dea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,52 @@ -.idea - # compiled app names: /docker-compose-secrets /dcs + +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk \ No newline at end of file diff --git a/.idea/docker-compose-secrets.iml b/.idea/docker-compose-secrets.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/docker-compose-secrets.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e96684d..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/go.mod b/go.mod index d24a346..b06cf61 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,6 @@ module docker-compose-secrets go 1.22 -require github.com/go-resty/resty/v2 v2.11.0 +require github.com/go-resty/resty/v2 v2.12.0 -require golang.org/x/net v0.17.0 // indirect +require golang.org/x/net v0.24.0 // indirect diff --git a/go.sum b/go.sum index cf1df02..7d15bcb 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,10 @@ -github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= -github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -11,8 +12,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -23,19 +26,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From d9733f84bcb3ba6d6b17797522d32b7018c0aad9 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Wed, 24 Apr 2024 16:15:13 +0200 Subject: [PATCH 05/20] test gh actions --- .github/workflows/dependency-review.yml | 39 +++++++++++++++++++++++++ .github/workflows/go.yml | 24 +++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..d19e21b --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,39 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable +# packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency review' +on: + pull_request: + branches: [ "main" ] + +# If using a dependency submission action in this workflow this permission will need to be set to: +# +# permissions: +# contents: write +# +# https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api +permissions: + contents: read + # Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option + pull-requests: write + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 + # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. + with: + comment-summary-in-pr: always + # fail-on-severity: moderate + # deny-licenses: GPL-1.0-or-later, LGPL-2.0-or-later + # retry-on-snapshot-warnings: true diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..6025796 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,24 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: push + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.22' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... From 83744913a7504be9e4ea7aa817cbe2b4c0124128 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Wed, 24 Apr 2024 16:34:50 +0200 Subject: [PATCH 06/20] test releaser --- .github/workflows/slsa-goreleaser.yml | 49 +++++++++++++++++++++++++++ .slsa-goreleaser.yml | 36 ++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 .github/workflows/slsa-goreleaser.yml create mode 100644 .slsa-goreleaser.yml diff --git a/.github/workflows/slsa-goreleaser.yml b/.github/workflows/slsa-goreleaser.yml new file mode 100644 index 0000000..e16ba05 --- /dev/null +++ b/.github/workflows/slsa-goreleaser.yml @@ -0,0 +1,49 @@ +name: SLSA go releaser +on: + workflow_dispatch: + push: + tags: + - "*" + +permissions: read-all + +jobs: + # Generate ldflags dynamically. + # Optional: only needed for ldflags. + args: + runs-on: ubuntu-latest + outputs: + commit-date: ${{ steps.ldflags.outputs.commit-date }} + commit: ${{ steps.ldflags.outputs.commit }} + version: ${{ steps.ldflags.outputs.version }} + tree-state: ${{ steps.ldflags.outputs.tree-state }} + steps: + - id: checkout + uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # tag=v2.3.4 + with: + fetch-depth: 0 + - id: ldflags + run: | + echo "commit-date=$(git log --date=iso8601-strict -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" + echo "commit=$GITHUB_SHA" >> "$GITHUB_OUTPUT" + echo "version=$(git describe --tags --always --dirty | cut -c2-)" >> "$GITHUB_OUTPUT" + echo "tree-state=$(if git diff --quiet; then echo "clean"; else echo "dirty"; fi)" >> "$GITHUB_OUTPUT" + + # Trusted builder. + build: + permissions: + id-token: write # To sign the provenance. + contents: write # To upload assets to release. + actions: read # To read the workflow path. + strategy: + matrix: + os: + - linux + - darwin + arch: + - amd64 + - arm64 + uses: slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@v2.0.0 + with: + go-version: 1.22 + config-file: .slsa-goreleaser/${{matrix.os}}-${{matrix.arch}}.yml \ No newline at end of file diff --git a/.slsa-goreleaser.yml b/.slsa-goreleaser.yml new file mode 100644 index 0000000..8ec6846 --- /dev/null +++ b/.slsa-goreleaser.yml @@ -0,0 +1,36 @@ +# Version for this file. +version: 1 + +# (Optional) List of env variables used during compilation. +env: + - GO111MODULE=on + - CGO_ENABLED=0 + +# (Optional) Flags for the compiler. +flags: + - -trimpath + - -tags=netgo + +# The OS to compile for. `GOOS` env variable will be set to this value. +goos: linux + +# The architecture to compile for. `GOARCH` env variable will be set to this value. +goarch: amd64 + +# (Optional) Entrypoint to compile. +# main: ./path/to/main.go + +# (Optional) Working directory. (default: root of the project) +# dir: ./relative/path/to/dir + +# Binary output name. +# {{ .Os }} will be replaced by goos field in the config file. +# {{ .Arch }} will be replaced by goarch field in the config file. +binary: binary-{{ .Os }}-{{ .Arch }} + +# (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. +ldflags: + - "-X main.Version={{ .Env.VERSION }}" + - "-X main.Commit={{ .Env.COMMIT }}" + - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" + - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file From 3ff1cab23b24b8f29a33c4551715ce344685c12a Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 24 Apr 2024 16:38:43 +0200 Subject: [PATCH 07/20] Update slsa-goreleaser.yml test on: push --- .github/workflows/slsa-goreleaser.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/slsa-goreleaser.yml b/.github/workflows/slsa-goreleaser.yml index e16ba05..b3c0c2a 100644 --- a/.github/workflows/slsa-goreleaser.yml +++ b/.github/workflows/slsa-goreleaser.yml @@ -1,9 +1,5 @@ name: SLSA go releaser -on: - workflow_dispatch: - push: - tags: - - "*" +on: push permissions: read-all @@ -46,4 +42,4 @@ jobs: uses: slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@v2.0.0 with: go-version: 1.22 - config-file: .slsa-goreleaser/${{matrix.os}}-${{matrix.arch}}.yml \ No newline at end of file + config-file: .slsa-goreleaser/${{matrix.os}}-${{matrix.arch}}.yml From 5c97743ae21cabb93a66da93795a63864a903bfa Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 00:34:42 +0200 Subject: [PATCH 08/20] test slsa gorelase 2 --- .slsa-goreleaser-darwin-amd64.yml | 36 +++++++++++++++++++ .slsa-goreleaser-darwin-arm64.yml | 36 +++++++++++++++++++ ...er.yml => .slsa-goreleaser-linux-amd64.yml | 2 +- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 .slsa-goreleaser-darwin-amd64.yml create mode 100644 .slsa-goreleaser-darwin-arm64.yml rename .slsa-goreleaser.yml => .slsa-goreleaser-linux-amd64.yml (96%) diff --git a/.slsa-goreleaser-darwin-amd64.yml b/.slsa-goreleaser-darwin-amd64.yml new file mode 100644 index 0000000..6637ada --- /dev/null +++ b/.slsa-goreleaser-darwin-amd64.yml @@ -0,0 +1,36 @@ +# Version for this file. +version: 1 + +# (Optional) List of env variables used during compilation. +env: + - GO111MODULE=on + - CGO_ENABLED=0 + +# (Optional) Flags for the compiler. +flags: + - -trimpath + - -tags=netgo + +# The OS to compile for. `GOOS` env variable will be set to this value. +goos: darwin + +# The architecture to compile for. `GOARCH` env variable will be set to this value. +goarch: amd64 + +# (Optional) Entrypoint to compile. +# main: ./path/to/main.go + +# (Optional) Working directory. (default: root of the project) +# dir: ./relative/path/to/dir + +# Binary output name. +# {{ .Os }} will be replaced by goos field in the config file. +# {{ .Arch }} will be replaced by goarch field in the config file. +binary: dcs-{{ .Os }}-{{ .Arch }} + +# (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. +ldflags: + - "-X main.Version={{ .Env.VERSION }}" + - "-X main.Commit={{ .Env.COMMIT }}" + - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" + - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file diff --git a/.slsa-goreleaser-darwin-arm64.yml b/.slsa-goreleaser-darwin-arm64.yml new file mode 100644 index 0000000..e36370b --- /dev/null +++ b/.slsa-goreleaser-darwin-arm64.yml @@ -0,0 +1,36 @@ +# Version for this file. +version: 1 + +# (Optional) List of env variables used during compilation. +env: + - GO111MODULE=on + - CGO_ENABLED=0 + +# (Optional) Flags for the compiler. +flags: + - -trimpath + - -tags=netgo + +# The OS to compile for. `GOOS` env variable will be set to this value. +goos: darwin + +# The architecture to compile for. `GOARCH` env variable will be set to this value. +goarch: arm64 + +# (Optional) Entrypoint to compile. +# main: ./path/to/main.go + +# (Optional) Working directory. (default: root of the project) +# dir: ./relative/path/to/dir + +# Binary output name. +# {{ .Os }} will be replaced by goos field in the config file. +# {{ .Arch }} will be replaced by goarch field in the config file. +binary: dcs-{{ .Os }}-{{ .Arch }} + +# (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. +ldflags: + - "-X main.Version={{ .Env.VERSION }}" + - "-X main.Commit={{ .Env.COMMIT }}" + - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" + - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file diff --git a/.slsa-goreleaser.yml b/.slsa-goreleaser-linux-amd64.yml similarity index 96% rename from .slsa-goreleaser.yml rename to .slsa-goreleaser-linux-amd64.yml index 8ec6846..8dfca9c 100644 --- a/.slsa-goreleaser.yml +++ b/.slsa-goreleaser-linux-amd64.yml @@ -26,7 +26,7 @@ goarch: amd64 # Binary output name. # {{ .Os }} will be replaced by goos field in the config file. # {{ .Arch }} will be replaced by goarch field in the config file. -binary: binary-{{ .Os }}-{{ .Arch }} +binary: dcs-{{ .Os }}-{{ .Arch }} # (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. ldflags: From eb95d17e522eed3d7d706e05a970923920c43eae Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 00:36:51 +0200 Subject: [PATCH 09/20] v3 --- .../darwin-amd64.yml | 0 .../darwin-arm64.yml | 0 .../linux-amd64.yml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .slsa-goreleaser-darwin-amd64.yml => .slsa-goreleaser/darwin-amd64.yml (100%) rename .slsa-goreleaser-darwin-arm64.yml => .slsa-goreleaser/darwin-arm64.yml (100%) rename .slsa-goreleaser-linux-amd64.yml => .slsa-goreleaser/linux-amd64.yml (100%) diff --git a/.slsa-goreleaser-darwin-amd64.yml b/.slsa-goreleaser/darwin-amd64.yml similarity index 100% rename from .slsa-goreleaser-darwin-amd64.yml rename to .slsa-goreleaser/darwin-amd64.yml diff --git a/.slsa-goreleaser-darwin-arm64.yml b/.slsa-goreleaser/darwin-arm64.yml similarity index 100% rename from .slsa-goreleaser-darwin-arm64.yml rename to .slsa-goreleaser/darwin-arm64.yml diff --git a/.slsa-goreleaser-linux-amd64.yml b/.slsa-goreleaser/linux-amd64.yml similarity index 100% rename from .slsa-goreleaser-linux-amd64.yml rename to .slsa-goreleaser/linux-amd64.yml From 55c329b79f94783b62a6db79c5c68f9fd53c10cb Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 00:40:01 +0200 Subject: [PATCH 10/20] v4 --- .slsa-goreleaser/darwin-amd64.yml | 10 +++++----- .slsa-goreleaser/darwin-arm64.yml | 10 +++++----- .slsa-goreleaser/linux-amd64.yml | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.slsa-goreleaser/darwin-amd64.yml b/.slsa-goreleaser/darwin-amd64.yml index 6637ada..cbe8d8c 100644 --- a/.slsa-goreleaser/darwin-amd64.yml +++ b/.slsa-goreleaser/darwin-amd64.yml @@ -29,8 +29,8 @@ goarch: amd64 binary: dcs-{{ .Os }}-{{ .Arch }} # (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. -ldflags: - - "-X main.Version={{ .Env.VERSION }}" - - "-X main.Commit={{ .Env.COMMIT }}" - - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" - - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file +# ldflags: +# - "-X main.Version={{ .Env.VERSION }}" +# - "-X main.Commit={{ .Env.COMMIT }}" +# - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" +# - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file diff --git a/.slsa-goreleaser/darwin-arm64.yml b/.slsa-goreleaser/darwin-arm64.yml index e36370b..0de818e 100644 --- a/.slsa-goreleaser/darwin-arm64.yml +++ b/.slsa-goreleaser/darwin-arm64.yml @@ -29,8 +29,8 @@ goarch: arm64 binary: dcs-{{ .Os }}-{{ .Arch }} # (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. -ldflags: - - "-X main.Version={{ .Env.VERSION }}" - - "-X main.Commit={{ .Env.COMMIT }}" - - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" - - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file +# ldflags: +# - "-X main.Version={{ .Env.VERSION }}" +# - "-X main.Commit={{ .Env.COMMIT }}" +# - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" +# - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file diff --git a/.slsa-goreleaser/linux-amd64.yml b/.slsa-goreleaser/linux-amd64.yml index 8dfca9c..3bc494b 100644 --- a/.slsa-goreleaser/linux-amd64.yml +++ b/.slsa-goreleaser/linux-amd64.yml @@ -29,8 +29,8 @@ goarch: amd64 binary: dcs-{{ .Os }}-{{ .Arch }} # (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. -ldflags: - - "-X main.Version={{ .Env.VERSION }}" - - "-X main.Commit={{ .Env.COMMIT }}" - - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" - - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file +# ldflags: +# - "-X main.Version={{ .Env.VERSION }}" +# - "-X main.Commit={{ .Env.COMMIT }}" +# - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" +# - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file From 049a2769b01c39a85dc626b7f2d43c996a69917a Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 00:42:19 +0200 Subject: [PATCH 11/20] linux-arm64.yml --- .slsa-goreleaser/linux-arm64.yml.yml | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .slsa-goreleaser/linux-arm64.yml.yml diff --git a/.slsa-goreleaser/linux-arm64.yml.yml b/.slsa-goreleaser/linux-arm64.yml.yml new file mode 100644 index 0000000..d0937a4 --- /dev/null +++ b/.slsa-goreleaser/linux-arm64.yml.yml @@ -0,0 +1,36 @@ +# Version for this file. +version: 1 + +# (Optional) List of env variables used during compilation. +env: + - GO111MODULE=on + - CGO_ENABLED=0 + +# (Optional) Flags for the compiler. +flags: + - -trimpath + - -tags=netgo + +# The OS to compile for. `GOOS` env variable will be set to this value. +goos: linux + +# The architecture to compile for. `GOARCH` env variable will be set to this value. +goarch: arm64 + +# (Optional) Entrypoint to compile. +# main: ./path/to/main.go + +# (Optional) Working directory. (default: root of the project) +# dir: ./relative/path/to/dir + +# Binary output name. +# {{ .Os }} will be replaced by goos field in the config file. +# {{ .Arch }} will be replaced by goarch field in the config file. +binary: dcs-{{ .Os }}-{{ .Arch }} + +# (Optional) ldflags generated dynamically in the workflow, and set as the `evaluated-envs` input variables in the workflow. +# ldflags: +# - "-X main.Version={{ .Env.VERSION }}" +# - "-X main.Commit={{ .Env.COMMIT }}" +# - "-X main.CommitDate={{ .Env.COMMIT_DATE }}" +# - "-X main.TreeState={{ .Env.TREE_STATE }}" \ No newline at end of file From 655077b7b6734f1b3d67b91957b6ba6c4040cb65 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 00:43:52 +0200 Subject: [PATCH 12/20] typo fix renamed: .slsa-goreleaser/linux-arm64.yml.yml -> .slsa-goreleaser/linux-arm64.yml --- .slsa-goreleaser/{linux-arm64.yml.yml => linux-arm64.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .slsa-goreleaser/{linux-arm64.yml.yml => linux-arm64.yml} (100%) diff --git a/.slsa-goreleaser/linux-arm64.yml.yml b/.slsa-goreleaser/linux-arm64.yml similarity index 100% rename from .slsa-goreleaser/linux-arm64.yml.yml rename to .slsa-goreleaser/linux-arm64.yml From 0ebdefcec003e1e17f6effee32ef5436bdbbbc22 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 01:23:03 +0200 Subject: [PATCH 13/20] v5 --- .github/workflows/dependency-review.yml | 4 +-- .gitignore | 3 +- .goreleaser.yaml | 41 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index d19e21b..fbcc9ff 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -8,9 +8,7 @@ # Source repository: https://github.com/actions/dependency-review-action # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement name: 'Dependency review' -on: - pull_request: - branches: [ "main" ] +on: push # If using a dependency submission action in this workflow this permission will need to be set to: # diff --git a/.gitignore b/.gitignore index e734dea..0e5f470 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,5 @@ Icon .AppleDesktop Network Trash Folder Temporary Items -.apdisk \ No newline at end of file +.apdisk +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..9bb18d0 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,41 @@ +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com + +# The lines below are called `modelines`. See `:help modeline` +# Feel free to remove those if you don't want/need to use them. +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +# vim: set ts=2 sw=2 tw=0 fo=cnqoj + +version: 1 + +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + - go generate ./... + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + +archives: + - format: tar.gz + # this name template makes the OS and Arch compatible with the results of `uname`. + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" From 33d887f3ebe105885b7510b8f5391cf639c9c1fb Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Thu, 25 Apr 2024 01:30:38 +0200 Subject: [PATCH 14/20] v6 --- .goreleaser.yaml | 71 ++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9bb18d0..23d7dd3 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,41 +1,36 @@ -# This is an example .goreleaser.yml file with some sensible defaults. -# Make sure to check the documentation at https://goreleaser.com +# .github/workflows/release.yml +name: Goreleaser -# The lines below are called `modelines`. See `:help modeline` -# Feel free to remove those if you don't want/need to use them. -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json -# vim: set ts=2 sw=2 tw=0 fo=cnqoj +on: push -version: 1 +permissions: + contents: write + # packages: write + # issues: write -before: - hooks: - # You may remove this if you don't use go modules. - - go mod tidy - # you may remove this if you don't need go generate - - go generate ./... - -builds: - - env: - - CGO_ENABLED=0 - goos: - - linux - - darwin - -archives: - - format: tar.gz - # this name template makes the OS and Arch compatible with the results of `uname`. - name_template: >- - {{ .ProjectName }}_ - {{- title .Os }}_ - {{- if eq .Arch "amd64" }}x86_64 - {{- else if eq .Arch "386" }}i386 - {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} - -changelog: - sort: asc - filters: - exclude: - - "^docs:" - - "^test:" +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: stable + # More assembly might be required: Docker logins, GPG, etc. + # It all depends on your needs. + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + # either 'goreleaser' (default) or 'goreleaser-pro' + distribution: goreleaser + # 'latest', 'nightly', or a semver + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution + # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} \ No newline at end of file From 2529d73ff51b99c195ff5f59c3d01cdd841127b4 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:03:36 +0300 Subject: [PATCH 15/20] refactor some ci --- .github/workflows/go.yml | 31 +++++++++-- .github/workflows/release.yml | 31 +++++++++++ .github/workflows/slsa-goreleaser.yml | 66 ++++++++++------------- .golangci.yml | 34 ++++++++++++ .goreleaser.yaml | 78 ++++++++++++++------------- 5 files changed, 162 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .golangci.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 6025796..200672e 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,24 +1,45 @@ # This workflow will build a golang project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go -name: Go +name: CI -on: push +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version: '1.22.x' + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + args: --timeout=5m --out-format=github-actions - name: Build run: go build -v ./... - name: Test run: go test -v ./... + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9d31e55 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.x' + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/slsa-goreleaser.yml b/.github/workflows/slsa-goreleaser.yml index b3c0c2a..3b30264 100644 --- a/.github/workflows/slsa-goreleaser.yml +++ b/.github/workflows/slsa-goreleaser.yml @@ -1,45 +1,37 @@ -name: SLSA go releaser -on: push +name: Build binaries on master +on: + push: + branches: + - master permissions: read-all jobs: - # Generate ldflags dynamically. - # Optional: only needed for ldflags. - args: + build: runs-on: ubuntu-latest - outputs: - commit-date: ${{ steps.ldflags.outputs.commit-date }} - commit: ${{ steps.ldflags.outputs.commit }} - version: ${{ steps.ldflags.outputs.version }} - tree-state: ${{ steps.ldflags.outputs.tree-state }} + strategy: + matrix: + goos: [linux, darwin] + goarch: [amd64, arm64] steps: - - id: checkout - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # tag=v2.3.4 + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 with: - fetch-depth: 0 - - id: ldflags + go-version: '1.22.x' + - name: Build ${{ matrix.goos }}-${{ matrix.goarch }} + env: + CGO_ENABLED: 0 + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} run: | - echo "commit-date=$(git log --date=iso8601-strict -1 --pretty=%ct)" >> "$GITHUB_OUTPUT" - echo "commit=$GITHUB_SHA" >> "$GITHUB_OUTPUT" - echo "version=$(git describe --tags --always --dirty | cut -c2-)" >> "$GITHUB_OUTPUT" - echo "tree-state=$(if git diff --quiet; then echo "clean"; else echo "dirty"; fi)" >> "$GITHUB_OUTPUT" - - # Trusted builder. - build: - permissions: - id-token: write # To sign the provenance. - contents: write # To upload assets to release. - actions: read # To read the workflow path. - strategy: - matrix: - os: - - linux - - darwin - arch: - - amd64 - - arm64 - uses: slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@v2.0.0 - with: - go-version: 1.22 - config-file: .slsa-goreleaser/${{matrix.os}}-${{matrix.arch}}.yml + mkdir -p dist + BIN_NAME=dcs + if [ "${{ matrix.goos }}" = "windows" ]; then BIN_NAME=dcs.exe; fi + go build -trimpath -ldflags "-s -w" -o dist/${BIN_NAME}-${{ matrix.goos }}-${{ matrix.goarch }} ./ + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: dcs-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/dcs-${{ matrix.goos }}-${{ matrix.goarch }} diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..d91acd2 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,34 @@ +run: + timeout: 5m + issues-exit-code: 1 + +linters: + enable: + - govet + - gosimple + - staticcheck + - unused + - ineffassign + - errcheck + - gocritic + - gosec + - misspell + - revive + +linters-settings: + gosec: + excludes: + - G204 # Subprocess launched with variable - safe in controlled context + revive: + rules: + - name: indent-error-flow + severity: warning + - name: exported + disabled: true + +issues: + exclude-use-default: false + exclude: + - "error return value not checked.*(Close|Log|Printf)" + - "should have comment or be unexported" + diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 23d7dd3..dca3649 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,36 +1,42 @@ -# .github/workflows/release.yml -name: Goreleaser - -on: push - -permissions: - contents: write - # packages: write - # issues: write - -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: stable - # More assembly might be required: Docker logins, GPG, etc. - # It all depends on your needs. - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v5 - with: - # either 'goreleaser' (default) or 'goreleaser-pro' - distribution: goreleaser - # 'latest', 'nightly', or a semver - version: latest - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution - # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} \ No newline at end of file +project_name: dcs +before: + hooks: + - go mod download +builds: + - id: dcs + main: ./ + binary: dcs + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + flags: ["-trimpath"] + ldflags: + - -s -w +archives: + - id: archive + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + format: tar.gz + files: + - LICENSE + - README.md +brews: + - name: dcs + repository: + owner: KNIF + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" + commit_author: + name: goreleaser + email: goreleaser@users.noreply.github.com + homepage: https://github.com/KNIF/docker-compose-secrets + description: Manage Docker Compose secrets from HashiCorp Vault +checksum: + name_template: "checksums.txt" +changelog: + sort: desc + use: git \ No newline at end of file From 9aa3cd073d75db39902ceb029b5d2b1cfe71034a Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:06:03 +0300 Subject: [PATCH 16/20] fix for linter --- app/client/http_client.go | 1 + app/environment/environment.go | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/client/http_client.go b/app/client/http_client.go index 4c16065..32e8bca 100644 --- a/app/client/http_client.go +++ b/app/client/http_client.go @@ -4,6 +4,7 @@ import ( "github.com/go-resty/resty/v2" ) +// gosec:ignore G101 -- this is a public header name constant, not a secret const HeaderVaultTokenName = "X-Vault-Token" type HttpClient struct { diff --git a/app/environment/environment.go b/app/environment/environment.go index d7b79a4..f99cd82 100644 --- a/app/environment/environment.go +++ b/app/environment/environment.go @@ -1,7 +1,6 @@ package environment import ( - "errors" "fmt" "os" ) @@ -28,11 +27,9 @@ func NewService() *Service { func (e *Service) CheckExistSystemEnv() error { for envKey, envVal := range e.systemEnv { if envVal == "" { - return errors.New( - fmt.Sprintf( - "environment variable `%s` is empty", - envKey, - ), + return fmt.Errorf( + "environment variable `%s` is empty", + envKey, ) } } From 742ca4baada6a201536f4c0892f1b0f984f0e477 Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:08:01 +0300 Subject: [PATCH 17/20] fix for linter --- app/client/http_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/http_client.go b/app/client/http_client.go index 32e8bca..fb366ab 100644 --- a/app/client/http_client.go +++ b/app/client/http_client.go @@ -4,7 +4,7 @@ import ( "github.com/go-resty/resty/v2" ) -// gosec:ignore G101 -- this is a public header name constant, not a secret +//nolint:gosec // Header name constant, not a credential const HeaderVaultTokenName = "X-Vault-Token" type HttpClient struct { From e6c72c3b365194ba68375260108fb6f5b4a73e9d Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:12:48 +0300 Subject: [PATCH 18/20] some readme exaples --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index 992a9f1..08af778 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,68 @@ The secrets are stored and managed securely in Vault and are injected into Docke With this setup, no more secrets can be leaked through insufficiently protected `.env` or `docker-compose.yml` files. +## Usage + +### Requirements + +- Docker CLI with Compose plugin (`docker compose`) +- HashiCorp Vault with KV v2 enabled at path `secret` + +### Environment variables (required) + +- `VAULT_ADDR`: Vault base URL, e.g. `http://127.0.0.1:8200` +- `VAULT_TOKEN`: Vault token with read access to the secret +- `VAULT_PATH`: Secret name under the KV v2 engine `secret` (e.g. `logto` if you wrote `secret/logto`) + +### Commands + +- `start`: runs `docker compose up -d` and injects secrets as environment variables +- `stop`: runs `docker compose down --remove-orphans` +- `restart`: runs `docker compose up -d --force-recreate` and injects secrets +- `update`: runs `docker compose pull`, and then automatically performs `restart` + +### CLI syntax + +``` +dcs + +Commands: + start | stop | restart | update +``` + +### Examples + +Set environment variables once in your shell and run a command: + +```bash +export VAULT_ADDR='http://127.0.0.1:8200' +export VAULT_TOKEN='s.xxxxxxxx' +export VAULT_PATH='logto' + +dcs start +``` + +One-liner without exporting variables globally: + +```bash +VAULT_ADDR='http://127.0.0.1:8200' \ +VAULT_TOKEN='s.xxxxxxxx' \ +VAULT_PATH='logto' \ +dcs restart +``` + +Stop and remove orphans: + +```bash +dcs stop +``` + +Pull latest images and redeploy: + +```bash +dcs update +``` + ## Demo In Vault, the secrets (environment variables) of an application (in this case: [Logto](https://logto.io)) are stored in a [KV Secrets Engine - Version 2](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2), located at the default "secret" path. From a11de13f01f6b0f4b5a8a6654e571f60a904ccbc Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:39:24 +0300 Subject: [PATCH 19/20] fix some rules --- .github/workflows/dependency-review.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index fbcc9ff..c633d1d 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -8,7 +8,9 @@ # Source repository: https://github.com/actions/dependency-review-action # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement name: 'Dependency review' -on: push +on: + pull_request: + types: [opened, synchronize, reopened] # If using a dependency submission action in this workflow this permission will need to be set to: # From 84c28d49c5a3ba33401f8f9a754b14f6ebaf5f4d Mon Sep 17 00:00:00 2001 From: Pavel Dushichev Date: Fri, 17 Oct 2025 10:45:16 +0300 Subject: [PATCH 20/20] fix some rules --- .github/workflows/release.yml | 2 +- .goreleaser.yaml | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d31e55..da4bf75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: uses: goreleaser/goreleaser-action@v5 with: distribution: goreleaser - version: latest + version: v1.26.2 args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index dca3649..2f7891c 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -24,17 +24,6 @@ archives: files: - LICENSE - README.md -brews: - - name: dcs - repository: - owner: KNIF - name: homebrew-tap - token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" - commit_author: - name: goreleaser - email: goreleaser@users.noreply.github.com - homepage: https://github.com/KNIF/docker-compose-secrets - description: Manage Docker Compose secrets from HashiCorp Vault checksum: name_template: "checksums.txt" changelog: