Skip to content

Commit dcf35b1

Browse files
committed
initial scaffold: envstruct project structure
0 parents  commit dcf35b1

17 files changed

Lines changed: 378 additions & 0 deletions

.github/dependabot.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "gomod"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
- package-ecosystem: "github-actions"
8+
directory: "/"
9+
schedule:
10+
interval: "weekly"

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
go-version: ['1.23', '1.24']
15+
steps:
16+
- uses: actions/checkout@v6
17+
- uses: actions/setup-go@v5
18+
with:
19+
go-version: ${{ matrix.go-version }}
20+
- run: go test ./... -count=1 -race
21+
22+
lint:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/checkout@v6
26+
- uses: actions/setup-go@v5
27+
with:
28+
go-version: '1.24'
29+
- uses: golangci/golangci-lint-action@v6
30+
with:
31+
version: latest

.github/workflows/publish.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Publish
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
verify:
9+
runs-on: ubuntu-latest
10+
strategy:
11+
matrix:
12+
go-version: ['1.23', '1.24']
13+
steps:
14+
- uses: actions/checkout@v6
15+
- uses: actions/setup-go@v5
16+
with:
17+
go-version: ${{ matrix.go-version }}
18+
- name: Test
19+
run: go test ./... -race -count=1
20+
- name: Vet
21+
run: go vet ./...
22+
23+
index:
24+
needs: verify
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Request pkg.go.dev indexing
28+
run: |
29+
VERSION="${{ github.event.release.tag_name }}"
30+
curl -s "https://proxy.golang.org/github.com/agentine/envstruct/@v/${VERSION}.info" || true
31+
curl -s "https://sum.golang.org/lookup/github.com/agentine/envstruct@${VERSION}" || true
32+
echo "Requested indexing for github.com/agentine/envstruct@${VERSION}"

.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Binaries
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary
9+
*.test
10+
11+
# Output
12+
*.out
13+
14+
# Coverage
15+
coverage.txt
16+
coverage.html
17+
18+
# IDE
19+
.idea/
20+
.vscode/
21+
*.swp
22+
*.swo
23+
*~
24+
25+
# OS
26+
.DS_Store
27+
Thumbs.db

Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.PHONY: install build test lint fmt clean ci
2+
3+
install:
4+
go mod tidy
5+
6+
build:
7+
go build ./...
8+
9+
test:
10+
go test -race -count=1 ./...
11+
12+
lint:
13+
go vet ./...
14+
golangci-lint run
15+
16+
fmt:
17+
gofmt -w .
18+
19+
clean:
20+
go clean
21+
rm -rf bin/
22+
23+
ci: lint test

