...

Source file src/github.com/golang-migrate/migrate/v4/source/gitlab/gitlab.go

Documentation: github.com/golang-migrate/migrate/v4/source/gitlab

     1  package gitlab
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"net/http"
     9  	nurl "net/url"
    10  	"os"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  import (
    16  	"github.com/golang-migrate/migrate/v4/source"
    17  	"github.com/xanzy/go-gitlab"
    18  )
    19  
    20  func init() {
    21  	source.Register("gitlab", &Gitlab{})
    22  }
    23  
    24  const DefaultMaxItemsPerPage = 100
    25  
    26  var (
    27  	ErrNoUserInfo       = fmt.Errorf("no username:token provided")
    28  	ErrNoAccessToken    = fmt.Errorf("no access token")
    29  	ErrInvalidHost      = fmt.Errorf("invalid host")
    30  	ErrInvalidProjectID = fmt.Errorf("invalid project id")
    31  	ErrInvalidResponse  = fmt.Errorf("invalid response")
    32  )
    33  
    34  type Gitlab struct {
    35  	client *gitlab.Client
    36  	url    string
    37  
    38  	projectID   string
    39  	path        string
    40  	listOptions *gitlab.ListTreeOptions
    41  	getOptions  *gitlab.GetFileOptions
    42  	migrations  *source.Migrations
    43  }
    44  
    45  type Config struct {
    46  }
    47  
    48  func (g *Gitlab) Open(url string) (source.Driver, error) {
    49  	u, err := nurl.Parse(url)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if u.User == nil {
    55  		return nil, ErrNoUserInfo
    56  	}
    57  
    58  	password, ok := u.User.Password()
    59  	if !ok {
    60  		return nil, ErrNoAccessToken
    61  	}
    62  
    63  	gn := &Gitlab{
    64  		client:     gitlab.NewClient(nil, password),
    65  		url:        url,
    66  		migrations: source.NewMigrations(),
    67  	}
    68  
    69  	if u.Host != "" {
    70  		uri := nurl.URL{
    71  			Scheme: "https",
    72  			Host:   u.Host,
    73  		}
    74  
    75  		err = gn.client.SetBaseURL(uri.String())
    76  		if err != nil {
    77  			return nil, ErrInvalidHost
    78  		}
    79  	}
    80  
    81  	pe := strings.Split(strings.Trim(u.Path, "/"), "/")
    82  	if len(pe) < 1 {
    83  		return nil, ErrInvalidProjectID
    84  	}
    85  	gn.projectID = pe[0]
    86  	if len(pe) > 1 {
    87  		gn.path = strings.Join(pe[1:], "/")
    88  	}
    89  
    90  	gn.listOptions = &gitlab.ListTreeOptions{
    91  		Path: &gn.path,
    92  		Ref:  &u.Fragment,
    93  		ListOptions: gitlab.ListOptions{
    94  			PerPage: DefaultMaxItemsPerPage,
    95  		},
    96  	}
    97  
    98  	gn.getOptions = &gitlab.GetFileOptions{
    99  		Ref: &u.Fragment,
   100  	}
   101  
   102  	if err := gn.readDirectory(); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	return gn, nil
   107  }
   108  
   109  func WithInstance(client *gitlab.Client, config *Config) (source.Driver, error) {
   110  	gn := &Gitlab{
   111  		client:     client,
   112  		migrations: source.NewMigrations(),
   113  	}
   114  	if err := gn.readDirectory(); err != nil {
   115  		return nil, err
   116  	}
   117  	return gn, nil
   118  }
   119  
   120  func (g *Gitlab) readDirectory() error {
   121  	var nodes []*gitlab.TreeNode
   122  	for {
   123  		n, response, err := g.client.Repositories.ListTree(g.projectID, g.listOptions)
   124  		if err != nil {
   125  			return err
   126  		}
   127  
   128  		if response.StatusCode != http.StatusOK {
   129  			return ErrInvalidResponse
   130  		}
   131  
   132  		nodes = append(nodes, n...)
   133  		if response.CurrentPage >= response.TotalPages {
   134  			break
   135  		}
   136  		g.listOptions.ListOptions.Page = response.NextPage
   137  	}
   138  
   139  	for i := range nodes {
   140  		m, err := g.nodeToMigration(nodes[i])
   141  		if err != nil {
   142  			continue
   143  		}
   144  
   145  		if !g.migrations.Append(m) {
   146  			return fmt.Errorf("unable to parse file %v", nodes[i].Name)
   147  		}
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func (g *Gitlab) nodeToMigration(node *gitlab.TreeNode) (*source.Migration, error) {
   154  	m := source.Regex.FindStringSubmatch(node.Name)
   155  	if len(m) == 5 {
   156  		versionUint64, err := strconv.ParseUint(m[1], 10, 64)
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  		return &source.Migration{
   161  			Version:    uint(versionUint64),
   162  			Identifier: m[2],
   163  			Direction:  source.Direction(m[3]),
   164  			Raw:        g.path + "/" + node.Name,
   165  		}, nil
   166  	}
   167  	return nil, source.ErrParse
   168  }
   169  
   170  func (g *Gitlab) Close() error {
   171  	return nil
   172  }
   173  
   174  func (g *Gitlab) First() (version uint, er error) {
   175  	if v, ok := g.migrations.First(); !ok {
   176  		return 0, &os.PathError{Op: "first", Path: g.path, Err: os.ErrNotExist}
   177  	} else {
   178  		return v, nil
   179  	}
   180  }
   181  
   182  func (g *Gitlab) Prev(version uint) (prevVersion uint, err error) {
   183  	if v, ok := g.migrations.Prev(version); !ok {
   184  		return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: g.path, Err: os.ErrNotExist}
   185  	} else {
   186  		return v, nil
   187  	}
   188  }
   189  
   190  func (g *Gitlab) Next(version uint) (nextVersion uint, err error) {
   191  	if v, ok := g.migrations.Next(version); !ok {
   192  		return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: g.path, Err: os.ErrNotExist}
   193  	} else {
   194  		return v, nil
   195  	}
   196  }
   197  
   198  func (g *Gitlab) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
   199  	if m, ok := g.migrations.Up(version); ok {
   200  		f, response, err := g.client.RepositoryFiles.GetFile(g.projectID, m.Raw, g.getOptions)
   201  		if err != nil {
   202  			return nil, "", err
   203  		}
   204  
   205  		if response.StatusCode != http.StatusOK {
   206  			return nil, "", ErrInvalidResponse
   207  		}
   208  
   209  		content, err := base64.StdEncoding.DecodeString(f.Content)
   210  		if err != nil {
   211  			return nil, "", err
   212  		}
   213  
   214  		return ioutil.NopCloser(strings.NewReader(string(content))), m.Identifier, nil
   215  	}
   216  
   217  	return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.path, Err: os.ErrNotExist}
   218  }
   219  
   220  func (g *Gitlab) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
   221  	if m, ok := g.migrations.Down(version); ok {
   222  		f, response, err := g.client.RepositoryFiles.GetFile(g.projectID, m.Raw, g.getOptions)
   223  		if err != nil {
   224  			return nil, "", err
   225  		}
   226  
   227  		if response.StatusCode != http.StatusOK {
   228  			return nil, "", ErrInvalidResponse
   229  		}
   230  
   231  		content, err := base64.StdEncoding.DecodeString(f.Content)
   232  		if err != nil {
   233  			return nil, "", err
   234  		}
   235  
   236  		return ioutil.NopCloser(strings.NewReader(string(content))), m.Identifier, nil
   237  	}
   238  
   239  	return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.path, Err: os.ErrNotExist}
   240  }
   241  

View as plain text