Skip to content

Commit 1b3fcf7

Browse files
committedJan 28, 2023
Generate migrations
1 parent af097e2 commit 1b3fcf7

10 files changed

+165
-23
lines changed
 

‎krab/action_gen_migration.go

+28-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/ohkrab/krab/cli"
8+
"github.com/ohkrab/krab/cliargs"
89
)
910

1011
// ActionGenMigration generates migration file.
@@ -26,16 +27,40 @@ func (a *ActionGenMigration) Synopsis() string {
2627

2728
// Run in CLI.
2829
func (a *ActionGenMigration) Run(args []string) int {
29-
resp, err := a.Cmd.Do(context.Background(), CmdOpts{Inputs{"args": args}})
30+
ui := a.Ui
31+
flags := cliargs.New(args)
32+
33+
for _, arg := range a.Cmd.Arguments().Args {
34+
flags.Add(arg.Name)
35+
}
36+
37+
err := flags.Parse()
38+
if err != nil {
39+
ui.Output(a.Help())
40+
ui.Error(err.Error())
41+
return 1
42+
}
43+
44+
resp, err := a.Cmd.Do(context.Background(), CmdOpts{Inputs: flags.Values()})
3045
if err != nil {
3146
a.Ui.Error(err.Error())
3247
return 1
3348
}
3449

3550
response := resp.(ResponseGenMigration)
3651

37-
a.Ui.Output(response.Path)
38-
a.Ui.Output(response.Ref)
52+
a.Ui.Output("File generated:")
53+
a.Ui.Info(response.Path)
54+
a.Ui.Output("Don't forget to add your migration to migration_set:")
55+
a.Ui.Output(`
56+
migration_set "public" {
57+
migrations = [
58+
...`)
59+
a.Ui.Info(fmt.Sprint(" ", response.Ref, ","))
60+
a.Ui.Output(` ...
61+
]
62+
}
63+
`)
3964

4065
return 0
4166
}

‎krab/arguments.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (a *Arguments) Validate(values Inputs) error {
7979
return err
8080
}
8181
} else {
82-
return fmt.Errorf("Argument value for `%s` is missing", a.Name)
82+
return fmt.Errorf("Argument value for `%s` (%s) is missing", a.Name, a.Description)
8383
}
8484
}
8585

‎krab/cmd_gen_migration.go

+36-8
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7-
"time"
7+
"path/filepath"
88

9+
"github.com/ohkrab/krab/krabenv"
910
"github.com/ohkrab/krab/krabhcl"
1011
"github.com/spf13/afero"
1112
)
1213

1314
// CmdGenMigration generates migation file.
1415
type CmdGenMigration struct {
1516
FS afero.Afero
17+
VersionGenerator
1618
}
1719

1820
// ResponseGenMigration json
@@ -21,6 +23,18 @@ type ResponseGenMigration struct {
2123
Ref string `json:"ref"`
2224
}
2325

26+
func (c *CmdGenMigration) Arguments() *Arguments {
27+
return &Arguments{
28+
Args: []*Argument{
29+
{
30+
Name: "name",
31+
Type: "string",
32+
Description: "Migration name",
33+
},
34+
},
35+
}
36+
}
37+
2438
func (c *CmdGenMigration) Addr() krabhcl.Addr { return krabhcl.NullAddr }
2539

