2017-02-08 06:01:29 +00:00
|
|
|
package github
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
nurl "net/url"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/google/go-github/github"
|
|
|
|
"github.com/mattes/migrate/source"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
source.Register("github", &Github{})
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrNoUserInfo = fmt.Errorf("no username:token provided")
|
|
|
|
ErrNoAccessToken = fmt.Errorf("no access token")
|
|
|
|
ErrInvalidRepo = fmt.Errorf("invalid repo")
|
|
|
|
ErrInvalidGithubClient = fmt.Errorf("expected *github.Client")
|
|
|
|
ErrNoDir = fmt.Errorf("no directory")
|
|
|
|
)
|
|
|
|
|
|
|
|
type Github struct {
|
|
|
|
client *github.Client
|
|
|
|
url string
|
|
|
|
|
|
|
|
pathOwner string
|
|
|
|
pathRepo string
|
|
|
|
path string
|
|
|
|
migrations *source.Migrations
|
|
|
|
}
|
|
|
|
|
2017-02-10 00:59:30 +00:00
|
|
|
type Config struct {
|
|
|
|
}
|
|
|
|
|
2017-02-08 06:01:29 +00:00
|
|
|
func (g *Github) Open(url string) (source.Driver, error) {
|
|
|
|
u, err := nurl.Parse(url)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if u.User == nil {
|
|
|
|
return nil, ErrNoUserInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
password, ok := u.User.Password()
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrNoUserInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
tr := &github.BasicAuthTransport{
|
|
|
|
Username: u.User.Username(),
|
|
|
|
Password: password,
|
|
|
|
}
|
|
|
|
|
|
|
|
gn := &Github{
|
|
|
|
client: github.NewClient(tr.Client()),
|
|
|
|
url: url,
|
|
|
|
migrations: source.NewMigrations(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// set owner, repo and path in repo
|
|
|
|
gn.pathOwner = u.Host
|
|
|
|
pe := strings.Split(strings.Trim(u.Path, "/"), "/")
|
|
|
|
if len(pe) < 1 {
|
|
|
|
return nil, ErrInvalidRepo
|
|
|
|
}
|
|
|
|
gn.pathRepo = pe[0]
|
|
|
|
if len(pe) > 1 {
|
|
|
|
gn.path = strings.Join(pe[1:], "/")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := gn.readDirectory(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return gn, nil
|
|
|
|
}
|
|
|
|
|
2017-02-10 00:59:30 +00:00
|
|
|
func WithInstance(client *github.Client, config *Config) (source.Driver, error) {
|
2017-02-08 06:01:29 +00:00
|
|
|
gn := &Github{
|
|
|
|
client: client,
|
|
|
|
migrations: source.NewMigrations(),
|
|
|
|
}
|
|
|
|
if err := gn.readDirectory(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return gn, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) readDirectory() error {
|
|
|
|
fileContent, dirContents, _, err := g.client.Repositories.GetContents(g.pathOwner, g.pathRepo, g.path, &github.RepositoryContentGetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if fileContent != nil {
|
|
|
|
return ErrNoDir
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, fi := range dirContents {
|
|
|
|
m, err := source.DefaultParse(*fi.Name)
|
|
|
|
if err != nil {
|
|
|
|
continue // ignore files that we can't parse
|
|
|
|
}
|
|
|
|
if !g.migrations.Append(m) {
|
|
|
|
return fmt.Errorf("unable to parse file %v", *fi.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) First() (version uint, er error) {
|
|
|
|
if v, ok := g.migrations.First(); !ok {
|
|
|
|
return 0, &os.PathError{"first", g.path, os.ErrNotExist}
|
|
|
|
} else {
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) Prev(version uint) (prevVersion uint, err error) {
|
|
|
|
if v, ok := g.migrations.Prev(version); !ok {
|
|
|
|
return 0, &os.PathError{fmt.Sprintf("prev for version %v", version), g.path, os.ErrNotExist}
|
|
|
|
} else {
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) Next(version uint) (nextVersion uint, err error) {
|
|
|
|
if v, ok := g.migrations.Next(version); !ok {
|
|
|
|
return 0, &os.PathError{fmt.Sprintf("next for version %v", version), g.path, os.ErrNotExist}
|
|
|
|
} else {
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
|
|
|
|
if m, ok := g.migrations.Up(version); ok {
|
|
|
|
file, _, _, err := g.client.Repositories.GetContents(g.pathOwner, g.pathRepo, path.Join(g.path, m.Raw), &github.RepositoryContentGetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
if file != nil {
|
|
|
|
r, err := file.Decode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
return ioutil.NopCloser(bytes.NewReader(r)), m.Identifier, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), g.path, os.ErrNotExist}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Github) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
|
|
|
|
if m, ok := g.migrations.Down(version); ok {
|
|
|
|
file, _, _, err := g.client.Repositories.GetContents(g.pathOwner, g.pathRepo, path.Join(g.path, m.Raw), &github.RepositoryContentGetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
if file != nil {
|
|
|
|
r, err := file.Decode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
return ioutil.NopCloser(bytes.NewReader(r)), m.Identifier, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, "", &os.PathError{fmt.Sprintf("read version %v", version), g.path, os.ErrNotExist}
|
|
|
|
}
|