Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# envault
# envchain

## Overview

`envault` is a tiny helper for Go services that **backfills** environment variables from one or more **providers** — without overwriting anything that’s already set. It also provides a small helper, `GetEnvOrDefault`, for reading environment variables with defaults.
`envchain` is a tiny helper for Go services that **backfills** environment variables from one or more **providers** — without overwriting anything that’s already set. It also provides a small helper, `GetEnvOrDefault`, for reading environment variables with defaults.

#### Providers Available:

Expand All @@ -26,12 +26,12 @@ Local dev:

1. Install the CLI:
```bash
go install github.com/stuft2/envault/cmd/envchain@latest
go install github.com/stuft2/envchain/cmd/envchain@latest
```
1. Create a sample `.env`:
```bash
cat > .env <<'EOF'
APP_NAME=envault-demo
APP_NAME=envchain-demo
PORT=8080
EOF
```
Expand All @@ -41,7 +41,7 @@ Local dev:
```
1. Verify output:
```text
APP_NAME=envault-demo
APP_NAME=envchain-demo
PORT=8080
```

Expand Down Expand Up @@ -70,28 +70,28 @@ CI/Prod: rely on process env only. If you don’t set `VAULT_ADDR`, the Vault pr

### Injecting Environment Variables

Construct the providers you want and pass them to `envault.Inject` in **precedence order**:
Construct the providers you want and pass them to `envchain.Inject` in **precedence order**:

```go
package main

import (
"log"

"github.com/stuft2/envault"
"github.com/stuft2/envault/providers/dotenv"
"github.com/stuft2/envault/providers/vault"
"github.com/stuft2/envchain"
"github.com/stuft2/envchain/providers/dotenv"
"github.com/stuft2/envchain/providers/vault"
)