PLAN.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# envstruct
2+
3+
**Replacement for:** [kelseyhightower/envconfig](https://github.com/kelseyhightower/envconfig)
4+
**Language:** Go
5+
**Package:** `github.com/agentine/envstruct`
6+
7+
## Why
8+
9+
kelseyhightower/envconfig has 8,524 importers and 20.2K GitHub dependents but has been dormant since May 2019 (last release v1.4.0). The sole maintainer is no longer active in OSS. There are 27 open issues and 31 open PRs with zero maintainer engagement. Existing alternatives (caarlos0/env, sethvargo/go-envconfig) have not captured envconfig's market share.
10+
11+
## Scope
12+
13+
A Go library that populates struct fields from environment variables using struct tags. Drop-in replacement for envconfig with modern Go practices.
14+
15+
## Core Features
16+
17+
1. **Struct tag parsing**`env:"VAR_NAME"` tags to map env vars to struct fields
18+
2. **Type support** — string, int (all sizes), uint, float, bool, time.Duration, url.URL, custom types
19+
3. **Prefix support** — scoped env var lookup via configurable prefix (e.g., `APP_`)
20+
4. **Nested structs** — flatten or use delimiter-separated names (e.g., `APP_DB_HOST`)
21+
5. **Required fields**`env:"VAR_NAME,required"` tag option
22+
6. **Default values**`env:"VAR_NAME" default:"value"` tag
23+
7. **Custom decoders**`Decoder` interface for user-defined types
24+
8. **Slice/map support** — comma-separated values for slices, key=value pairs for maps
25+
9. **Usage generation** — auto-generate usage text from struct tags
26+
10. **Error reporting** — clear error messages with field name, expected type, and env var name
27+
28+
## Architecture
29+
30+
```
31+
envstruct/
32+
├── envstruct.go # Core Process/MustProcess functions
33+
├── decoder.go # Type decoders and Decoder interface
34+
├── tags.go # Struct tag parsing
35+
├── usage.go # Usage text generation
36+
├── errors.go # Typed error types
37+
├── envstruct_test.go # Core tests
38+
├── decoder_test.go # Decoder tests
39+
├── usage_test.go # Usage tests
40+
├── example_test.go # Runnable examples
41+
├── go.mod
42+
├── go.sum
43+
└── README.md
44+
```
45+
46+
## API Surface
47+
48+
```go
49+
// Process populates a struct from environment variables.
50+
func Process(prefix string, spec interface{}) error
51+
52+
// MustProcess is like Process but panics on error.
53+
func MustProcess(prefix string, spec interface{})
54+
55+
// Usage writes a usage message to the given writer.
56+
func Usage(prefix string, spec interface{}, out io.Writer) error
57+
58+
// Decoder is implemented by types that can decode themselves from a string.
59+
type Decoder interface {
60+
Decode(value string) error
61+
}
62+
63+
// Setter is implemented by types that can set themselves from a string (envconfig compat).
64+
type Setter interface {
65+
Set(value string) error
66+
}
67+
```
68+
69+
## Migration Path
70+
71+
- Same `Process(prefix, &spec)` function signature as envconfig
72+
- Supports envconfig's struct tag format for easy migration
73+
- Also supports `env:"NAME"` tag format (more conventional)
74+
- Decoder interface is compatible with envconfig's interface
75+
76+
## Deliverables
77+
78+
1. Core library with full type support
79+
2. Comprehensive test suite (>90% coverage)
80+
3. README with migration guide from envconfig
81+
4. Runnable examples
82+
5. CI configuration (GitHub Actions)
83+
84+
## Non-Goals
85+
86+
- File-based config (.env, YAML, TOML) — use dedicated libraries
87+
- Config hot-reloading — environment variables are read once at startup
88+
- CLI flag parsing — separate concern

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# envstruct
2+
3+
A Go library that populates struct fields from environment variables. Drop-in replacement for [kelseyhightower/envconfig](https://github.com/kelseyhightower/envconfig).
4+
5+
## Install
6+
7+
```
8+
go get github.com/agentine/envstruct
9+
```
10+
11+
## License
12+
13+
MIT

decoder.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package envstruct
2+
3+
// Decoder is implemented by types that can decode themselves from a string.
4+
type Decoder interface {
5+
Decode(value string) error
6+
}
7+
8+
// Setter is implemented by types that can set themselves from a string.
9+
// This interface exists for compatibility with kelseyhightower/envconfig.
10+
type Setter interface {
11+
Set(value string) error
12+
}

decoder_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package envstruct
2+
3+
import "testing"
4+
5+
func TestDecoderInterface(t *testing.T) {
6+
// Verify Decoder and Setter interfaces are defined.
7+
var _ Decoder = (*testDecoder)(nil)
8+
var _ Setter = (*testSetter)(nil)
9+
}
10+
11+
type testDecoder struct{ val string }
12+
13+
func (d *testDecoder) Decode(value string) error { d.val = value; return nil }
14+
15+
type testSetter struct{ val string }
16+
17+
func (s *testSetter) Set(value string) error { s.val = value; return nil }

envstruct.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Package envstruct populates struct fields from environment variables.
2+
//
3+
// It is a drop-in replacement for kelseyhightower/envconfig with the same
4+
// Process(prefix, &spec) function signature.
5+
package envstruct
6+
7+
import "io"
8+
9+
// Process populates the struct pointed to by spec with values from
10+
// environment variables. The prefix is prepended to each field name
11+
// (or tag override) when looking up environment variables.
12+
func Process(prefix string, spec interface{}) error {
13+
return nil
14+
}
15+
16+
// MustProcess is like Process but panics on error.
17+
func MustProcess(prefix string, spec interface{}) {
18+
if err := Process(prefix, spec); err != nil {
19+
panic(err)
20+
}
21+
}
22+
23+
// Usage writes a usage message describing the environment variables
24+
// expected by spec to the given writer.
25+
func Usage(prefix string, spec interface{}, out io.Writer) error {
26+
return nil
27+
}

0 commit comments

Comments
 (0)