diff --git a/pkg/environment/default.go b/pkg/environment/default.go index cbe5714d0..d9b7d4826 100644 --- a/pkg/environment/default.go +++ b/pkg/environment/default.go @@ -1,10 +1,17 @@ package environment func NewDefaultProvider() Provider { - return NewMultiProvider( + p := []Provider{ NewOsEnvProvider(), NewNoFailProvider( NewOnePasswordProvider(), ), - ) + } + + passProvider, err := NewPassProvider() + if err == nil { + p = append(p, passProvider) + } + + return NewMultiProvider(p...) } diff --git a/pkg/environment/pass.go b/pkg/environment/pass.go new file mode 100644 index 000000000..ea54496eb --- /dev/null +++ b/pkg/environment/pass.go @@ -0,0 +1,51 @@ +package environment + +import ( + "bytes" + "context" + "errors" + "fmt" + "log/slog" + "os/exec" + "strings" +) + +// PassProvider is a provider that retrieves secrets using the `pass` password +// manager. +type PassProvider struct{} + +type ErrPassNotAvailable struct{} + +func (ErrPassNotAvailable) Error() string { + return "pass is not installed" +} + +// NewPassProvider creates a new PassProvider instance. +func NewPassProvider() (*PassProvider, error) { + path, err := exec.LookPath("pass") + if err != nil && !errors.Is(err, exec.ErrNotFound) { + slog.Warn("failed to lookup `pass` binary", "error", err) + } + if path == "" { + return nil, ErrPassNotAvailable{} + } + return &PassProvider{}, nil +} + +// Get retrieves the value of a secret by its name using the `pass` CLI. +// The name corresponds to the path in the `pass` store. +func (p *PassProvider) Get(ctx context.Context, name string) (string, error) { + cmd := exec.CommandContext(ctx, "pass", "show", name) + + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("failed to retrieve secret with `pass`: %w, stderr: %v", err, stderr.String()) + } + + return strings.TrimSpace(out.String()), nil +} diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index 38a0ef3da..abf4cff28 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -157,7 +157,7 @@ func getModelsForAgent(ctx context.Context, cfg *latest.Config, a *latest.AgentC return nil, fmt.Errorf("model '%s' not found in configuration", name) } - env := environment.NewMultiProvider( + providers := []environment.Provider{ environment.NewKeyValueProvider(modelCfg.Env), environment.NewKeyValueProvider(cfg.Env), environment.NewEnvFilesProvider(absEnvFiles), @@ -165,7 +165,15 @@ func getModelsForAgent(ctx context.Context, cfg *latest.Config, a *latest.AgentC environment.NewNoFailProvider( environment.NewOnePasswordProvider(), ), - ) + } + + // Append pass provider at the end if available + passProvider, err := environment.NewPassProvider() + if err == nil { + providers = append(providers, passProvider) + } + + env := environment.NewMultiProvider(providers...) model, err := provider.New(ctx, &modelCfg, env, opts...) if err != nil {