Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#10: apply cobra template for pretty print #92

Merged
merged 2 commits into from
Apr 13, 2024
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@
go.work
.idea
gob
*.sql
11.json
target
62 changes: 31 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ flowchart TD
gob.yaml --> plugin2
gob.yaml --> plugin3
```
You just need to tell `gob` 3W(where,when and what)
You just need to tell `gbc` 3W(where,when and what)

1. **Where** : where to download the tool
2. **When** : when to execute to command
Expand All @@ -59,40 +59,40 @@ You just need to tell `gob` 3W(where,when and what)
## Quick Start
1. Install `gob` with below command
```shell
go install github.com/kcmvp/gob
go install github.com/kcmvp/gbc
```
2. Initialize project with below command(in the project home directory)
```shell
gob init
gbc init
```

| Make some changes and comit code | execute `gob deps` |
| Make some changes and comit code | execute `gbc deps` |
|--------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| <img src="https://github.com/kcmvp/gob/blob/main/docs/commit_hook.gif" height="245" width="400"> | <img src="https://github.com/kcmvp/gob/blob/main/docs/dependency_tree.png" height="245" width="300"> |
| <img src="https://github.com/kcmvp/gbc/blob/main/docs/commit_hook.gif" height="245" width="400"> | <img src="https://github.com/kcmvp/gbc/blob/main/docs/dependency_tree.png" height="245" width="300"> |


## Commands

Build Commands
- [gob init](#gob-init)
- [gob build](#gob-build)
- [gob clean](#gob-clean)
- [gob test](#gob-test)
- [gob lint](#gob-lint)
- [gob deps](#gob-deps)
- [gbc init](#gbc-init)
- [gbc build](#gbc-build)
- [gbc clean](#gbc-clean)
- [gbc test](#gbc-test)
- [gbc lint](#gbc-lint)
- [gbc deps](#gbc-deps)

Plugin Commands
- [gob plugin install](#gob-plugin-install)
- [gob plugin list](#gob-plugin-list)
- [gbc plugin install](#gbc-plugin-install)
- [gbc plugin list](#gbc-plugin-list)

Setup Commands
- [gob setup version](#gob-setup-version)
- [gbc setup version](#gbc-setup-version)

### gob init
### gbc init
```shell
gob init
gbc init
```
Initialize gob for the project, it will do following initializations
Initialize gbc for the project, it will do following initializations
1. generate file `gob.yaml`
2. generate file `.golangci.yaml`, which is the configuration for [golangci-lint](https://github.com/golangci/golangci-lint)
3. setup `git hooks` if project in the source control.
Expand All @@ -113,49 +113,49 @@ exec:
- test
plugins:
golangci-lint:
alias: lint #When : when issue `gob lint`
alias: lint #When : when issue `gbc lint`
args: run ./... #What: execute `golangci-lint run ./...`
url: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 #Where: where to download the plugin
gotestsum:
alias: test
args: --format testname -- -coverprofile=target/cover.out ./...
url: gotest.tools/gotestsum@v1.11.0
```
in most cases you don't need to edit the configuration manually. you can achieve this by [plugin commands](#gob-plugin-install)
in most cases you don't need to edit the configuration manually. you can achieve this by [plugin commands](#gbc-plugin-install)

### gob build
### gbc build
```shell
gob build
gbc build
```
This command would build all the candidate binaries(main methods in main packages) to the `target` folder.
1. Final binary name is same as go source file name which contains `main method`
2. Would fail if there are same name go main surce file

### gob clean
### gbc clean
```shell
gob clean
gbc clean
```
This command would clean `target` folder

### gob test
### gbc test
```shell
gob test
gbc test
```
This command would run all tests for the project and generate coverage report at `target/cover.html`

### gob lint
### gbc lint
```shell
gob lint
gbc lint
```
Run `golangci-lint` against project based on the configuration, a report named `target/lint.log` will be generated if there are any violations
### gob deps
### gbc deps
```shell
gob deps
gbc deps
```
List project dependencies tree and indicate there are updates for a specific dependency
### gob plugin install
### gbc plugin install
```shell
gob plugin install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 lint run ./...
gbc plugin install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 lint run ./...
```
It is an advanced version of `go install`, which supports multi-version.(eg:`golangci-lint-v1.55.2`, `golangci-lint-v1.55.1`)
1. Install the versioned tool(just the same as `go install`)
Expand Down
19 changes: 19 additions & 0 deletions application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# "postgres://username:password@localhost:5432/database_name"
# username:password@protocol(address)/dbname?param=value
datasource:
ds1:
driver: sqlite3
user: usera
password: passwd1
Host: localhost
url: file:test1.db?cache=shared&mode=memory
ds2:
driver: sqlite3
user: userb
password: passwd2
url: file:test2.db?cache=shared&mode=memory
scripts:
- sqlite3-schema.sql



12 changes: 12 additions & 0 deletions application_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# "postgres://username:password@localhost:5432/database_name"
# username:password@protocol(address)/dbname?param=value
datasource:
ds1:
driver: sqlite3
user: abc
password: 123
url: file:test3.db?cache=shared&mode=memory
scripts:
- sqlite3-schema.sql
- sqlite3-init.sql

57 changes: 57 additions & 0 deletions boot/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package boot

import (
"fmt"
"os"
"path/filepath"
"sync"

"github.com/kcmvp/gob/internal"
"github.com/kcmvp/gob/utils"

"github.com/samber/do/v2"
"github.com/spf13/viper"
)

