• 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

43.66
/source/github/github.go
1
package github
2

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

13
        "golang.org/x/oauth2"
14

15
        "github.com/golang-migrate/migrate/v4/source"
16
        "github.com/google/go-github/v39/github"
17
)
18

19
func init() {
2✔
20
        source.Register("github", &Github{})
2✔
21
}
2✔
22

23
var (
24
        ErrNoUserInfo          = fmt.Errorf("no username:token provided")
25
        ErrNoAccessToken       = fmt.Errorf("no access token")
26
        ErrInvalidRepo         = fmt.Errorf("invalid repo")
27
        ErrInvalidGithubClient = fmt.Errorf("expected *github.Client")
28
        ErrNoDir               = fmt.Errorf("no directory")
29
)
30

31
type Github struct {
32
        config     *Config
33
        client     *github.Client
34
        options    *github.RepositoryContentGetOptions
35
        migrations *source.Migrations
36
}
37

38
type Config struct {
39
        Owner string
40
        Repo  string
41
        Path  string
42
        Ref   string
43
}
44

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

51
        // client defaults to http.DefaultClient
52
        var client *http.Client
2✔
53
        if u.User != nil {
2✔
54
                password, ok := u.User.Password()
×
55
                if !ok {
×
56
                        return nil, ErrNoUserInfo
×
57
                }
×
58
                ts := oauth2.StaticTokenSource(
×
59
                        &oauth2.Token{AccessToken: password},
×
60
                )
×
61
                client = oauth2.NewClient(context.Background(), ts)
×
62

63
        }
64

65
        gn := &Github{
2✔
66
                client:     github.NewClient(client),
2✔
67
                migrations: source.NewMigrations(),
2✔
68
                options:    &github.RepositoryContentGetOptions{Ref: u.Fragment},
2✔
69
        }
2✔
70

2✔
71
        gn.ensureFields()
2✔
72

2✔
73
        // set owner, repo and path in repo
2✔
74
        gn.config.Owner = u.Host
2✔
75
        pe := strings.Split(strings.Trim(u.Path, "/"), "/")
2✔
76
        if len(pe) < 1 {
2✔
77
                return nil, ErrInvalidRepo
×
78
        }
×
79
        gn.config.Repo = pe[0]
2✔
80
        if len(pe) > 1 {
4✔
81
                gn.config.Path = strings.Join(pe[1:], "/")
2✔
82
        }
2✔
83

84
        if err := gn.readDirectory(); err != nil {
2✔
85
                return nil, err
×
86
        }
×
87

88
        return gn, nil
2✔
89
}
90

91
func WithInstance(client *github.Client, config *Config) (source.Driver, error) {
×
92
        gn := &Github{
×
93
                client:     client,
×
94
                config:     config,
×
95
                migrations: source.NewMigrations(),
×
96
                options:    &github.RepositoryContentGetOptions{Ref: config.Ref},
×
97
        }
×
98

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

103
        return gn, nil
×
104
}
105

106
func (g *Github) readDirectory() error {
2✔
107
        g.ensureFields()
2✔
108

2✔
109
        fileContent, dirContents, _, err := g.client.Repositories.GetContents(
2✔
110
                context.Background(),
2✔
111
                g.config.Owner,
2✔
112
                g.config.Repo,
2✔
113
                g.config.Path,
2✔
114
                g.options,
2✔
115
        )
2✔
116

2✔
117
        if err != nil {
2✔
118
                return err
×
119
        }
×
120
        if fileContent != nil {
2✔
121
                return ErrNoDir
×
122
        }
×
123

124
        for _, fi := range dirContents {
30✔
125
                m, err := source.DefaultParse(*fi.Name)
28✔
126
                if err != nil {
28✔
127
                        continue // ignore files that we can't parse
×
128
                }
129
                if !g.migrations.Append(m) {
28✔
130
                        return fmt.Errorf("unable to parse file %v", *fi.Name)
×
131
                }
×
132
        }
133

134
        return nil
2✔
135
}
136

137
func (g *Github) ensureFields() {
8✔
138
        if g.config == nil {
10✔
139
                g.config = &Config{}
2✔
140
        }
2✔
141
}
142

143
func (g *Github) Close() error {
×
144
        return nil
×
145
}
×
146

147
func (g *Github) First() (version uint, err error) {
2✔
148
        g.ensureFields()
2✔
149

2✔
150
        if v, ok := g.migrations.First(); !ok {
2✔
151
                return 0, &os.PathError{Op: "first", Path: g.config.Path, Err: os.ErrNotExist}
×
152
        } else {
2✔
153
                return v, nil
2✔
154
        }
2✔
155
}
156

157
func (g *Github) Prev(version uint) (prevVersion uint, err error) {
×
158
        g.ensureFields()
×
159

×
160
        if v, ok := g.migrations.Prev(version); !ok {
×
161
                return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
×
162
        } else {
×
163
                return v, nil
×
164
        }
×
165
}
166

167
func (g *Github) Next(version uint) (nextVersion uint, err error) {
2✔
168
        g.ensureFields()
2✔
169

2✔
170
        if v, ok := g.migrations.Next(version); !ok {
2✔
171
                return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
×
172
        } else {
2✔
173
                return v, nil
2✔
174
        }
2✔
175
}
176

NEW
177
func (g *Github) ReadUp(version uint) (r io.ReadCloser, e source.Executor, identifier string, err error) {
×
178
        g.ensureFields()
×
179

×
180
        if m, ok := g.migrations.Up(version); ok {
×
181
                r, _, err := g.client.Repositories.DownloadContents(
×
182
                        context.Background(),
×
183
                        g.config.Owner,
×
184
                        g.config.Repo,
×
185
                        path.Join(g.config.Path, m.Raw),
×
186
                        g.options,
×
187
                )
×
188

×
189
                if err != nil {
×
NEW
190
                        return nil, nil, "", err
×
191
                }
×
NEW
192
                return r, nil, m.Identifier, nil
×
193
        }
NEW
194
        return nil, nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
×
195
}
196

NEW
197
func (g *Github) ReadDown(version uint) (r io.ReadCloser, e source.Executor, identifier string, err error) {
×
198
        g.ensureFields()
×
199

×
200
        if m, ok := g.migrations.Down(version); ok {
×
201
                r, _, err := g.client.Repositories.DownloadContents(
×
202
                        context.Background(),
×
203
                        g.config.Owner,
×
204
                        g.config.Repo,
×
205
                        path.Join(g.config.Path, m.Raw),
×
206
                        g.options,
×
207
                )
×
208

×
209
                if err != nil {
×
NEW
210
                        return nil, nil, "", err
×
211
                }
×
NEW
212
                return r, nil, m.Identifier, nil
×
213
        }
NEW
214
        return nil, nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
×
215
}
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