2640
func (c *CmdGenMigration) Name() []string {
@@ -30,18 +44,32 @@ func (c *CmdGenMigration) Name() []string {
3044
func (c *CmdGenMigration) HttpMethod() string { return "" }
3145

3246
func (c *CmdGenMigration) Do(ctx context.Context, o CmdOpts) (interface{}, error) {
33-
34-
return c.run(ctx)
47+
err := c.Arguments().Validate(o.Inputs)
48+
if err != nil {
49+
return nil, err
50+
}
51+
return c.run(ctx, o.Inputs)
3552
}
3653

37-
func (c *CmdGenMigration) run(ctx context.Context) (ResponseGenMigration, error) {
54+
func (c *CmdGenMigration) run(ctx context.Context, inputs Inputs) (ResponseGenMigration, error) {
3855
result := ResponseGenMigration{}
39-
buf := bytes.Buffer{}
4056

41-
ref := "create_animals"
57+
dir, err := krabenv.ConfigDir()
58+
if err != nil {
59+
return result, err
60+
}
61+
dir = filepath.Join(dir, "db", "migrations")
62+
err = c.FS.MkdirAll(dir, 0755)
63+
if err != nil {
64+
return result, err
65+
}
66+
67+
version := c.VersionGenerator.Next()
68+
ref := inputs["name"].(string)
4269
result.Ref = fmt.Sprint("migration.", ref)
43-
version := time.Now().UTC().Format("20060102_150405") // YYYYMMDD_HHMMSS
70+
result.Path = filepath.Join(dir, fmt.Sprint(version, "_", ref, krabenv.Ext()))
4471

72+
buf := bytes.Buffer{}
4573
buf.WriteString(`migration "`)
4674
buf.WriteString(ref)
4775
buf.WriteString(`" {`)
@@ -61,7 +89,7 @@ func (c *CmdGenMigration) run(ctx context.Context) (ResponseGenMigration, error)
6189
buf.WriteString(`}`)
6290
buf.WriteString("\n")
6391

64-
c.FS.WriteFile("/tmp/migrate.krab.hcl", buf.Bytes(), 0644)
92+
c.FS.WriteFile(result.Path, buf.Bytes(), 0644)
6593

6694
return result, nil
6795
}

‎krab/cmd_registry.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import (
99
// CmdRegistry is a list of registred commands.
1010
type CmdRegistry struct {
1111
Commands []Cmd
12+
13+
FS afero.Afero
14+
VersionGenerator
1215
}
1316

1417
// Register appends new command to registry.
@@ -17,10 +20,10 @@ func (r *CmdRegistry) Register(c Cmd) {
1720
}
1821

1922
// RegisterAll registers all commands in the registry.
20-
func (r *CmdRegistry) RegisterAll(config *Config, fs afero.Afero, conn krabdb.Connection) {
23+
func (r *CmdRegistry) RegisterAll(config *Config, conn krabdb.Connection) {
2124
r.Register(&CmdVersion{})
2225

23-
r.Register(&CmdGenMigration{FS: fs})
26+
r.Register(&CmdGenMigration{FS: r.FS, VersionGenerator: r.VersionGenerator})
2427

2528
for _, action := range config.Actions {
2629
action := action

‎krab/parser.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/hashicorp/hcl/v2"
99
"github.com/hashicorp/hcl/v2/hclsyntax"
1010
"github.com/hashicorp/hcl2/hclparse"
11+
"github.com/ohkrab/krab/krabenv"
1112
"github.com/ohkrab/krab/krabfn"
1213
"github.com/spf13/afero"
1314
)
@@ -112,15 +113,15 @@ func (p *Parser) dirFiles(dir string) ([]string, error) {
112113
}
113114

114115
func fileExt(path string) string {
115-
if strings.HasSuffix(path, ".krab.hcl") {
116-
return ".krab.hcl"
116+
if strings.HasSuffix(path, krabenv.Ext()) {
117+
return krabenv.Ext()
117118
}
118119

119-
return "" // unrecognized extension
120+
return "" // unrecognized
120121
}
121122

122123
func isIgnoredFile(name string) bool {
123-
return strings.HasPrefix(name, ".") || // Unix-like hidden files
124-
strings.HasSuffix(name, "~") || // vim
124+
return strings.HasPrefix(name, ".") || // dotfiles
125+
strings.HasSuffix(name, "~") || // vim/backups
125126
strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
126127
}

‎krab/version_generator.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package krab
2+
3+
import "time"
4+
5+
type VersionGenerator interface {
6+
Next() string
7+
}
8+
9+
type TimestampVersionGenerator struct{}
10+
11+
func (g *TimestampVersionGenerator) Next() string {
12+
version := time.Now().UTC().Format("20060102_150405") // YYYYMMDD_HHMMSS
13+
return version
14+
}

‎krabenv/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ func Env() string {
2121
func Test() bool {
2222
return Env() == "test"
2323
}
24+
25+
func Ext() string {
26+
return ".krab.hcl"
27+
}

‎main.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ func main() {
3131

3232
conn := &krabdb.DefaultConnection{}
3333

34-
registry := &krab.CmdRegistry{Commands: []krab.Cmd{}}
35-
registry.RegisterAll(config, parser.FS, conn)
34+
registry := &krab.CmdRegistry{
35+
Commands: []krab.Cmd{},
36+
FS: parser.FS,
37+
VersionGenerator: &krab.TimestampVersionGenerator{},
38+
}
39+
registry.RegisterAll(config, conn)
3640
// agent := krabapi.Agent{Registry: registry}
3741
// agent.Run()
3842

‎spec/action_gen_migration_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package spec
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestActionGenMigration(t *testing.T) {
11+
c := mockCli(mockConfig(``))
12+
defer c.Teardown()
13+
c.AssertSuccessfulRun(t, []string{"gen", "migration", "-name", "create_maps"})
14+
c.AssertOutputContains(t, "migration.create_maps")
15+
files := c.FSFiles()
16+
assert.Len(t, files, 1)
17+
for k, b := range files {
18+
expected := `migration "create_maps" {
19+
version = "20230101"
20+
21+
up {
22+
}
23+
24+
down {
25+
}
26+
}`
27+
ok, err := c.fs.FileContainsBytes(k, []byte(expected))
28+
assert.NoError(t, err)
29+
if !ok {
30+
fmt.Println("Expected:", expected)
31+
fmt.Println("Current:", string(b))
32+
assert.FailNow(t, "Output file does not contain valid data")
33+
}
34+
}
35+
}

‎spec/helpers_test.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7+
"io/fs"
78
"strings"
89
"testing"
910

@@ -40,9 +41,15 @@ func (m *cliMock) setup(args []string) {
4041
m.helpWriter = bytes.Buffer{}
4142
m.uiErrorWriter = bytes.Buffer{}
4243
m.uiWriter = bytes.Buffer{}
44+
memfs := afero.NewMemMapFs()
45+
m.fs = afero.Afero{Fs: memfs}
4346

44-
registry := &krab.CmdRegistry{Commands: []krab.Cmd{}}
45-
registry.RegisterAll(m.config, m.fs, m.connection)
47+
registry := &krab.CmdRegistry{
48+
Commands: []krab.Cmd{},
49+
FS: m.fs,
50+
VersionGenerator: &versionGeneratorMock{},
51+
}
52+
registry.RegisterAll(m.config, m.connection)
4653

4754
m.app = krabcli.New(
4855
cli.New(&m.uiErrorWriter, &m.uiWriter),
@@ -201,6 +208,21 @@ func (m *cliMock) AssertSQLContains(t *testing.T, expected string) bool {
201208
return assert.True(t, found != -1, fmt.Sprintf("SQL mismatch:\n%s\nwith:\n%s", expected, sql))
202209
}
203210

211+
func (m *cliMock) FSFiles() map[string][]byte {
212+
data := map[string][]byte{}
213+
m.fs.Walk("/", func(path string, info fs.FileInfo, err error) error {
214+
if !info.IsDir() {
215+
b, err := m.fs.ReadFile(path)
216+
if err != nil {
217+
panic(err)
218+
}
219+
data[path] = b
220+
}
221+
return nil
222+
})
223+
return data
224+
}
225+
204226
func (m *cliMock) ResetSQLRecorder() {
205227
m.connection.assertedSQLIndex = 0
206228
m.connection.recorder = []string{}
@@ -240,6 +262,12 @@ func (m *cliMock) Insert(t *testing.T, table string, cols string, vals string) b
240262
return assert.NoError(t, err, "Insertion must happen")
241263
}
242264

265+
type versionGeneratorMock struct{}
266+
267+
func (g *versionGeneratorMock) Next() string {
268+
return "20230101"
269+
}
270+
243271
func mockCli(config *krab.Config) *cliMock {
244272
mock := &cliMock{
245273
config: config,

0 commit comments

Comments
 (0)
Please sign in to comment.