• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

golang-migrate / migrate / 5186663860

pending completion
5186663860

Pull #932

github

longit644
Add Golang function as a source of migration
Pull Request #932: Add Golang function as a source of migration

252 of 370 new or added lines in 32 files covered. (68.11%)

4 existing lines in 4 files now uncovered.

4214 of 7231 relevant lines covered (58.28%)

61.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.99
/source/gitlab/gitlab.go
1
package gitlab
2

3
import (
4
        "encoding/base64"
5
        "fmt"
6
        "io"
7
        "net/http"
8
        nurl "net/url"
9
        "os"
10
        "strconv"
11
        "strings"
12

13
        "github.com/golang-migrate/migrate/v4/source"
14
        "github.com/xanzy/go-gitlab"
15
)
16

17
func init() {
2✔
18
        source.Register("gitlab", &Gitlab{})
2✔
19
}
2✔
20

21
const DefaultMaxItemsPerPage = 100
22

23
var (
24
        ErrNoUserInfo       = fmt.Errorf("no username:token provided")
25
        ErrNoAccessToken    = fmt.Errorf("no access token")
26
        ErrInvalidHost      = fmt.Errorf("invalid host")
27
        ErrInvalidProjectID = fmt.Errorf("invalid project id")
28
        ErrInvalidResponse  = fmt.Errorf("invalid response")
29
)
30

31
type Gitlab struct {
32
        client *gitlab.Client
33
        url    string
34

35
        projectID   string
36
        path        string
37
        listOptions *gitlab.ListTreeOptions
38
        getOptions  *gitlab.GetFileOptions
39
        migrations  *source.Migrations
40
}
41

42
type Config struct {
43
}
44

45
func (g *Gitlab) Open(url string) (source.Driver, error) {
×
46
        u, err := nurl.Parse(url)
×
47
        if err != nil {
×
48
                return nil, err
×
49
        }
×
50

51
        if u.User == nil {
×
52
                return nil, ErrNoUserInfo
×
53
        }
×
54

55
        password, ok := u.User.Password()
×
56
        if !ok {
×
57
                return nil, ErrNoAccessToken
×
58
        }
×
59

60
        gn := &Gitlab{
×
61
                client:     gitlab.NewClient(nil, password),
×
62
                url:        url,
×
63
                migrations: source.NewMigrations(),
×
64
        }
×
65

×
66
        if u.Host != "" {
×
67
                uri := nurl.URL{
×
68
                        Scheme: "https",
×
69
                        Host:   u.Host,
×
70
                }
×
71

×
72
                err = gn.client.SetBaseURL(uri.String())
×
73
                if err != nil {
×
74
                        return nil, ErrInvalidHost
×
75
                }
×
76
        }
77

78
        pe := strings.Split(strings.Trim(u.Path, "/"), "/")
×
79
        if len(pe) < 1 {
×
80
                return nil, ErrInvalidProjectID
×
81
        }
×
82
        gn.projectID = pe[0]
×
83
        if len(pe) > 1 {
×
84
                gn.path = strings.Join(pe[1:], "/")
×
85
        }
×
86

87
        gn.listOptions = &gitlab.ListTreeOptions{
×
88
                Path: &gn.path,
×
89
                Ref:  &u.Fragment,
×
90
                ListOptions: gitlab.ListOptions{
×
91
                        PerPage: DefaultMaxItemsPerPage,
×
92
                },
×
93
        }
×
94

×
95
        gn.getOptions = &gitlab.GetFileOptions{
×
96
                Ref: &u.Fragment,
×
97
        }
×
98

×
99
        if err := gn.readDirectory(); err != nil {
×
100
                return nil, err
×
101
        }
×
102

103
        return gn, nil
×
104
}
105

106
func WithInstance(client *gitlab.Client, config *Config) (source.Driver, error) {
×
107
        gn := &Gitlab{
×
108
                client:     client,
×
109
                migrations: source.NewMigrations(),
×
110
        }
×
111
        if err := gn.readDirectory(); err != nil {
×
112
                return nil, err
×
113
        }
×
114
        return gn, nil
×
115
}
116

