gopkg.in/zensey/migrate.v3@v3.0.0/source/github/github.go (about) 1 package github 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "io/ioutil" 9 nurl "net/url" 10 "os" 11 "path" 12 "strings" 13 14 "github.com/google/go-github/github" 15 "github.com/mattes/migrate/source" 16 ) 17 18 func init() { 19 source.Register("github", &Github{}) 20 } 21 22 var ( 23 ErrNoUserInfo = fmt.Errorf("no username:token provided") 24 ErrNoAccessToken = fmt.Errorf("no access token") 25 ErrInvalidRepo = fmt.Errorf("invalid repo") 26 ErrInvalidGithubClient = fmt.Errorf("expected *github.Client") 27 ErrNoDir = fmt.Errorf("no directory") 28 ) 29 30 type Github struct { 31 client *github.Client 32 url string 33 34 pathOwner string 35 pathRepo string 36 path string 37 migrations *source.Migrations 38 } 39 40 type Config struct { 41 } 42 43 func (g *Github) Open(url string) (source.Driver, error) { 44 u, err := nurl.Parse(url) 45 if err != nil { 46 return nil, err 47 } 48 49 if u.User == nil { 50 return nil, ErrNoUserInfo 51 } 52 53 password, ok := u.User.Password() 54 if !ok { 55 return nil, ErrNoUserInfo 56 } 57 58 tr := &github.BasicAuthTransport{ 59 Username: u.User.Username(), 60 Password: password, 61 } 62 63 gn := &Github{ 64 client: github.NewClient(tr.Client()), 65 url: url, 66 migrations: source.NewMigrations(), 67 } 68 69 // set owner, repo and path in repo 70 gn.pathOwner = u.Host 71 pe := strings.Split(strings.Trim(u.Path, "/"), "/") 72 if len(pe) < 1 { 73 return nil, ErrInvalidRepo 74 } 75 gn.pathRepo = pe[0] 76 if len(pe) > 1 { 77 gn.path = strings.Join(pe[1:], "/") 78 } 79 80 if err := gn.readDirectory(); err != nil { 81 return nil, err 82 } 83 84 return gn, nil 85 } 86 87 func WithInstance(client *github.Client, config *Config) (source.Driver, error) { 88 gn := &Github{ 89 client: client, 90 migrations: source.NewMigrations(), 91 } 92 if err := gn.readDirectory(); err != nil { 93 return nil, err 94 } 95 return gn, nil 96 } 97 98 func (g *Github) readDirectory() error { 99 fileContent, dirContents, _, err := g.client.Repositories.GetContents(context.Background(), g.pathOwner, g.pathRepo, g.path, &github.RepositoryContentGetOptions{}) 100 if err != nil { 101 return err 102 } 103 if fileContent != nil { 104 return ErrNoDir 105 } 106 107 for _, fi := range dirContents { 108 m, err := source.DefaultParse(*fi.Name) 109 if err != nil { 110 continue // ignore files that we can't parse 111 } 112 if !g.migrations.Append(m) { 113 return fmt.Errorf("unable to parse file %v", *fi.Name) 114 } 115 } 116 117 return nil 118 } 119 120 func (g *Github) Close() error { 121 return nil 122 } 123 124 func (g *Github) First() (version uint, er error) { 125 if v, ok := g.migrations.First(); !ok { 126 return 0, &os.PathError{"first", g.path, os.ErrNotExist} 127 } else { 128 return v, nil 129 } 130 } 131 132 func (g *Github) Prev(version uint) (prevVersion uint, err error) { 133 if v, ok := g.migrations.Prev(version); !ok { 134 return 0, &os.PathError{fmt.Sprintf("prev for version %v", version), g.path, os.ErrNotExist} 135 } else { 136 return v, nil 137 } 138 } 139 140 func (g *Github) Next(version uint) (nextVersion uint, err error) { 141 if v, ok := g.migrations.Next(version); !ok { 142 return 0, &os.PathError{fmt.Sprintf("next for version %v", version), g.path, os.ErrNotExist} 143 } else { 144 return v, nil 145 } 146 } 147 148 func (g *Github) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { 149 if m, ok := g.migrations.Up(version); ok { 150 file, _, _, err := g.client.Repositories.GetContents(context.Background(), g.pathOwner, g.pathRepo, path.Join(g.path, m.Raw), &github.RepositoryContentGetOptions{}) 151 if err != nil { 152 return nil, "", err 153 } 154 if file != nil { 155 r, err := file.GetContent() 156 if err != nil { 157 return nil, "", err 158 } 159 return ioutil.NopCloser(bytes.NewReader([]byte(r))), m.Identifier, nil 160 } 161 } 162 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), g.path, os.ErrNotExist} 163 } 164 165 func (g *Github) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { 166 if m, ok := g.migrations.Down(version); ok { 167 file, _, _, err := g.client.Repositories.GetContents(context.Background(), g.pathOwner, g.pathRepo, path.Join(g.path, m.Raw), &github.RepositoryContentGetOptions{}) 168 if err != nil { 169 return nil, "", err 170 } 171 if file != nil { 172 r, err := file.GetContent() 173 if err != nil { 174 return nil, "", err 175 } 176 return ioutil.NopCloser(bytes.NewReader([]byte(r))), m.Identifier, nil 177 } 178 } 179 return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), g.path, os.ErrNotExist} 180 }