const (
DefaultCfg = "application"
)

var (
cfg *viper.Viper
once sync.Once
)

func RootDir() string {
return internal.RootDir
}

func Container() *do.RootScope {
return internal.Container
}

func InitApp() {
InitAppWith(DefaultCfg)
}

func InitAppWith(cfgName string) {
if cfg == nil {
once.Do(func() {
cfg = viper.New()
cfg.SetConfigName(cfgName) // name of cfg file (without extension)
cfg.SetConfigType("yaml") // REQUIRED if the cfg file does not have the extension in the name
cfg.AddConfigPath(internal.RootDir) // optionally look for cfg in the working directory
if err := cfg.ReadInConfig(); err != nil { // Find and read the cfg file
panic(fmt.Errorf("fatal error cfg file: %w", err))
}
if test, _ := utils.TestCaller(); test {
if testCfg, err := os.Open(filepath.Join(internal.RootDir, fmt.Sprintf("%s_test.yaml", cfgName))); err == nil {
if err = cfg.MergeConfig(testCfg); err != nil {
panic(fmt.Errorf("failed to merge test configuration file: %w", err))
}
}
}
setupDb()
})
}
}
86 changes: 86 additions & 0 deletions boot/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package boot

import (
"database/sql"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/kcmvp/gob/internal"

//"github.com/kcmvp/gob/internal"

"github.com/samber/do/v2"
typetostring "github.com/samber/go-type-to-string"
"github.com/samber/lo"
)

const (
DSKey = "datasource"
UserKey = "${user}"
PasswordKey = "${password}"
HostKey = "${host}"
DefaultDS = "DefaultDS"
)

type dataSource struct {
Driver string `mapstructure:"driver"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Host string `mapstructure:"host"`
URL string `mapstructure:"url"`
Scripts []string `mapstructure:"scripts"`
}

func (ds dataSource) DSN() string {
dsn := strings.ReplaceAll(ds.URL, UserKey, ds.User)
dsn = strings.ReplaceAll(dsn, PasswordKey, ds.Password)
return strings.ReplaceAll(dsn, HostKey, ds.Host)
}

func dsMap() map[string]dataSource {
// single data source
if v := cfg.Get(fmt.Sprintf("%s.%s", DSKey, "driver")); v != nil {
var ds dataSource
if err := cfg.UnmarshalKey(DSKey, &ds); err != nil {
panic(fmt.Errorf("failed parse datasource: %w", err))
}
return map[string]dataSource{DefaultDS: ds}
// multiple data sources
} else if v = cfg.Get(DSKey); v != nil {
dss := v.(map[string]any)
return lo.MapValues(dss, func(_ any, key string) dataSource {
var ds dataSource
key = fmt.Sprintf("%s.%s", DSKey, key)
if err := cfg.UnmarshalKey(key, &ds); err != nil {
panic(fmt.Errorf("failed parse datasource: %w", err))
}
return ds
})
}
return map[string]dataSource{}
}

func setupDb() {
for name, ds := range dsMap() {
if db, err := sql.Open(ds.Driver, ds.DSN()); err == nil {
if err = db.Ping(); err != nil {
_ = db.Close()
panic(fmt.Errorf("failed to initialize %s: %w", name, err))
}
lo.ForEach(ds.Scripts, func(script string, _ int) {
if data, err := os.ReadFile(filepath.Join(internal.RootDir, script)); err == nil {
if _, err = db.Exec(string(data)); err != nil {
panic(fmt.Errorf("failed to execute %s: %w", script, err))
}
} else {
panic(fmt.Errorf("failed to read %s: %w", script, err))
}
})
do.ProvideNamedValue[*sql.DB](internal.Container, fmt.Sprintf("%s_%s", name, typetostring.GetType[*sql.DB]()), db)
} else {
panic(fmt.Errorf("failed to connect to datasource %s: %w", name, err))
}
}
}
42 changes: 42 additions & 0 deletions boot/db_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package boot

import (
"database/sql"
"fmt"
"github.com/kcmvp/gob/internal"
_ "github.com/mattn/go-sqlite3"
"github.com/samber/do/v2"
typetostring "github.com/samber/go-type-to-string"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"testing"
)

type DBTestSuite struct {
suite.Suite
}

func TestDBTestSuite(t *testing.T) {
suite.Run(t, &DBTestSuite{})
}

func (dbs *DBTestSuite) SetupSuite() {
InitApp()
}

func (dbs *DBTestSuite) TestMultipleDB() {
ds := dsMap()
assert.Equal(dbs.T(), 2, len(ds))
db := do.MustInvokeNamed[*sql.DB](internal.Container, fmt.Sprintf("%s_%s", "ds1", typetostring.GetType[*sql.DB]()))
assert.NotNil(dbs.T(), db)
rs, err := db.Exec("select * from Product")
assert.NoError(dbs.T(), err)
cnt, _ := rs.RowsAffected()
assert.Equal(dbs.T(), int64(2), cnt)
db = do.MustInvokeNamed[*sql.DB](internal.Container, fmt.Sprintf("%s_%s", "ds2", typetostring.GetType[*sql.DB]()))
assert.NotNil(dbs.T(), db)
rs, err = db.Exec("select * from Product")
assert.NoError(dbs.T(), err)
cnt, _ = rs.RowsAffected()
assert.Equal(dbs.T(), int64(0), cnt)
}
Loading
Loading