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

golang-migrate / migrate / 29014097444

09 Jul 2026 11:12AM UTC coverage: 54.447% (+0.04%) from 54.412%
29014097444

Pull #1411

github

alexfalkowski
fix(github): upgrade go-github to v89 to remove unmaintained openpgp

Bumps the github and github-ee source drivers from go-github/v39 to v89,
which removes golang.org/x/crypto/openpgp from the module graph entirely
(go mod why golang.org/x/crypto/openpgp now reports the package is not
needed).

v89 reworked client construction, so this is an API adaptation rather than
a plain version bump:

- github: NewClient(*http.Client) becomes options-based
  NewClient(...ClientOptionsFunc) (*Client, error). The token path uses
  github.WithAuthToken(...), which drops the driver's direct
  golang.org/x/oauth2 dependency (now indirect via other sources).
- github-ee: NewEnterpriseClient was removed in favor of
  NewClient(WithHTTPClient(...), WithEnterpriseURLs(...)). URL handling is
  equivalent; https://host/api/v3 still resolves to /api/v3/.

No behavior or URL-contract change for github:// or github-ee://.

Fixes #1410.

AI-assisted-by: [Claude Code](https://claude.com/claude-code)
Pull Request #1411: fix(github): upgrade go-github to v89 to remove unmaintained openpgp

8 of 10 new or added lines in 2 files covered. (80.0%)

1 existing line in 1 file now uncovered.

4383 of 8050 relevant lines covered (54.45%)

49.72 hits per line

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

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

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

12
        "github.com/golang-migrate/migrate/v4/source"
13
        "github.com/google/go-github/v89/github"
14
)
15

16
func init() {
2✔
17
        source.Register("github", &Github{})
2✔
18
}
2✔
19

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

28
type Github struct {
29
        config     *Config
30
        client     *github.Client
31
        options    *github.RepositoryContentGetOptions
32
        migrations *source.Migrations
33
}
34

35
type Config struct {
36
        Owner string
37
        Repo  string
38
        Path  string
39
        Ref   string
40
}
41

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

48
        var opts []github.ClientOptionsFunc
2✔
49
        if u.User != nil {
2✔
50
                password, ok := u.User.Password()
×
51
                if !ok {
×
52
                        return nil, ErrNoUserInfo
×
53
                }
×
NEW
54
                opts = append(opts, github.WithAuthToken(password))
×
55
        }
56

57
        client, err := github.NewClient(opts...)
2✔
58
        if err != nil {
2✔
NEW
59
                return nil, err
×
UNCOV
60
        }
×
61

62
        gn := &Github{
2✔
63
                client:     client,
2✔
64
                migrations: source.NewMigrations(),
2✔
65
                options:    &github.RepositoryContentGetOptions{Ref: u.Fragment},
2✔
66
        }
2✔
67

2✔
68
        gn.ensureFields()
2✔
69

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

81
        if err := gn.readDirectory(); err != nil {
2✔
82
                return nil, err
×
83
        }
×
84

85
        return gn, nil
2✔
86
}
87

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

×
96
        if err := gn.readDirectory(); err != nil {
×
97
                return nil, err
×
98
        }
×
99

100
        return gn, nil
×
101
}
102

103
func (g *Github) readDirectory() error {
2✔
104
        g.ensureFields()
2✔
105

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

2✔
114
        if err != nil {
2✔
115
                return err
×
116
        }
×
117
        if fileContent != nil {
2✔
118
                return ErrNoDir
×
119
        }
×
120

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

131
        return nil
2✔
132
}
133

134
func (g *Github) ensureFields() {
8✔
135
        if g.config == nil {
10✔
136
                g.config = &Config{}
2✔
137
        }
2✔
138
}
139

140
func (g *Github) Close() error {
×
141
        return nil
×
142
}
×
143

144
func (g *Github) First() (version uint, err error) {
2✔
145
        g.ensureFields()
2✔
146

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

154
func (g *Github) Prev(version uint) (prevVersion uint, err error) {
×
155
        g.ensureFields()
×
156

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

164
func (g *Github) Next(version uint) (nextVersion uint, err error) {
2✔
165
        g.ensureFields()
2✔
166

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

174
func (g *Github) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
×
175
        g.ensureFields()
×
176

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

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

194
func (g *Github) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
×
195
        g.ensureFields()
×
196

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

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

© 2026 Coveralls, Inc