diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f689c0e..e33d6d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: go-version: ^1.17 id: go - name: check out - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Cache uses: actions/cache@v2.1.0 @@ -26,7 +26,7 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: build run: go build ./... @@ -34,7 +34,7 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: test run: go test ./... -v @@ -42,7 +42,7 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/README.md b/README.md index da91ee6..faf3b0d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This command has a fzf-like UI that allows you to find and run the file version used by the [hasura cli command](https://hasura.io/docs/latest/graphql/core/hasura-cli/index.html). - + ## install diff --git a/cmd/hasura/hasura.go b/cmd/hasura/hasura.go index 3389712..9526170 100644 --- a/cmd/hasura/hasura.go +++ b/cmd/hasura/hasura.go @@ -1,9 +1,10 @@ package hasura import ( + "bufio" "errors" "fmt" - "io/ioutil" + "os" "os/exec" "regexp" "strconv" @@ -20,23 +21,28 @@ const ( var versionRegex *regexp.Regexp -type Cmd struct { - called string - command []string - fileNames []string - options map[string]interface{} - target string +type HasuraCmd struct { + called string + command []string + options map[string]interface{} + files []fileInfo + applyTarget string } -func NewHasuraCmd(called string, options map[string]interface{}) *Cmd { +type fileInfo struct { + name string + headline string +} + +func NewHasuraCmd(called string, options map[string]interface{}) *HasuraCmd { if called == calledMigrateApply || called == calledMigrateDelete { setRegex() } - return &Cmd{called: called, options: options} + return &HasuraCmd{called: called, options: options} } -func (h *Cmd) Run() (string, error) { +func (h *HasuraCmd) Run() (string, error) { if err := h.setFileNames(); err != nil { return "", err } @@ -48,7 +54,7 @@ func (h *Cmd) Run() (string, error) { return h.setCommand().exec() } -func (h *Cmd) exec() (string, error) { +func (h *HasuraCmd) exec() (string, error) { fmt.Println("running... ", "hasura", strings.Join(h.command, " ")) fmt.Println("") @@ -57,15 +63,14 @@ func (h *Cmd) exec() (string, error) { return string(r), err } -func (h *Cmd) setCommand() *Cmd { +func (h *HasuraCmd) setCommand() *HasuraCmd { switch h.called { case CalledSeedApply: - h.command = append(strings.Split(h.called, " "), []string{"--file", h.target}...) + h.command = append(strings.Split(h.called, " "), []string{"--file", h.applyTarget}...) case calledMigrateApply, calledMigrateDelete: - h.command = append(strings.Split(h.called, " "), []string{"--version", h.target}...) + h.command = append(strings.Split(h.called, " "), []string{"--version", h.applyTarget}...) } - - if h.target == "" { + if h.applyTarget == "" { h.command = strings.Split(h.called, " ") } @@ -83,8 +88,8 @@ func (h *Cmd) setCommand() *Cmd { return h } -func (h *Cmd) setTarget() error { - if len(h.fileNames) == 0 { +func (h *HasuraCmd) setTarget() error { + if len(h.files) == 0 { return nil } @@ -94,15 +99,15 @@ func (h *Cmd) setTarget() error { } if h.called == calledMigrateApply || h.called == calledMigrateDelete { - h.target = trimVersion(fileName) + h.applyTarget = trimVersion(fileName) } else { - h.target = fileName + h.applyTarget = fileName } return nil } -func (h *Cmd) setFileNames() error { +func (h *HasuraCmd) setFileNames() error { var filePath string switch h.called { @@ -114,47 +119,57 @@ func (h *Cmd) setFileNames() error { return nil } - files, err := ioutil.ReadDir(filePath) + files, err := os.ReadDir(filePath) if err != nil { return err } if len(files) == 0 { - return errors.New("no file") + return errors.New("no such file or directory") } for _, file := range files { if file.IsDir() { if h.called == calledMigrateApply || h.called == calledMigrateDelete { - h.fileNames = append(h.fileNames, file.Name()) + headline, err := readFileHeadline("./migrations/default/" + file.Name() + "/up.sql") + if err != nil { + return err + } + h.files = append(h.files, fileInfo{name: file.Name(), headline: headline}) } } if !file.IsDir() && h.called == CalledSeedApply { - h.fileNames = append(h.fileNames, file.Name()) + headline, err := readFileHeadline("./seeds/default/" + file.Name()) + if err != nil { + return err + } + h.files = append(h.files, fileInfo{name: file.Name(), headline: headline}) } } return nil } -func (h *Cmd) findOne() (string, error) { - type fileName struct { - name string - } - - fileNames := make([]fileName, 0, len(h.fileNames)) - - for _, f := range h.fileNames { - fileNames = append(fileNames, fileName{f}) - } +func (h *HasuraCmd) findOne() (string, error) { + i, err := fuzzyfinder.Find( + h.files, + func(i int) string { return h.files[i].name }, + fuzzyfinder.WithPreviewWindow(func(i, width, height int) string { + if i == -1 { + return "" + } + return fmt.Sprintf(`📝 Applied SQL file - i, err := fuzzyfinder.Find(fileNames, func(i int) string { return fileNames[i].name }) + %s + ... + `, h.files[i].headline) + }), + ) if err != nil { return "", err } - - return fileNames[i].name, nil + return h.files[i].name, nil } func trimVersion(fileName string) string { @@ -164,3 +179,26 @@ func trimVersion(fileName string) string { func setRegex() { versionRegex = regexp.MustCompile(`^[0-9]+`) } + +// readFileHeadline +// Opens a file in the path passed as an argument, reads the first three lines, and returns them as a string. +func readFileHeadline(path string) (string, error) { + var headline string + f, err := os.Open(path) + if err != nil { + return "", err + } + defer f.Close() + scanner := bufio.NewScanner(f) + + var count int + for scanner.Scan() { + if count == 4 { + headline += scanner.Text() + break + } + headline += fmt.Sprintln(scanner.Text()) + count++ + } + return headline, nil +} diff --git a/cmd/hasura/hasura_test.go b/cmd/hasura/hasura_test.go index fdf7901..555eff3 100644 --- a/cmd/hasura/hasura_test.go +++ b/cmd/hasura/hasura_test.go @@ -10,24 +10,24 @@ func TestCmd_setCommand(t *testing.T) { t.Parallel() type fields struct { - called string - command []string - fileNames []string - options map[string]interface{} - target string + called string + command []string + files []fileInfo + options map[string]interface{} + target string } tests := []struct { name string fields fields - want *Cmd + want *HasuraCmd }{ { name: "case: not set h.target", fields: fields{ called: "seed apply", }, - want: &Cmd{ + want: &HasuraCmd{ called: "seed apply", command: []string{"seed", "apply"}, }, @@ -38,10 +38,10 @@ func TestCmd_setCommand(t *testing.T) { called: "seed apply", target: "xxx.sql", }, - want: &Cmd{ - called: "seed apply", - target: "xxx.sql", - command: []string{"seed", "apply", "--file", "xxx.sql"}, + want: &HasuraCmd{ + called: "seed apply", + applyTarget: "xxx.sql", + command: []string{"seed", "apply", "--file", "xxx.sql"}, }, }, { @@ -53,10 +53,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "seed apply", - target: "xxx.sql", - command: []string{"seed", "apply", "--file", "xxx.sql", "--admin-secret", "secret"}, + want: &HasuraCmd{ + called: "seed apply", + applyTarget: "xxx.sql", + command: []string{"seed", "apply", "--file", "xxx.sql", "--admin-secret", "secret"}, options: map[string]interface{}{ "admin-secret": "secret", }, @@ -71,10 +71,10 @@ func TestCmd_setCommand(t *testing.T) { "no-color": false, }, }, - want: &Cmd{ - called: "seed apply", - target: "xxx.sql", - command: []string{"seed", "apply", "--file", "xxx.sql", "--no-color", "false"}, + want: &HasuraCmd{ + called: "seed apply", + applyTarget: "xxx.sql", + command: []string{"seed", "apply", "--file", "xxx.sql", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, }, @@ -90,10 +90,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "seed apply", - target: "xxx.sql", - command: []string{"seed", "apply", "--file", "xxx.sql", "--admin-secret", "secret", "--no-color", "false"}, + want: &HasuraCmd{ + called: "seed apply", + applyTarget: "xxx.sql", + command: []string{"seed", "apply", "--file", "xxx.sql", "--admin-secret", "secret", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, "admin-secret": "secret", @@ -106,9 +106,9 @@ func TestCmd_setCommand(t *testing.T) { called: "migrate", target: "xxx.sql", }, - want: &Cmd{ - called: "migrate", - target: "xxx.sql", + want: &HasuraCmd{ + called: "migrate", + applyTarget: "xxx.sql", }, }, { @@ -117,10 +117,10 @@ func TestCmd_setCommand(t *testing.T) { called: "migrate apply", target: "xxx", }, - want: &Cmd{ - called: "migrate apply", - target: "xxx", - command: []string{"migrate", "apply", "--version", "xxx"}, + want: &HasuraCmd{ + called: "migrate apply", + applyTarget: "xxx", + command: []string{"migrate", "apply", "--version", "xxx"}, }, }, { @@ -132,10 +132,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "migrate apply", - target: "xxx", - command: []string{"migrate", "apply", "--version", "xxx", "--admin-secret", "secret"}, + want: &HasuraCmd{ + called: "migrate apply", + applyTarget: "xxx", + command: []string{"migrate", "apply", "--version", "xxx", "--admin-secret", "secret"}, options: map[string]interface{}{ "admin-secret": "secret", }, @@ -150,10 +150,10 @@ func TestCmd_setCommand(t *testing.T) { "no-color": false, }, }, - want: &Cmd{ - called: "migrate apply", - target: "xxx", - command: []string{"migrate", "apply", "--version", "xxx", "--no-color", "false"}, + want: &HasuraCmd{ + called: "migrate apply", + applyTarget: "xxx", + command: []string{"migrate", "apply", "--version", "xxx", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, }, @@ -169,10 +169,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "migrate apply", - target: "xxx", - command: []string{"migrate", "apply", "--version", "xxx", "--admin-secret", "secret", "--no-color", "false"}, + want: &HasuraCmd{ + called: "migrate apply", + applyTarget: "xxx", + command: []string{"migrate", "apply", "--version", "xxx", "--admin-secret", "secret", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, "admin-secret": "secret", @@ -185,10 +185,10 @@ func TestCmd_setCommand(t *testing.T) { called: "migrate delete", target: "xxx", }, - want: &Cmd{ - called: "migrate delete", - target: "xxx", - command: []string{"migrate", "delete", "--version", "xxx"}, + want: &HasuraCmd{ + called: "migrate delete", + applyTarget: "xxx", + command: []string{"migrate", "delete", "--version", "xxx"}, }, }, { @@ -200,10 +200,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "migrate delete", - target: "xxx", - command: []string{"migrate", "delete", "--version", "xxx", "--admin-secret", "secret"}, + want: &HasuraCmd{ + called: "migrate delete", + applyTarget: "xxx", + command: []string{"migrate", "delete", "--version", "xxx", "--admin-secret", "secret"}, options: map[string]interface{}{ "admin-secret": "secret", }, @@ -218,10 +218,10 @@ func TestCmd_setCommand(t *testing.T) { "no-color": false, }, }, - want: &Cmd{ - called: "migrate delete", - target: "xxx", - command: []string{"migrate", "delete", "--version", "xxx", "--no-color", "false"}, + want: &HasuraCmd{ + called: "migrate delete", + applyTarget: "xxx", + command: []string{"migrate", "delete", "--version", "xxx", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, }, @@ -237,10 +237,10 @@ func TestCmd_setCommand(t *testing.T) { "admin-secret": "secret", }, }, - want: &Cmd{ - called: "migrate delete", - target: "xxx", - command: []string{"migrate", "delete", "--version", "xxx", "--admin-secret", "secret", "--no-color", "false"}, + want: &HasuraCmd{ + called: "migrate delete", + applyTarget: "xxx", + command: []string{"migrate", "delete", "--version", "xxx", "--admin-secret", "secret", "--no-color", "false"}, options: map[string]interface{}{ "no-color": false, "admin-secret": "secret", @@ -253,9 +253,9 @@ func TestCmd_setCommand(t *testing.T) { called: "hoge", target: "xxx", }, - want: &Cmd{ - called: "hoge", - target: "xxx", + want: &HasuraCmd{ + called: "hoge", + applyTarget: "xxx", }, }, } @@ -263,30 +263,28 @@ func TestCmd_setCommand(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - h := &Cmd{ - called: tt.fields.called, - command: tt.fields.command, - fileNames: tt.fields.fileNames, - options: tt.fields.options, - target: tt.fields.target, + h := &HasuraCmd{ + called: tt.fields.called, + command: tt.fields.command, + files: tt.fields.files, + options: tt.fields.options, + applyTarget: tt.fields.target, } got := h.setCommand() sort.Strings(got.command) sort.Strings(tt.want.command) - sort.Strings(got.fileNames) - sort.Strings(tt.want.fileNames) if !reflect.DeepEqual(got.called, tt.want.called) { t.Errorf("Cmd.setCommand() = %v, want.called %v", got.called, tt.want.called) } - if !reflect.DeepEqual(got.target, tt.want.target) { - t.Errorf("Cmd.setCommand() = %v, want.target %v", got.target, tt.want.target) + if !reflect.DeepEqual(got.applyTarget, tt.want.applyTarget) { + t.Errorf("HasuraCmd.setCommand() = %v, want.target %v", got.applyTarget, tt.want.applyTarget) } if !reflect.DeepEqual(got.command, tt.want.command) { t.Errorf("Cmd.setCommand() = %v, want.command %v", got.command, tt.want.command) } - if !reflect.DeepEqual(got.fileNames, tt.want.fileNames) { - t.Errorf("Cmd.setCommand() = %v, want.fileNames %v", got.fileNames, tt.want.fileNames) + if !reflect.DeepEqual(got.files, tt.want.files) { + t.Errorf("HasuraCmd.setCommand() = %v, want.files %v", got.files, tt.want.files) } if !reflect.DeepEqual(got.options, tt.want.options) { for _, set := range tt.want.options {