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

golang-migrate / migrate / 15574104151

11 Jun 2025 01:44AM UTC coverage: 56.693% (+0.4%) from 56.314%
15574104151

Pull #1285

github

leozeli
fix lint
Pull Request #1285: feat(source): add embed source support

88 of 101 new or added lines in 1 file covered. (87.13%)

4650 of 8202 relevant lines covered (56.69%)

6020.84 hits per line

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

87.13
/source/embed/embed.go
1
package embed
2

3
import (
4
        "embed"
5
        "errors"
6
        "fmt"
7
        "io"
8
        "io/fs"
9
        "path"
10
        "strconv"
11

12
        "github.com/golang-migrate/migrate/v4/source"
13
)
14

15
type Embed struct {
16
        FS         embed.FS
17
        migrations *source.Migrations
18
        path       string
19
}
20

21
// NewEmbed returns a new Driver using the embed.FS and a relative path.
22
func NewEmbed(fsys embed.FS, path string) (source.Driver, error) {
38✔
23
        var e Embed
38✔
24
        if err := e.Init(fsys, path); err != nil {
40✔
25
                return nil, fmt.Errorf("failed to init embed driver with path %s: %w", path, err)
2✔
26
        }
2✔
27
        return &e, nil
36✔
28
}
29

30
// Open is part of source.Driver interface implementation.
31
// Open cannot be called on the embed driver directly as it's designed to use embed.FS.
32
func (e *Embed) Open(url string) (source.Driver, error) {
2✔
33
        return nil, errors.New("Open() cannot be called on the embed driver")
2✔
34
}
2✔
35

36
// Init prepares Embed instance to read migrations from embed.FS and a relative path.
37
func (e *Embed) Init(fsys embed.FS, path string) error {
38✔
38
        entries, err := fs.ReadDir(fsys, path)
38✔
39
        if err != nil {
40✔
40
                return err
2✔
41
        }
2✔
42

43
        ms := source.NewMigrations()
36✔
44
        for _, e := range entries {
324✔
45
                if e.IsDir() {
288✔
NEW
46
                        continue
×
47
                }
48
                m, err := source.DefaultParse(e.Name())
288✔
49
                if err != nil {
288✔
NEW
50
                        continue
×
51
                }
52
                file, err := e.Info()
288✔
53
                if err != nil {
288✔
NEW
54
                        return err
×
NEW
55
                }
×
56
                if !ms.Append(m) {
288✔
NEW
57
                        return source.ErrDuplicateMigration{
×
NEW
58
                                Migration: *m,
×
NEW
59
                                FileInfo:  file,
×
NEW
60
                        }
×
NEW
61
                }
×
62
        }
63

64
        e.FS = fsys
36✔
65
        e.path = path
36✔
66
        e.migrations = ms
36✔
67
        return nil
36✔
68
}
69

70
// Close is part of source.Driver interface implementation.
71
func (e *Embed) Close() error {
2✔
72
        // Since embed.FS doesn't support Close(), this method is a no-op
2✔
73
        return nil
2✔
74
}
2✔
75

76
// First is part of source.Driver interface implementation.
77
func (e *Embed) First() (version uint, err error) {
9,794,660✔
78
        if version, ok := e.migrations.First(); ok {
19,589,318✔
79
                return version, nil
9,794,658✔
80
        }
9,794,658✔
81
        return 0, &fs.PathError{
2✔
82
                Op:   "first",
2✔
83
                Path: e.path,
2✔
84
                Err:  fs.ErrNotExist,
2✔
85
        }
2✔
86
}
87

88
// Prev is part of source.Driver interface implementation.
89
func (e *Embed) Prev(version uint) (prevVersion uint, err error) {
22✔
90
        if version, ok := e.migrations.Prev(version); ok {
30✔
91
                return version, nil
8✔
92
        }
8✔
93
        return 0, &fs.PathError{
14✔
94
                Op:   "prev for version " + strconv.FormatUint(uint64(version), 10),
14✔
95
                Path: e.path,
14✔
96
                Err:  fs.ErrNotExist,
14✔
97
        }
14✔
98
}
99

100
// Next is part of source.Driver interface implementation.
101
func (e *Embed) Next(version uint) (nextVersion uint, err error) {
74✔
102
        if version, ok := e.migrations.Next(version); ok {
126✔
103
                return version, nil
52✔
104
        }
52✔
105
        return 0, &fs.PathError{
22✔
106
                Op:   "next for version " + strconv.FormatUint(uint64(version), 10),
22✔
107
                Path: e.path,
22✔
108
                Err:  fs.ErrNotExist,
22✔
109
        }
22✔
110
}
111

112
// ReadUp is part of source.Driver interface implementation.
113
func (e *Embed) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
22✔
114
        if m, ok := e.migrations.Up(version); ok {
32✔
115
                body, err := e.FS.ReadFile(path.Join(e.path, m.Raw))
10✔
116
                if err != nil {
10✔
NEW
117
                        return nil, "", err
×
NEW
118
                }
×
119
                return io.NopCloser(&fileReader{data: body}), m.Identifier, nil
10✔
120
        }
121
        return nil, "", &fs.PathError{
12✔
122
                Op:   "read up for version " + strconv.FormatUint(uint64(version), 10),
12✔
123
                Path: e.path,
12✔
124
                Err:  fs.ErrNotExist,
12✔
125
        }
12✔
126
}
127

128
// ReadDown is part of source.Driver interface implementation.
129
func (e *Embed) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
20✔
130
        if m, ok := e.migrations.Down(version); ok {
28✔
131
                body, err := e.FS.ReadFile(path.Join(e.path, m.Raw))
8✔
132
                if err != nil {
8✔
NEW
133
                        return nil, "", err
×
NEW
134
                }
×
135
                return io.NopCloser(&fileReader{data: body}), m.Identifier, nil
8✔
136
        }
137
        return nil, "", &fs.PathError{
12✔
138
                Op:   "read down for version " + strconv.FormatUint(uint64(version), 10),
12✔
139
                Path: e.path,
12✔
140
                Err:  fs.ErrNotExist,
12✔
141
        }
12✔
142
}
143

144
// fileReader []byte to io.ReadCloser
145
type fileReader struct {
146
        data []byte
147
        pos  int
148
}
149

150
func (fr *fileReader) Read(p []byte) (n int, err error) {
12✔
151
        if fr.pos >= len(fr.data) {
16✔
152
                return 0, io.EOF
4✔
153
        }
4✔
154
        n = copy(p, fr.data[fr.pos:])
8✔
155
        fr.pos += n
8✔
156
        return n, nil
8✔
157
}
158

159
func (fr *fileReader) Close() error {
2✔
160
        // do nothing, as embed.FS does not require closing
2✔
161
        return nil
2✔
162
}
2✔
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