117
func (g *Gitlab) readDirectory() error {
×
118
        var nodes []*gitlab.TreeNode
×
119
        for {
×
120
                n, response, err := g.client.Repositories.ListTree(g.projectID, g.listOptions)
×
121
                if err != nil {
×
122
                        return err
×
123
                }
×
124

125
                if response.StatusCode != http.StatusOK {
×
126
                        return ErrInvalidResponse
×
127
                }
×
128

129
                nodes = append(nodes, n...)
×
130
                if response.CurrentPage >= response.TotalPages {
×
131
                        break
×
132
                }
133
                g.listOptions.ListOptions.Page = response.NextPage
×
134
        }
135

136
        for i := range nodes {
×
137
                m, err := g.nodeToMigration(nodes[i])
×
138
                if err != nil {
×
139
                        continue
×
140
                }
141

142
                if !g.migrations.Append(m) {
×
143
                        return fmt.Errorf("unable to parse file %v", nodes[i].Name)
×
144
                }
×
145
        }
146

147
        return nil
×
148
}
149

150
func (g *Gitlab) nodeToMigration(node *gitlab.TreeNode) (*source.Migration, error) {
×
151
        m := source.Regex.FindStringSubmatch(node.Name)
×
152
        if len(m) == 5 {
×
153
                versionUint64, err := strconv.ParseUint(m[1], 10, 64)
×
154
                if err != nil {
×
155
                        return nil, err
×
156
                }
×
157
                return &source.Migration{
×
158
                        Version:    uint(versionUint64),
×
159
                        Identifier: m[2],
×
160
                        Direction:  source.Direction(m[3]),
×
161
                        Raw:        g.path + "/" + node.Name,
×
162
                }, nil
×
163
        }
164
        return nil, source.ErrParse
×
165
}
166

167
func (g *Gitlab) Close() error {
×
168
        return nil
×
169
}
×
170

171
func (g *Gitlab) First() (version uint, er error) {
×
172
        if v, ok := g.migrations.First(); !ok {
×
173
                return 0, &os.PathError{Op: "first", Path: g.path, Err: os.ErrNotExist}
×
174
        } else {
×
175
                return v, nil
×
176
        }
×
177
}
178

179
func (g *Gitlab) Prev(version uint) (prevVersion uint, err error) {
×
180
        if v, ok := g.migrations.Prev(version); !ok {
×
181
                return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: g.path, Err: os.ErrNotExist}
×
182
        } else {
×
183
                return v, nil
×
184
        }
×
185
}
186

187
func (g *Gitlab) Next(version uint) (nextVersion uint, err error) {
×
188
        if v, ok := g.migrations.Next(version); !ok {
×
189
                return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: g.path, Err: os.ErrNotExist}
×
190
        } else {
×
191
                return v, nil
×
192
        }
×
193
}
194

NEW
195
func (g *Gitlab) ReadUp(version uint) (r io.ReadCloser, e source.Executor, identifier string, err error) {
×
196
        if m, ok := g.migrations.Up(version); ok {
×
197
                f, response, err := g.client.RepositoryFiles.GetFile(g.projectID, m.Raw, g.getOptions)
×
198
                if err != nil {
×
NEW
199
                        return nil, nil, "", err
×
200
                }
×
201

202
                if response.StatusCode != http.StatusOK {
×
NEW
203
                        return nil, nil, "", ErrInvalidResponse
×
204
                }
×
205

206
                content, err := base64.StdEncoding.DecodeString(f.Content)
×
207
                if err != nil {
×
NEW
208
                        return nil, nil, "", err
×
209
                }
×
210

NEW
211
                return io.NopCloser(strings.NewReader(string(content))), nil, m.Identifier, nil
×
212
        }
213

NEW
214
        return nil, nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.path, Err: os.ErrNotExist}
×
215
}
216

NEW
217
func (g *Gitlab) ReadDown(version uint) (r io.ReadCloser, e source.Executor, identifier string, err error) {
×
218
        if m, ok := g.migrations.Down(version); ok {
×
219
                f, response, err := g.client.RepositoryFiles.GetFile(g.projectID, m.Raw, g.getOptions)
×
220
                if err != nil {
×
NEW
221
                        return nil, nil, "", err
×
222
                }
×
223

224
                if response.StatusCode != http.StatusOK {
×
NEW
225
                        return nil, nil, "", ErrInvalidResponse
×
226
                }
×
227

228
                content, err := base64.StdEncoding.DecodeString(f.Content)
×
229
                if err != nil {
×
NEW
230
                        return nil, nil, "", err
×
231
                }
×
232

NEW
233
                return io.NopCloser(strings.NewReader(string(content))), nil, m.Identifier, nil
×
234
        }
235

NEW
236
        return nil, nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.path, Err: os.ErrNotExist}
×
237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc