snapshot of https://github.com/golang-migrate/migrate.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / test (1.26.x) (push) Failing after 5s
CI / Check coverage (push) Has been skipped
CI / lint (push) Failing after 41s
CI / test (1.25.x) (push) Failing after 54s
CI / Release a new version (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 21:52:57 +05:00
commit 9f3bffc2d3
405 changed files with 27657 additions and 0 deletions

1
source/github/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.github_test_secrets

16
source/github/README.md Normal file
View File

@@ -0,0 +1,16 @@
# github
This driver is catered for those that want to source migrations from [github.com](https://github.com). The URL scheme doesn't require a hostname, as it just simply defaults to `github.com`.
Authenticated client: `github://user:personal-access-token@owner/repo/path#ref`
Unauthenticated client: `github://owner/repo/path#ref`
| URL Query | WithInstance Config | Description |
|------------|---------------------|-------------|
| user | | (optional) The username of the user connecting |
| personal-access-token | | (optional) An access token from GitHub (https://github.com/settings/tokens) |
| owner | | the repo owner |
| repo | | the name of the repository |
| path | | path in repo to migrations |
| ref | | (optional) can be a SHA, branch, or tag |

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS users;

View File

@@ -0,0 +1,5 @@
CREATE TABLE users (
user_id integer unique,
name varchar(40),
email varchar(40)
);

View File

@@ -0,0 +1 @@
ALTER TABLE users DROP COLUMN IF EXISTS city;

View File

@@ -0,0 +1,3 @@
ALTER TABLE users ADD COLUMN city varchar(100);

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS users_email_index;

View File

@@ -0,0 +1,3 @@
CREATE UNIQUE INDEX CONCURRENTLY users_email_index ON users (email);
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS books;

View File

@@ -0,0 +1,5 @@
CREATE TABLE books (
user_id integer,
name varchar(40),
author varchar(40)
);

View File

@@ -0,0 +1 @@
DROP TABLE IF EXISTS movies;

View File

@@ -0,0 +1,5 @@
CREATE TABLE movies (
user_id integer,
name varchar(40),
director varchar(40)
);

View File

@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.

View File

@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.

View File

@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.

View File

@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.

215
source/github/github.go Normal file
View File

@@ -0,0 +1,215 @@
package github
import (
"context"
"fmt"
"io"
"net/http"
nurl "net/url"
"os"
"path"
"strings"
"golang.org/x/oauth2"
"github.com/golang-migrate/migrate/v4/source"
"github.com/google/go-github/v39/github"
)
func init() {
source.Register("github", &Github{})
}
var (
ErrNoUserInfo = fmt.Errorf("no username:token provided")
ErrNoAccessToken = fmt.Errorf("no access token")
ErrInvalidRepo = fmt.Errorf("invalid repo")
ErrInvalidGithubClient = fmt.Errorf("expected *github.Client")
ErrNoDir = fmt.Errorf("no directory")
)
type Github struct {
config *Config
client *github.Client
options *github.RepositoryContentGetOptions
migrations *source.Migrations
}
type Config struct {
Owner string
Repo string
Path string
Ref string
}
func (g *Github) Open(url string) (source.Driver, error) {
u, err := nurl.Parse(url)
if err != nil {
return nil, err
}
// client defaults to http.DefaultClient
var client *http.Client
if u.User != nil {
password, ok := u.User.Password()
if !ok {
return nil, ErrNoUserInfo
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: password},
)
client = oauth2.NewClient(context.Background(), ts)
}
gn := &Github{
client: github.NewClient(client),
migrations: source.NewMigrations(),
options: &github.RepositoryContentGetOptions{Ref: u.Fragment},
}
gn.ensureFields()
// set owner, repo and path in repo
gn.config.Owner = u.Host
pe := strings.Split(strings.Trim(u.Path, "/"), "/")
if len(pe) < 1 {
return nil, ErrInvalidRepo
}
gn.config.Repo = pe[0]
if len(pe) > 1 {
gn.config.Path = strings.Join(pe[1:], "/")
}
if err := gn.readDirectory(); err != nil {
return nil, err
}
return gn, nil
}
func WithInstance(client *github.Client, config *Config) (source.Driver, error) {
gn := &Github{
client: client,
config: config,
migrations: source.NewMigrations(),
options: &github.RepositoryContentGetOptions{Ref: config.Ref},
}
if err := gn.readDirectory(); err != nil {
return nil, err
}
return gn, nil
}
func (g *Github) readDirectory() error {
g.ensureFields()
fileContent, dirContents, _, err := g.client.Repositories.GetContents(
context.Background(),
g.config.Owner,
g.config.Repo,
g.config.Path,
g.options,
)
if err != nil {
return err
}
if fileContent != nil {
return ErrNoDir
}
for _, fi := range dirContents {
m, err := source.DefaultParse(*fi.Name)
if err != nil {
continue // ignore files that we can't parse
}
if !g.migrations.Append(m) {
return fmt.Errorf("unable to parse file %v", *fi.Name)
}
}
return nil
}
func (g *Github) ensureFields() {
if g.config == nil {
g.config = &Config{}
}
}
func (g *Github) Close() error {
return nil
}
func (g *Github) First() (version uint, err error) {
g.ensureFields()
if v, ok := g.migrations.First(); !ok {
return 0, &os.PathError{Op: "first", Path: g.config.Path, Err: os.ErrNotExist}
} else {
return v, nil
}
}
func (g *Github) Prev(version uint) (prevVersion uint, err error) {
g.ensureFields()
if v, ok := g.migrations.Prev(version); !ok {
return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
} else {
return v, nil
}
}
func (g *Github) Next(version uint) (nextVersion uint, err error) {
g.ensureFields()
if v, ok := g.migrations.Next(version); !ok {
return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
} else {
return v, nil
}
}
func (g *Github) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
g.ensureFields()
if m, ok := g.migrations.Up(version); ok {
r, _, err := g.client.Repositories.DownloadContents(
context.Background(),
g.config.Owner,
g.config.Repo,
path.Join(g.config.Path, m.Raw),
g.options,
)
if err != nil {
return nil, "", err
}
return r, m.Identifier, nil
}
return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
}
func (g *Github) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
g.ensureFields()
if m, ok := g.migrations.Down(version); ok {
r, _, err := g.client.Repositories.DownloadContents(
context.Background(),
g.config.Owner,
g.config.Repo,
path.Join(g.config.Path, m.Raw),
g.options,
)
if err != nil {
return nil, "", err
}
return r, m.Identifier, nil
}
return nil, "", &os.PathError{Op: fmt.Sprintf("read version %v", version), Path: g.config.Path, Err: os.ErrNotExist}
}

View File

@@ -0,0 +1,59 @@
package github
import (
"bytes"
"fmt"
"os"
"testing"
st "github.com/golang-migrate/migrate/v4/source/testing"
"github.com/stretchr/testify/assert"
)
var GithubTestSecret = "" // username:token
func init() {
secrets, err := os.ReadFile(".github_test_secrets")
if err == nil {
GithubTestSecret = string(bytes.TrimSpace(secrets)[:])
}
}
func Test(t *testing.T) {
if len(GithubTestSecret) == 0 {
t.Skip("test requires .github_test_secrets")
}
g := &Github{}
d, err := g.Open("github://" + GithubTestSecret + "@mattes/migrate_test_tmp/test#452b8003e7")
if err != nil {
t.Fatal(err)
}
st.Test(t, d)
}
func TestDefaultClient(t *testing.T) {
g := &Github{}
owner := "golang-migrate"
repo := "migrate"
path := "source/github/examples/migrations"
url := fmt.Sprintf("github://%s/%s/%s", owner, repo, path)
d, err := g.Open(url)
if err != nil {
t.Fatal(err)
}
ver, err := d.First()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, uint(1085649617), ver)
ver, err = d.Next(ver)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, uint(1185749658), ver)
}