func main() {
// Precedence: keep existing env, then .env, then Vault.
if err := envault.Inject(dotenv.NewProvider(".env"), vault.NewProvider("kvv2/byuapi-persons-v4/dev/env-vars")); err != nil {
if err := envchain.Inject(dotenv.NewProvider(".env"), vault.NewProvider("kvv2/byuapi-persons-v4/dev/env-vars")); err != nil {
// could make provider errors fatal, but we're assuming that deployed environments
// will always have config and secrets injected before server start.
log.Printf("env injection warnings: %v", err)
}

// ... retrieve env vars with os.GetEnv or envault.GetEnvOrDefault
// ... retrieve env vars with os.GetEnv or envchain.GetEnvOrDefault
}
```

Expand All @@ -114,13 +114,13 @@ Context: The Vault provider uses a background context by default. To override:
```go
p := vault.NewProvider("/app/web")
p.Context = ctx // set deadlines, cancellation, etc.
if err := envault.Inject(p); err != nil { /* ... */ }
if err := envchain.Inject(p); err != nil { /* ... */ }
```

Or pass one shared context across all context-aware providers:

```go
if err := envault.InjectWithContext(ctx, dotenv.NewProvider(".env"), vault.NewProvider("/app/web")); err != nil {
if err := envchain.InjectWithContext(ctx, dotenv.NewProvider(".env"), vault.NewProvider("/app/web")); err != nil {
// handle joined provider errors
}
```
Expand Down Expand Up @@ -149,19 +149,19 @@ import (
"fmt"
"os"

"github.com/stuft2/envault"
"github.com/stuft2/envchain"
)

func main() {
// Example: PORT will default to 8080 if not set.
port := envault.GetEnvOrDefault("PORT", "8080")
addr := envault.GetEnvOrDefault("ADDR", ":http")
port := envchain.GetEnvOrDefault("PORT", "8080")
addr := envchain.GetEnvOrDefault("ADDR", ":http")

fmt.Println("Starting server on", addr, "port", port)

// Example with empty string (treated as set):
_ = os.Setenv("DEBUG", "")
debug := envault.GetEnvOrDefault("DEBUG", "false")
debug := envchain.GetEnvOrDefault("DEBUG", "false")
fmt.Println("Debug mode =", debug)
}
```
Expand Down Expand Up @@ -198,11 +198,11 @@ Symptom: `vault: invalid VAULT_ADDR "...": ...`
Cause: `VAULT_ADDR` is malformed (missing scheme, invalid host, or invalid URL).
Fix: use a valid URL such as `https://vault.byu.edu` (or `https://vault.byu.edu/v1`).

Symptom: `Usage: envault [flags] -- command [args...]`
Symptom: `Usage: envchain [flags] -- command [args...]`
Cause: command was not passed after `--`.
Fix: provide a command after separator, for example `envchain -- env`.

Symptom: `envault: failed to execute "..."`
Symptom: `envchain: failed to execute "..."`
Cause: the command after `--` is missing from `PATH` or not executable.
Fix: verify the executable name and run `which <command>` to confirm availability.

Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ tasks:
- golangci-lint fmt

build:
desc: Build the envault CLI
desc: Build the envchain CLI
cmds:
- go build ./cmd/envchain

Expand Down
20 changes: 10 additions & 10 deletions cmd/envchain/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import (
"os"
"os/exec"

"github.com/stuft2/envault"
"github.com/stuft2/envault/internal"
"github.com/stuft2/envault/providers/dotenv"
"github.com/stuft2/envault/providers/vault"
"github.com/stuft2/envchain"
"github.com/stuft2/envchain/internal"
"github.com/stuft2/envchain/providers/dotenv"
"github.com/stuft2/envchain/providers/vault"
)

func main() {
os.Exit(run(os.Args[1:]))
}

func run(args []string) int {
return runWithDeps(args, os.Stdin, os.Stdout, os.Stderr, envault.Inject, gatherProviders, defaultCommandExecutor)
return runWithDeps(args, os.Stdin, os.Stdout, os.Stderr, envchain.Inject, gatherProviders, defaultCommandExecutor)
}

type injectFunc func(...internal.Provider) error
Expand All @@ -36,15 +36,15 @@ func runWithDeps(
gather gatherProvidersFunc,
executeCommand commandExecutorFunc,
) int {
fs := flag.NewFlagSet("envault", flag.ContinueOnError)
fs := flag.NewFlagSet("envchain", flag.ContinueOnError)
fs.SetOutput(stderr)

dotenvPath := fs.String("dotenv", ".env", "path to a dotenv file (empty to skip)")
vaultPath := fs.String("vault-path", "", "Vault KV v2 secret path to read (empty to skip)")
verbose := fs.Bool("verbose", false, "enable verbose logging")

fs.Usage = func() {
fmt.Fprintln(fs.Output(), "Usage: envault [flags] -- command [args...]")
fmt.Fprintln(fs.Output(), "Usage: envchain [flags] -- command [args...]")
fmt.Fprintln(fs.Output())
fmt.Fprintln(fs.Output(), "Flags:")
fs.PrintDefaults()
Expand All @@ -64,20 +64,20 @@ func runWithDeps(
}

if *verbose {
logger := log.New(stderr, "envault: ", log.LstdFlags)
logger := log.New(stderr, "envchain: ", log.LstdFlags)
internal.SetLogger(logger)
internal.Debugf("verbose logging enabled")
}

providers := gather(*dotenvPath, *vaultPath)
if err := inject(providers...); err != nil {
fmt.Fprintf(stderr, "envault: %v\n", err)
fmt.Fprintf(stderr, "envchain: %v\n", err)
return 1
}

exitCode, err := executeCommand(rest[0], rest[1:], stdin, stdout, stderr)
if err != nil {
fmt.Fprintf(stderr, "envault: failed to execute %q: %v\n", rest[0], err)
fmt.Fprintf(stderr, "envchain: failed to execute %q: %v\n", rest[0], err)
return 1
}
return exitCode
Expand Down
18 changes: 9 additions & 9 deletions cmd/envchain/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"strings"
"testing"

"github.com/stuft2/envault"
"github.com/stuft2/envault/internal"
"github.com/stuft2/envault/providers/dotenv"
"github.com/stuft2/envault/providers/vault"
"github.com/stuft2/envchain"
"github.com/stuft2/envchain/internal"
"github.com/stuft2/envchain/providers/dotenv"
"github.com/stuft2/envchain/providers/vault"
)

func TestRunHelpReturnsZeroAndUsage(t *testing.T) {
Expand All @@ -33,7 +33,7 @@ func TestRunHelpReturnsZeroAndUsage(t *testing.T) {
if code != 0 {
t.Fatalf("runWithDeps code=%d want 0", code)
}
if got := stderr.String(); !strings.Contains(got, "Usage: envault [flags] -- command [args...]") {
if got := stderr.String(); !strings.Contains(got, "Usage: envchain [flags] -- command [args...]") {
t.Fatalf("usage output missing; got %q", got)
}
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestRunNonExistentCommandReturnsExecutionError(t *testing.T) {

var stderr bytes.Buffer
code := runWithDeps(
[]string{"--", "definitely-not-a-real-command-envault-test"},
[]string{"--", "definitely-not-a-real-command-envchain-test"},
strings.NewReader(""),
&bytes.Buffer{},
&stderr,
Expand All @@ -94,7 +94,7 @@ func TestRunNonExistentCommandReturnsExecutionError(t *testing.T) {
t.Fatalf("runWithDeps code=%d want 1", code)
}
msg := stderr.String()
if !strings.Contains(msg, `failed to execute "definitely-not-a-real-command-envault-test"`) {
if !strings.Contains(msg, `failed to execute "definitely-not-a-real-command-envchain-test"`) {
t.Fatalf("stderr missing execution error message; got %q", msg)
}
}
Expand Down Expand Up @@ -150,7 +150,7 @@ func TestRunVerboseEnablesDebugLoggingWithoutSecretLeakage(t *testing.T) {
strings.NewReader(""),
&bytes.Buffer{},
&stderr,
envault.Inject,
envchain.Inject,
gatherProviders,
func(string, []string, io.Reader, io.Writer, io.Writer) (int, error) { return 0, nil },
)
Expand Down Expand Up @@ -183,7 +183,7 @@ func TestRunProviderErrorReturnsOne(t *testing.T) {
if code != 1 {
t.Fatalf("runWithDeps code=%d want 1", code)
}
if got := stderr.String(); !strings.Contains(got, "envault: inject failed") {
if got := stderr.String(); !strings.Contains(got, "envchain: inject failed") {
t.Fatalf("stderr missing provider failure; got %q", got)
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# envault Docs
# envchain Docs

This folder outlines the remaining work to make `envault` easy to adopt across teams and environments.
This folder outlines the remaining work to make `envchain` easy to adopt across teams and environments.

## Roadmap

Expand Down
2 changes: 1 addition & 1 deletion docs/features/project-license.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Goal

Provide clear legal terms so organizations can evaluate and adopt `envault` without ambiguity.
Provide clear legal terms so organizations can evaluate and adopt `envchain` without ambiguity.

## Problem

Expand Down
2 changes: 1 addition & 1 deletion docs/features/quickstart-and-troubleshooting-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Current README explains concepts well, but new users still need a fast copy/past

1. Install CLI.
2. Create sample `.env`.
3. Run `envault -- env` (or equivalent) and verify expected variables.
3. Run `envchain -- env` (or equivalent) and verify expected variables.
4. Optional Vault example with required env vars.

## Troubleshooting Scope
Expand Down
4 changes: 2 additions & 2 deletions docs/features/release-binaries-and-checksums.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Goal

Allow users to install and use `envault` without requiring a local Go toolchain.
Allow users to install and use `envchain` without requiring a local Go toolchain.

## Problem

Expand All @@ -23,7 +23,7 @@ Current installation flow assumes Go is installed and configured. This is a barr
## Implementation Tasks

1. Add a release workflow (e.g., GoReleaser or custom GitHub Actions).
2. Produce archives per platform with `envault` binary.
2. Produce archives per platform with `envchain` binary.
3. Generate `checksums.txt` for each release.
4. Document install methods:
- direct binary download
Expand Down
2 changes: 1 addition & 1 deletion env_default.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package envault
package envchain

import "os"

Expand Down
2 changes: 1 addition & 1 deletion env_default_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package envault
package envchain

import (
"fmt"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/stuft2/envault
module github.com/stuft2/envchain

go 1.26

Expand Down
4 changes: 2 additions & 2 deletions inject.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package envault
package envchain

import (
"context"
"errors"

"github.com/stuft2/envault/internal"
"github.com/stuft2/envchain/internal"
)

func Inject(providers ...internal.Provider) error {
Expand Down
4 changes: 2 additions & 2 deletions inject_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package envault
package envchain

import (
"context"
"errors"
"strings"
"testing"

"github.com/stuft2/envault/internal"
"github.com/stuft2/envchain/internal"
)

type stubProvider struct {
Expand Down
2 changes: 1 addition & 1 deletion providers/dotenv/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"os"

"github.com/joho/godotenv"
"github.com/stuft2/envault/internal"
"github.com/stuft2/envchain/internal"
)

type Provider struct {
Expand Down
2 changes: 1 addition & 1 deletion providers/vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"strings"
"time"

"github.com/stuft2/envault/internal"
"github.com/stuft2/envchain/internal"
)

type Provider struct {
Expand Down