github.com/mmrath/gobase@v0.0.1/apps/db_migration/internal/dir_migrate.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 nurl "net/url" 8 "os" 9 "path" 10 "path/filepath" 11 "regexp" 12 "strconv" 13 14 "github.com/golang-migrate/migrate/v4/source" 15 ) 16 17 // Regex matches the following pattern: 18 // 123_name.up.ext 19 // 123_name.down.ext 20 var Regex = regexp.MustCompile(`^([0-9]+)_(.*)`) 21 22 // Parse returns Migration for matching Regex pattern. 23 func Parse(raw string) (*uint, *string, error) { 24 m := Regex.FindStringSubmatch(raw) 25 if len(m) == 3 { 26 versionUint64, err := strconv.ParseUint(m[1], 10, 64) 27 if err != nil { 28 return nil, nil, err 29 } 30 version := uint(versionUint64) 31 return &version, &m[2], nil 32 } 33 return nil, nil, source.ErrParse 34 } 35 36 func init() { 37 source.Register("dir", &File{}) 38 } 39 40 type File struct { 41 url string 42 path string 43 migrations *source.Migrations 44 } 45 46 func (f *File) Open(url string) (source.Driver, error) { 47 u, err := nurl.Parse(url) 48 if err != nil { 49 return nil, err 50 } 51 52 // concat host and path to restore full path 53 // host might be `.` 54 p := u.Host + u.Path 55 56 if len(p) == 0 { 57 // default to current directory if no path 58 wd, err := os.Getwd() 59 if err != nil { 60 return nil, err 61 } 62 p = wd 63 64 } else if p[0:1] == "." || p[0:1] != "/" { 65 // make path absolute if relative 66 abs, err := filepath.Abs(p) 67 if err != nil { 68 return nil, err 69 } 70 p = abs 71 } 72 73 // scan directory 74 files, err := ioutil.ReadDir(p) 75 if err != nil { 76 return nil, err 77 } 78 79 nf := &File{ 80 url: url, 81 path: p, 82 migrations: source.NewMigrations(), 83 } 84 85 for _, fi := range files { 86 if fi.IsDir() { 87 v, id, err := Parse(fi.Name()) 88 if err != nil { 89 continue // ignore files that we can't parse 90 } 91 92 //Up file exists 93 up := &source.Migration{ 94 Version: *v, 95 Identifier: *id, 96 Direction: source.Up, 97 Raw: filepath.Join(fi.Name(), "up.sql"), 98 } 99 100 down := &source.Migration{ 101 Version: *v, 102 Identifier: *id, 103 Direction: source.Down, 104 Raw: filepath.Join(fi.Name(), "down.sql"), 105 } 106 107 _, err = nf.appendMigration(up) 108 if err != nil { 109 return nil, err 110 } 111 _, err = nf.appendMigration(down) 112 if err != nil { 113 return nil, err 114 } 115 116 } 117 } 118 return nf, nil 119 } 120 121 func (f *File) appendMigration(m *source.Migration) (bool, error) { 122 if stat, err := os.Stat(path.Join(f.path, m.Raw)); err == nil && !stat.IsDir() { 123 if !f.migrations.Append(m) { 124 return false, fmt.Errorf("unable to parse file %v", m) 125 } 126 } 127 return true, nil 128 } 129 130 func (f *File) Close() error { 131 // nothing do to here 132 return nil 133 } 134 135 func (f *File) First() (version uint, err error) { 136 if v, ok := f.migrations.First(); !ok { 137 return 0, &os.PathError{Op: "first", Path: f.path, Err: os.ErrNotExist} 138 } else { 139 return v, nil 140 } 141 } 142 143 func (f *File) Prev(version uint) (prevVersion uint, err error) { 144 if v, ok := f.migrations.Prev(version); !ok { 145 return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: f.path, Err: os.ErrNotExist} 146 } else { 147 return v, nil 148 } 149 } 150 151 func (f *File) Next(version uint) (nextVersion uint, err error) { 152 if v, ok := f.migrations.Next(version); !ok { 153 return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: f.path, Err: os.ErrNotExist} 154 } else { 155 return v, nil 156 } 157 } 158 159 func (f *File) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { 160 if m, ok := f.migrations.Up(version); ok { 161 r, err := os.Open(path.Join(f.path, m.Raw)) 162 if err != nil { 163 return nil, "", err 164 } 165 return r, m.Identifier, nil 166 } 167 return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: f.path, Err: os.ErrNotExist} 168 } 169 170 func (f *File) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { 171 if m, ok := f.migrations.Down(version); ok { 172 r, err := os.Open(path.Join(f.path, m.Raw)) 173 if err != nil { 174 return nil, "", err 175 } 176 return r, m.Identifier, nil 177 } 178 return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: f.path, Err: os.ErrNotExist} 179 }