From 285610b730dd9c5dcc9f186cdd8111379cec408a Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:00:32 -0800 Subject: [PATCH 01/11] Add osext.EnvOrDefault --- os/env.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ os/env_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 os/env.go create mode 100644 os/env_test.go diff --git a/os/env.go b/os/env.go new file mode 100644 index 0000000..bf65c6b --- /dev/null +++ b/os/env.go @@ -0,0 +1,95 @@ +//go:build go1.18 +// +build go1.18 + +package osext + +import ( + "os" + "reflect" + "strconv" + + constraintsext "github.com/go-playground/pkg/v5/constraints" +) + +// EnvDefaults interface defines the supported types that can be used for environment variables conversions. +type EnvDefaults interface { + constraintsext.Number | ~string +} + +// EnvOrDefault retrieves the value of the environment variable named by the key. +// +// If the variable is not set, or if the conversion fails due to incorrect value, +// a default value is returned. +func EnvOrDefault[T EnvDefaults](key string, defaultValue T) T { + if v, ok := os.LookupEnv(key); ok { + + rv := reflect.ValueOf(defaultValue) + ty := rv.Type() + + switch ty.Kind() { + case reflect.String: + return reflect.ValueOf(v).Convert(ty).Interface().(T) + case reflect.Int: + i, err := strconv.ParseInt(v, 10, 0) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Int8: + i, err := strconv.ParseInt(v, 10, 8) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Int16: + i, err := strconv.ParseInt(v, 10, 16) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Int32: + i, err := strconv.ParseInt(v, 10, 32) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Int64: + i, err := strconv.ParseInt(v, 10, 64) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Uint, reflect.Uintptr: + i, err := strconv.ParseUint(v, 10, 0) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Uint8: + i, err := strconv.ParseUint(v, 10, 8) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Uint16: + i, err := strconv.ParseUint(v, 10, 16) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Uint32: + i, err := strconv.ParseUint(v, 10, 32) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Uint64: + i, err := strconv.ParseUint(v, 10, 64) + if err == nil { + return reflect.ValueOf(i).Convert(ty).Interface().(T) + } + case reflect.Float32: + f, err := strconv.ParseFloat(v, 32) + if err == nil { + return reflect.ValueOf(f).Convert(ty).Interface().(T) + } + case reflect.Float64: + f, err := strconv.ParseFloat(v, 64) + if err == nil { + return reflect.ValueOf(f).Convert(ty).Interface().(T) + } + } + } + return defaultValue +} diff --git a/os/env_test.go b/os/env_test.go new file mode 100644 index 0000000..9e4f817 --- /dev/null +++ b/os/env_test.go @@ -0,0 +1,99 @@ +package osext + +import ( + "fmt" + "os" + "testing" +) + +const constTestEnvKey = "OSEXT_TEST_ENV_KEY_DO_NOT_USE" + +type ( + CustomInt int + CustomInt8 int8 + CustomInt16 int16 + CustomInt32 int32 + CustomInt64 int64 + CustomUint uint + CustomUint8 uint8 + CustomUint16 uint16 + CustomUint32 uint32 + CustomUint64 uint64 + CustomUintptr uintptr + CustomFloat32 float32 + CustomFloat64 float64 + CustomString string +) + +func TestEnv(t *testing.T) { + tests := []struct { + name string + testFunc func() error + }{ + // Integer types + {"int", func() error { return SetAndUnsetTest(t, "24", 24, 42) }}, + {"CustomInt", func() error { return SetAndUnsetTest(t, "24", CustomInt(24), CustomInt(42)) }}, + {"int8", func() error { return SetAndUnsetTest(t, "24", int8(24), int8(42)) }}, + {"CustomInt8", func() error { return SetAndUnsetTest(t, "24", CustomInt8(24), CustomInt8(42)) }}, + {"int16", func() error { return SetAndUnsetTest(t, "24", int16(24), int16(42)) }}, + {"CustomInt16", func() error { return SetAndUnsetTest(t, "24", CustomInt16(24), CustomInt16(42)) }}, + {"int32", func() error { return SetAndUnsetTest(t, "24", int32(24), int32(42)) }}, + {"CustomInt32", func() error { return SetAndUnsetTest(t, "24", CustomInt32(24), CustomInt32(42)) }}, + {"int64", func() error { return SetAndUnsetTest(t, "24", int64(24), int64(42)) }}, + {"CustomInt64", func() error { return SetAndUnsetTest(t, "24", CustomInt64(24), CustomInt64(42)) }}, + {"uint", func() error { return SetAndUnsetTest(t, "24", uint(24), uint(42)) }}, + {"CustomUint", func() error { return SetAndUnsetTest(t, "24", CustomUint(24), CustomUint(42)) }}, + {"uint8", func() error { return SetAndUnsetTest(t, "24", uint8(24), uint8(42)) }}, + {"CustomUint8", func() error { return SetAndUnsetTest(t, "24", CustomUint8(24), CustomUint8(42)) }}, + {"uint16", func() error { return SetAndUnsetTest(t, "24", uint16(24), uint16(42)) }}, + {"CustomUint16", func() error { return SetAndUnsetTest(t, "24", CustomUint16(24), CustomUint16(42)) }}, + {"uint32", func() error { return SetAndUnsetTest(t, "24", uint32(24), uint32(42)) }}, + {"CustomUint32", func() error { return SetAndUnsetTest(t, "24", CustomUint32(24), CustomUint32(42)) }}, + {"uint64", func() error { return SetAndUnsetTest(t, "24", uint64(24), uint64(42)) }}, + {"CustomUint64", func() error { return SetAndUnsetTest(t, "24", CustomUint64(24), CustomUint64(42)) }}, + {"uintptr", func() error { return SetAndUnsetTest(t, "24", uintptr(24), uintptr(42)) }}, + {"CustomUintptr", func() error { return SetAndUnsetTest(t, "24", CustomUintptr(24), CustomUintptr(42)) }}, + + // Float types + {"float32", func() error { return SetAndUnsetTest(t, "24.5", float32(24.5), float32(42.5)) }}, + {"CustomFloat32", func() error { return SetAndUnsetTest(t, "24.5", CustomFloat32(24.5), CustomFloat32(42.5)) }}, + {"float64", func() error { return SetAndUnsetTest(t, "24.5", float64(24.5), float64(42.5)) }}, + {"CustomFloat64", func() error { return SetAndUnsetTest(t, "24.5", CustomFloat64(24.5), CustomFloat64(42.5)) }}, + + // String types + {"string", func() error { return SetAndUnsetTest(t, "hello", "hello", "world") }}, + {"CustomString", func() error { return SetAndUnsetTest(t, "hello", CustomString("hello"), CustomString("world")) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.testFunc(); err != nil { + t.Errorf("Test %s failed: %v", tt.name, err) + } + }) + } +} + +func SetAndUnsetTest[T EnvDefaults](t *testing.T, envValueSet string, expectedSet T, expectedDefault T) error { + v := EnvOrDefault(constTestEnvKey, expectedDefault) + if v != expectedDefault { + return fmt.Errorf("default value mismatch: got %v, want %v", v, expectedDefault) + } + + err := os.Setenv(constTestEnvKey, envValueSet) + if err != nil { + return fmt.Errorf("failed to set env var: %w", err) + } + + v = EnvOrDefault(constTestEnvKey, expectedDefault) + if v != expectedSet { + return fmt.Errorf("set value mismatch: got %v, want %v", v, expectedSet) + } + + err = os.Unsetenv(constTestEnvKey) + if err != nil { + return fmt.Errorf("failed to unset env var: %w", err) + } + + return nil +} From d9e0d67e51d196d703224af08e74b00a8c42ecd0 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:01:50 -0800 Subject: [PATCH 02/11] update actions --- .github/workflows/go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a2bc30a..7f5cd13 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,10 +19,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: ${{ matrix.go-version }} @@ -33,8 +33,8 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version: stable - name: golangci-lint From 349ad7fbd3e9add61035cbf02061e719929a32b0 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:02:27 -0800 Subject: [PATCH 03/11] update actions --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7f5cd13..9477da4 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -38,6 +38,6 @@ jobs: with: go-version: stable - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@v9 with: version: latest \ No newline at end of file From 744e3f4f06bcc09a445d32cfa470290eded4c910 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:04:35 -0800 Subject: [PATCH 04/11] tag test package too --- os/env_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/os/env_test.go b/os/env_test.go index 9e4f817..df36faf 100644 --- a/os/env_test.go +++ b/os/env_test.go @@ -1,3 +1,6 @@ +//go:build go1.18 +// +build go1.18 + package osext import ( From 45b5e2a8b3e8abf9a516e9e0eca2d4badd2c2506 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:06:10 -0800 Subject: [PATCH 05/11] expand go version tests --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9477da4..48fdd16 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,7 +14,7 @@ jobs: test: strategy: matrix: - go-version: [1.22.x,1.21.x,1.20.x,1.19.x,1.18.x,1.17.x] + go-version: [1.25.x,1.24.x,1.23.x,1.22.x,1.21.x,1.20.x,1.19.x,1.18.x,1.17.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: From 978827a0ff349a0c428392144d6e345d75736f6d Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:32:54 -0800 Subject: [PATCH 06/11] enhance --- os/env.go | 1 - os/env_test.go | 17 ++++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/os/env.go b/os/env.go index bf65c6b..d3dd6a2 100644 --- a/os/env.go +++ b/os/env.go @@ -22,7 +22,6 @@ type EnvDefaults interface { // a default value is returned. func EnvOrDefault[T EnvDefaults](key string, defaultValue T) T { if v, ok := os.LookupEnv(key); ok { - rv := reflect.ValueOf(defaultValue) ty := rv.Type() diff --git a/os/env_test.go b/os/env_test.go index df36faf..6ab455d 100644 --- a/os/env_test.go +++ b/os/env_test.go @@ -5,12 +5,10 @@ package osext import ( "fmt" - "os" + "math/rand" "testing" ) -const constTestEnvKey = "OSEXT_TEST_ENV_KEY_DO_NOT_USE" - type ( CustomInt int CustomInt8 int8 @@ -78,25 +76,18 @@ func TestEnv(t *testing.T) { } func SetAndUnsetTest[T EnvDefaults](t *testing.T, envValueSet string, expectedSet T, expectedDefault T) error { + constTestEnvKey := fmt.Sprintf("TEST_ENV_%d", rand.Intn(1000000)) + v := EnvOrDefault(constTestEnvKey, expectedDefault) if v != expectedDefault { return fmt.Errorf("default value mismatch: got %v, want %v", v, expectedDefault) } - err := os.Setenv(constTestEnvKey, envValueSet) - if err != nil { - return fmt.Errorf("failed to set env var: %w", err) - } + t.Setenv(constTestEnvKey, envValueSet) v = EnvOrDefault(constTestEnvKey, expectedDefault) if v != expectedSet { return fmt.Errorf("set value mismatch: got %v, want %v", v, expectedSet) } - - err = os.Unsetenv(constTestEnvKey) - if err != nil { - return fmt.Errorf("failed to unset env var: %w", err) - } - return nil } From d690d0cdb5dfff9e951bde2f1aa217e8684c19c0 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 13 Dec 2025 14:33:42 -0800 Subject: [PATCH 07/11] old linter --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 48fdd16..c17a918 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -38,6 +38,6 @@ jobs: with: go-version: stable - name: golangci-lint - uses: golangci/golangci-lint-action@v9 + uses: golangci/golangci-lint-action@v4 with: version: latest \ No newline at end of file From 6c69cace4e8799150b042793a2e7d769f40dae78 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 14 Dec 2025 09:54:56 -0800 Subject: [PATCH 08/11] simplify --- os/env.go | 84 ++++++++++++-------------------------------------- os/env_test.go | 4 +-- 2 files changed, 22 insertions(+), 66 deletions(-) diff --git a/os/env.go b/os/env.go index d3dd6a2..6a2b924 100644 --- a/os/env.go +++ b/os/env.go @@ -16,77 +16,33 @@ type EnvDefaults interface { constraintsext.Number | ~string } -// EnvOrDefault retrieves the value of the environment variable named by the key. +// Env retrieves the value of the environment variable named by the key. // // If the variable is not set, or if the conversion fails due to incorrect value, // a default value is returned. -func EnvOrDefault[T EnvDefaults](key string, defaultValue T) T { +func Env[T EnvDefaults](key string, defaultValue T) T { if v, ok := os.LookupEnv(key); ok { - rv := reflect.ValueOf(defaultValue) - ty := rv.Type() + ty := reflect.TypeOf(defaultValue) + elem := reflect.New(ty).Elem() switch ty.Kind() { case reflect.String: - return reflect.ValueOf(v).Convert(ty).Interface().(T) - case reflect.Int: - i, err := strconv.ParseInt(v, 10, 0) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Int8: - i, err := strconv.ParseInt(v, 10, 8) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Int16: - i, err := strconv.ParseInt(v, 10, 16) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Int32: - i, err := strconv.ParseInt(v, 10, 32) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Int64: - i, err := strconv.ParseInt(v, 10, 64) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Uint, reflect.Uintptr: - i, err := strconv.ParseUint(v, 10, 0) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Uint8: - i, err := strconv.ParseUint(v, 10, 8) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Uint16: - i, err := strconv.ParseUint(v, 10, 16) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Uint32: - i, err := strconv.ParseUint(v, 10, 32) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Uint64: - i, err := strconv.ParseUint(v, 10, 64) - if err == nil { - return reflect.ValueOf(i).Convert(ty).Interface().(T) - } - case reflect.Float32: - f, err := strconv.ParseFloat(v, 32) - if err == nil { - return reflect.ValueOf(f).Convert(ty).Interface().(T) - } - case reflect.Float64: - f, err := strconv.ParseFloat(v, 64) - if err == nil { - return reflect.ValueOf(f).Convert(ty).Interface().(T) + elem.SetString(v) + return elem.Interface().(T) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if i, err := strconv.ParseInt(v, 10, ty.Bits()); err == nil { + elem.SetInt(i) + return elem.Interface().(T) + } + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if i, err := strconv.ParseUint(v, 10, ty.Bits()); err == nil { + elem.SetUint(i) + return elem.Interface().(T) + } + case reflect.Float32, reflect.Float64: + if f, err := strconv.ParseFloat(v, ty.Bits()); err == nil { + elem.SetFloat(f) + return elem.Interface().(T) } } } diff --git a/os/env_test.go b/os/env_test.go index 6ab455d..5440e97 100644 --- a/os/env_test.go +++ b/os/env_test.go @@ -78,14 +78,14 @@ func TestEnv(t *testing.T) { func SetAndUnsetTest[T EnvDefaults](t *testing.T, envValueSet string, expectedSet T, expectedDefault T) error { constTestEnvKey := fmt.Sprintf("TEST_ENV_%d", rand.Intn(1000000)) - v := EnvOrDefault(constTestEnvKey, expectedDefault) + v := Env(constTestEnvKey, expectedDefault) if v != expectedDefault { return fmt.Errorf("default value mismatch: got %v, want %v", v, expectedDefault) } t.Setenv(constTestEnvKey, envValueSet) - v = EnvOrDefault(constTestEnvKey, expectedDefault) + v = Env(constTestEnvKey, expectedDefault) if v != expectedSet { return fmt.Errorf("set value mismatch: got %v, want %v", v, expectedSet) } From 8b602a0663ea4787355b29ce81581c084c893970 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 14 Dec 2025 20:42:12 -0800 Subject: [PATCH 09/11] extend, simplify and reuse --- os/env.go | 53 ++++++++++++++++++++++++++++++++++++++------------ os/env_test.go | 5 +++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/os/env.go b/os/env.go index 6a2b924..cf4874d 100644 --- a/os/env.go +++ b/os/env.go @@ -9,6 +9,8 @@ import ( "strconv" constraintsext "github.com/go-playground/pkg/v5/constraints" + . "github.com/go-playground/pkg/v5/values/option" + . "github.com/go-playground/pkg/v5/values/result" ) // EnvDefaults interface defines the supported types that can be used for environment variables conversions. @@ -21,30 +23,57 @@ type EnvDefaults interface { // If the variable is not set, or if the conversion fails due to incorrect value, // a default value is returned. func Env[T EnvDefaults](key string, defaultValue T) T { + return GetEnv[T](key).UnwrapOr(defaultValue) +} + +// GetEnv retrieves the value of the environment variable named by the key. +// +// If the variable is not set, or if the conversion fails it returns `None`, otherwise `Some`. +func GetEnv[T EnvDefaults](key string) Option[T] { + r := LookupEnv[T](key) + if r.IsErr() { + return None[T]() + } + return r.Unwrap() +} + +// LookupEnv retrieves the value of the environment variable named by the key. +// +// If the variable is not present it returns Ok(None) +// If the variable is present and conversion is successful, the value Ok(Some) is returned +// If the variable is present and conversion fails it returns Err(error) +func LookupEnv[T EnvDefaults](key string) Result[Option[T], error] { if v, ok := os.LookupEnv(key); ok { - ty := reflect.TypeOf(defaultValue) + ty := reflect.TypeFor[T]() elem := reflect.New(ty).Elem() switch ty.Kind() { case reflect.String: elem.SetString(v) - return elem.Interface().(T) + return Ok[Option[T], error](Some(elem.Interface().(T))) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if i, err := strconv.ParseInt(v, 10, ty.Bits()); err == nil { - elem.SetInt(i) - return elem.Interface().(T) + i, err := strconv.ParseInt(v, 10, ty.Bits()) + if err != nil { + return Err[Option[T], error](err) } + elem.SetInt(i) + return Ok[Option[T], error](Some(elem.Interface().(T))) + case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if i, err := strconv.ParseUint(v, 10, ty.Bits()); err == nil { - elem.SetUint(i) - return elem.Interface().(T) + i, err := strconv.ParseUint(v, 10, ty.Bits()) + if err != nil { + return Err[Option[T], error](err) } + elem.SetUint(i) + return Ok[Option[T], error](Some(elem.Interface().(T))) case reflect.Float32, reflect.Float64: - if f, err := strconv.ParseFloat(v, ty.Bits()); err == nil { - elem.SetFloat(f) - return elem.Interface().(T) + f, err := strconv.ParseFloat(v, ty.Bits()) + if err != nil { + return Err[Option[T], error](err) } + elem.SetFloat(f) + return Ok[Option[T], error](Some(elem.Interface().(T))) } } - return defaultValue + return Ok[Option[T], error](None[T]()) } diff --git a/os/env_test.go b/os/env_test.go index 5440e97..526eaba 100644 --- a/os/env_test.go +++ b/os/env_test.go @@ -64,6 +64,11 @@ func TestEnv(t *testing.T) { // String types {"string", func() error { return SetAndUnsetTest(t, "hello", "hello", "world") }}, {"CustomString", func() error { return SetAndUnsetTest(t, "hello", CustomString("hello"), CustomString("world")) }}, + + // Conversion failure test cases - these should return default values when conversion fails + {"int_conversion_failure", func() error { return SetAndUnsetTest(t, "not_a_number", 42, 42) }}, + {"float32_conversion_failure", func() error { return SetAndUnsetTest(t, "not_a_float", float32(42.5), float32(42.5)) }}, + {"float64_conversion_failure", func() error { return SetAndUnsetTest(t, "not_a_float", float64(42.5), float64(42.5)) }}, } for _, tt := range tests { From 97b758e2eb954846de9380e1dd02224c7226d571 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 14 Dec 2025 20:45:13 -0800 Subject: [PATCH 10/11] increase version --- os/env.go | 4 ++-- os/env_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/os/env.go b/os/env.go index cf4874d..9e07fae 100644 --- a/os/env.go +++ b/os/env.go @@ -1,5 +1,5 @@ -//go:build go1.18 -// +build go1.18 +//go:build go1.22 +// +build go1.22 package osext diff --git a/os/env_test.go b/os/env_test.go index 526eaba..07a6685 100644 --- a/os/env_test.go +++ b/os/env_test.go @@ -1,5 +1,5 @@ -//go:build go1.18 -// +build go1.18 +//go:build go1.22 +// +build go1.22 package osext From 1db650dd0fcff89c0b4b174bc37981b6f9589b2f Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 20 Dec 2025 13:00:02 -0800 Subject: [PATCH 11/11] update changelog --- CHANGELOG.md | 7 ++++++- README.md | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7553da..92e1048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.31.0] - 2025-12-20 +### Added +- Add new `osext` package with some helpers for fetching and parsing ENV variables. + ## [5.30.0] - 2024-06-01 ### Changed - Changed NanoTome to not use linkname due to Go1.23 upcoming breaking changes. @@ -136,7 +140,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `timext.NanoTime` for fast low level monotonic time with nanosecond precision. -[Unreleased]: https://github.com/go-playground/pkg/compare/v5.30.0...HEAD +[Unreleased]: https://github.com/go-playground/pkg/compare/v5.31.0...HEAD +[5.31.0]: https://github.com/go-playground/pkg/compare/v5.30.0..v5.31.0 [5.30.0]: https://github.com/go-playground/pkg/compare/v5.29.1..v5.30.0 [5.29.1]: https://github.com/go-playground/pkg/compare/v5.29.0..v5.29.1 [5.29.0]: https://github.com/go-playground/pkg/compare/v5.28.1..v5.29.0 diff --git a/README.md b/README.md index 02b8072..316018d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pkg -![Project status](https://img.shields.io/badge/version-5.30.0-green.svg) +[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/go-playground/pkg)](https://github.com/go-playground/pkg/releases) [![Lint & Test](https://github.com/go-playground/pkg/actions/workflows/go.yml/badge.svg)](https://github.com/go-playground/pkg/actions/workflows/go.yml) [![Coverage Status](https://coveralls.io/repos/github/go-playground/pkg/badge.svg?branch=master)](https://coveralls.io/github/go-playground/pkg?branch=master) [![GoDoc](https://godoc.org/github.com/go-playground/pkg?status.svg)](https://pkg.go.dev/mod/github.com/go-playground/pkg/v5)