Skip to content

Commit

Permalink
add nilgo.Run (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktong authored Apr 25, 2024
1 parent a595e18 commit 37d5caa
Show file tree
Hide file tree
Showing 28 changed files with 762 additions and 42 deletions.
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ updates:
schedule:
interval: weekly

- package-ecosystem: gomod
directory: /gcp
labels:
- Skip-Changelog
schedule:
interval: weekly

- package-ecosystem: gomod
directory: /grpc
labels:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
if: ${{ github.actor != 'dependabot[bot]' }}
strategy:
matrix:
module: [ '', 'grpc' ]
module: [ '', 'gcp', 'grpc' ]
name: Coverage
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
lint:
strategy:
matrix:
module: [ '', 'grpc' ]
module: [ '', 'gcp', 'grpc' ]
name: Lint
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
with:
script: |
const modules = [
'gcp',
'grpc'
]
for (const module of modules) {
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
test:
strategy:
matrix:
module: [ '', 'grpc' ]
module: [ '', 'gcp', 'grpc' ]
go-version: [ 'stable', 'oldstable' ]
name: Test
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# All files and folders start with .
.*

# Go workspace files
go.work
go.work.sum

# Go vendor folder
vendor
5 changes: 4 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ linters-settings:
Use of this source code is governed by a MIT license found in the LICENSE file.
goimports:
local-prefixes: github.com/nil-go/nilgo
gomoddirectives:
replace-local: true
govet:
check-shadowing: true
enable:
- shadow
makezero:
always: true
misspell:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- Add Runner for parallel execution (#9).
- Support gRPC server (#15).
- Add nilgo.Run (#22).
68 changes: 68 additions & 0 deletions config/konf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2024 The nilgo authors
// Use of this source code is governed by a MIT license found in the LICENSE file.

// Package config provides an application configuration loader base on [konf].
//
// # Configuration Sources
//
// It loads configuration from the following sources,
// and each source takes precedence over the sources below it:
//
// - config files specified by WithFS and WithFile.
// WithFile also can be overridden by the environment variable `CONFIG_FILE`.
// For example, if CONFIG_FILE = "f1, f2,f3", it will load f1, f2, and f3,
// and each file takes precedence over the files before it.
// - environment variables which matches the following pattern:
// prefix + "_" + key, all in ALL CAPS.
// For example, FOO_BAR is the name of environment variable for configuration `foo.bar`.
//
// [konf]: https://pkg.go.dev/github.com/nil-go/konf
package config

import (
"errors"
"fmt"
"log/slog"
"os"

"github.com/nil-go/konf"
"github.com/nil-go/konf/provider/env"
"github.com/nil-go/konf/provider/fs"
"gopkg.in/yaml.v3"
)

// New creates a new konf.Config with the given Option(s).
func New(opts ...Option) (*konf.Config, error) {
options := options{}
for _, opt := range opts {
opt(&options)
}
if files := konf.Get[[]string]("config.file"); len(files) > 0 {
options.files = files
}
if len(options.files) == 0 {
options.files = []string{"config/config.yaml"}
}

config := konf.New(options.opts...)
for _, file := range options.files {
if err := config.Load(fs.New(options.fs, file, fs.WithUnmarshal(yaml.Unmarshal))); err != nil {
var e *os.PathError
if !errors.As(err, &e) {
return nil, fmt.Errorf("load config file %s: %w", file, err)
}

// Ignore not found error since config file is optional.
slog.Warn("Config file not found.", "file", file)
}
}
// Ignore error: env loader does not return error.
_ = config.Load(env.New())
if options.fn != nil {
if err := options.fn(config); err != nil {
return nil, err
}
}

return config, nil
}
150 changes: 150 additions & 0 deletions config/konf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (c) 2024 The nilgo authors
// Use of this source code is governed by a MIT license found in the LICENSE file.

package config_test

import (
"errors"
"testing"
"testing/fstest"

"github.com/nil-go/konf"
"github.com/nil-go/konf/provider/env"
"github.com/nil-go/konf/provider/fs"
"gopkg.in/yaml.v3"

"github.com/nil-go/nilgo/config"
"github.com/nil-go/nilgo/internal/assert"
)

func TestNew(t *testing.T) {
testcases := []struct {
description string
opts []config.Option
env map[string]string
key string
explanation string
err string
}{
{
description: "config file",
opts: []config.Option{config.WithFile("testdata/config.yaml")},
key: "nilgo.source",
explanation: `nilgo.source has value[file] that is loaded by loader[fs:///testdata/config.yaml].
`,
},
{
description: "multiple config files",
opts: []config.Option{config.WithFile("testdata/config.yaml", "testdata/staging.yaml")},
key: "nilgo.stage",
explanation: `nilgo.stage has value[staging] that is loaded by loader[fs:///testdata/staging.yaml].
Here are other value(loader)s:
- dev(fs:///testdata/config.yaml)
`,
},
{
description: "with environment variables",
opts: []config.Option{config.WithFile("testdata/config.yaml")},
env: map[string]string{"NILGO_SOURCE": "env"},
key: "nilgo.source",
explanation: `nilgo.source has value[env] that is loaded by loader[env:*].
Here are other value(loader)s:
- file(fs:///testdata/config.yaml)
`,
},
{
description: "config file path in environment variable",
env: map[string]string{"CONFIG_FILE": "testdata/config.yaml"},
key: "nilgo.source",
explanation: `nilgo.source has value[file] that is loaded by loader[fs:///testdata/config.yaml].
`,
},
{
description: "default config file not found",
key: "nilgo.source",
explanation: `nilgo.source has no configuration.
`,
},
{
description: "with fs",
opts: []config.Option{config.WithFS(fstest.MapFS{"config/config.yaml": {Data: []byte("nilgo:\n source: fs")}})},
key: "nilgo.source",
explanation: `nilgo.source has value[fs] that is loaded by loader[fs:///config/config.yaml].
`,
},
{
description: "with option",
opts: []config.Option{
config.WithOption(konf.WithCaseSensitive()),
config.WithFS(fstest.MapFS{"config/config.yaml": {Data: []byte("nilgo:\n source: fs")}}),
},
key: "nilgo.Source",
explanation: `nilgo.Source has no configuration.
`,
},
{
description: "with",
opts: []config.Option{
config.With(func(cfg *konf.Config) error {
return cfg.Load(fs.New(nil, "testdata/config.yaml", fs.WithUnmarshal(yaml.Unmarshal)))
}),
config.WithFS(fstest.MapFS{"config/config.yaml": {Data: []byte("nilgo:\n source: fs")}}),
},
key: "nilgo.Source",
explanation: `nilgo.Source has value[file] that is loaded by loader[fs:///testdata/config.yaml].
Here are other value(loader)s:
- fs(fs:///config/config.yaml)
`,
},
{
description: "unmarshal error",
opts: []config.Option{config.WithFS(fstest.MapFS{"config/config.yaml": {Data: []byte("nilgo")}})},
key: "nilgo.source",
err: "load config file config/config.yaml: load configuration: unmarshal: yaml: unmarshal errors:\n" +
" line 1: cannot unmarshal !!str `nilgo` into map[string]interface {}",
},
{
description: "with error",
opts: []config.Option{
config.With(func(*konf.Config) error {
return errors.New("with error")
}),
config.WithFS(fstest.MapFS{"config/config.yaml": {Data: []byte("nilgo:\n source: fs")}}),
},
key: "nilgo.Source",
err: "with error",
},
}

for _, testcase := range testcases {
testcase := testcase

t.Run(testcase.description, func(t *testing.T) {
for key, value := range testcase.env {
t.Setenv(key, value)
}

// Reset the default config to load the new environment variables.
cfg := konf.New()
// Ignore error: env loader does not return error.
_ = cfg.Load(env.New())
konf.SetDefault(cfg)

cfg, err := config.New(testcase.opts...)
if testcase.err == "" {
assert.NoError(t, err)
assert.Equal(t, testcase.explanation, cfg.Explain(testcase.key))
} else {
assert.EqualError(t, err, testcase.err)
}
})
}
}
55 changes: 55 additions & 0 deletions config/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2024 The nilgo authors
// Use of this source code is governed by a MIT license found in the LICENSE file.

package config

import (
"io/fs"

"github.com/nil-go/konf"
)

// WithFile explicitly provides the config file paths,
// and each file takes precedence over the files before it.
//
// By default, it uses "config/config.yaml".
// It can be overridden by the environment variable "CONFIG_FILE".
func WithFile(files ...string) Option {
return func(options *options) {
options.files = append(options.files, files...)
}
}

// WithFS provides the fs.FS to load the config files from.
//
// By default, it uses OS file system under the current directory.
func WithFS(fs fs.FS) Option {
return func(options *options) {
options.fs = fs
}
}

// WithOption provides the konf.Option to customize the config.
func WithOption(opts ...konf.Option) Option {
return func(options *options) {
options.opts = append(options.opts, opts...)
}
}

// With allows to customize the config with the given function.
func With(fn func(config *konf.Config) error) Option {
return func(options *options) {
options.fn = fn
}
}

type (
// Option configures the config with specific options.
Option func(*options)
options struct {
opts []konf.Option
files []string
fs fs.FS
fn func(*konf.Config) error
}
)
3 changes: 3 additions & 0 deletions config/testdata/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nilgo:
source: file
stage: dev
2 changes: 2 additions & 0 deletions config/testdata/staging.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nilgo:
stage: staging
3 changes: 3 additions & 0 deletions gcp/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/nil-go/nilgo/gcp

go 1.21
4 changes: 4 additions & 0 deletions gcp/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 The nilgo authors
// Use of this source code is governed by a MIT license found in the LICENSE file.

package gcp
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
module github.com/nil-go/nilgo

go 1.21

require (
github.com/nil-go/konf v1.1.0
github.com/nil-go/sloth v0.3.0
github.com/nil-go/sloth/otel v0.3.0
go.opentelemetry.io/otel/trace v1.26.0
gopkg.in/yaml.v3 v3.0.1
)

require go.opentelemetry.io/otel v1.26.0 // indirect
Loading

0 comments on commit 37d5caa

Please sign in to comment.