github.com/solongordon/pop@v4.10.0+incompatible/file_migrator.go (about) 1 package pop 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 "text/template" 12 13 "github.com/gobuffalo/fizz" 14 "github.com/gobuffalo/pop/fix" 15 "github.com/gobuffalo/pop/logging" 16 "github.com/pkg/errors" 17 ) 18 19 // FileMigrator is a migrator for SQL and Fizz 20 // files on disk at a specified path. 21 type FileMigrator struct { 22 Migrator 23 Path string 24 } 25 26 // NewFileMigrator for a path and a Connection 27 func NewFileMigrator(path string, c *Connection) (FileMigrator, error) { 28 fm := FileMigrator{ 29 Migrator: NewMigrator(c), 30 Path: path, 31 } 32 fm.SchemaPath = path 33 34 err := fm.findMigrations() 35 if err != nil { 36 return fm, errors.WithStack(err) 37 } 38 39 return fm, nil 40 } 41 42 func (fm *FileMigrator) findMigrations() error { 43 dir := fm.Path 44 if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { 45 // directory doesn't exist 46 return nil 47 } 48 filepath.Walk(dir, func(p string, info os.FileInfo, err error) error { 49 if !info.IsDir() { 50 matches := mrx.FindAllStringSubmatch(info.Name(), -1) 51 if len(matches) == 0 { 52 return nil 53 } 54 m := matches[0] 55 var dbType string 56 if m[3] == "" { 57 dbType = "all" 58 } else { 59 dbType = m[3][1:] 60 if !DialectSupported(dbType) { 61 return fmt.Errorf("unsupported dialect %s", dbType) 62 } 63 } 64 mf := Migration{ 65 Path: p, 66 Version: m[1], 67 Name: m[2], 68 DBType: dbType, 69 Direction: m[4], 70 Type: m[5], 71 Runner: func(mf Migration, tx *Connection) error { 72 f, err := os.Open(p) 73 if err != nil { 74 return errors.WithStack(err) 75 } 76 content, err := migrationContent(mf, tx, f) 77 if err != nil { 78 return errors.Wrapf(err, "error processing %s", mf.Path) 79 } 80 81 if content == "" { 82 return nil 83 } 84 85 err = tx.RawQuery(content).Exec() 86 if err != nil { 87 return errors.Wrapf(err, "error executing %s, sql: %s", mf.Path, content) 88 } 89 return nil 90 }, 91 } 92 fm.Migrations[mf.Direction] = append(fm.Migrations[mf.Direction], mf) 93 } 94 return nil 95 }) 96 return nil 97 } 98 99 func migrationContent(mf Migration, c *Connection, r io.Reader) (string, error) { 100 b, err := ioutil.ReadAll(r) 101 if err != nil { 102 return "", nil 103 } 104 105 content := string(b) 106 107 if mf.Type == "fizz" { 108 // test for && fix anko migrations 109 fixed, err := fix.Anko(content) 110 if err != nil { 111 return "", errors.Wrapf(err, "could not fizz the migration %s", mf.Path) 112 } 113 if strings.TrimSpace(fixed) != strings.TrimSpace(content) { 114 log(logging.Warn, "%s uses an old fizz syntax. please use\n%s", mf.Path, fixed) 115 } 116 content = fixed 117 } 118 119 t := template.Must(template.New("sql").Parse(content)) 120 var bb bytes.Buffer 121 err = t.Execute(&bb, c.Dialect.Details()) 122 if err != nil { 123 return "", errors.Wrapf(err, "could not execute migration template %s", mf.Path) 124 } 125 content = bb.String() 126 127 if mf.Type == "fizz" { 128 content, err = fizz.AString(content, c.Dialect.FizzTranslator()) 129 if err != nil { 130 return "", errors.Wrapf(err, "could not fizz the migration %s", mf.Path) 131 } 132 } 133 return content, nil 134 }