github.com/duskeagle/pop@v4.10.1-0.20190417200916-92f2b794aab5+incompatible/file_migrator.go (about)

     1  package pop
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"text/template"
    12  
    13  	"github.com/gobuffalo/fizz"
    14  	"github.com/gobuffalo/pop/fix"
    15  	"github.com/gobuffalo/pop/logging"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // FileMigrator is a migrator for SQL and Fizz
    20  // files on disk at a specified path.
    21  type FileMigrator struct {
    22  	Migrator
    23  	Path string
    24  }
    25  
    26  // NewFileMigrator for a path and a Connection
    27  func NewFileMigrator(path string, c *Connection) (FileMigrator, error) {
    28  	fm := FileMigrator{
    29  		Migrator: NewMigrator(c),
    30  		Path:     path,
    31  	}
    32  	fm.SchemaPath = path
    33  
    34  	err := fm.findMigrations()
    35  	if err != nil {
    36  		return fm, errors.WithStack(err)
    37  	}
    38  
    39  	return fm, nil
    40  }
    41  
    42  func (fm *FileMigrator) findMigrations() error {
    43  	dir := fm.Path
    44  	if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
    45  		// directory doesn't exist
    46  		return nil
    47  	}
    48  	filepath.Walk(dir, func(p string, info os.FileInfo, err error) error {
    49  		if !info.IsDir() {
    50  			matches := mrx.FindAllStringSubmatch(info.Name(), -1)
    51  			if len(matches) == 0 {
    52  				return nil
    53  			}
    54  			m := matches[0]
    55  			var dbType string
    56  			if m[3] == "" {
    57  				dbType = "all"
    58  			} else {
    59  				dbType = m[3][1:]
    60  				if !DialectSupported(dbType) {
    61  					return fmt.Errorf("unsupported dialect %s", dbType)
    62  				}
    63  			}
    64  			mf := Migration{
    65  				Path:      p,
    66  				Version:   m[1],
    67  				Name:      m[2],
    68  				DBType:    dbType,
    69  				Direction: m[4],
    70  				Type:      m[5],
    71  				Runner: func(mf Migration, tx *Connection) error {
    72  					f, err := os.Open(p)
    73  					if err != nil {
    74  						return errors.WithStack(err)
    75  					}
    76  					content, err := migrationContent(mf, tx, f)
    77  					if err != nil {
    78  						return errors.Wrapf(err, "error processing %s", mf.Path)
    79  					}
    80  
    81  					if content == "" {
    82  						return nil
    83  					}
    84  
    85  					err = tx.RawQuery(content).Exec()
    86  					if err != nil {
    87  						return errors.Wrapf(err, "error executing %s, sql: %s", mf.Path, content)
    88  					}
    89  					return nil
    90  				},
    91  			}
    92  			fm.Migrations[mf.Direction] = append(fm.Migrations[mf.Direction], mf)
    93  		}
    94  		return nil
    95  	})
    96  	return nil
    97  }
    98  
    99  func migrationContent(mf Migration, c *Connection, r io.Reader) (string, error) {
   100  	b, err := ioutil.ReadAll(r)
   101  	if err != nil {
   102  		return "", nil
   103  	}
   104  
   105  	content := string(b)
   106  
   107  	if mf.Type == "fizz" {
   108  		// test for && fix anko migrations
   109  		fixed, err := fix.Anko(content)
   110  		if err != nil {
   111  			return "", errors.Wrapf(err, "could not fizz the migration %s", mf.Path)
   112  		}
   113  		if strings.TrimSpace(fixed) != strings.TrimSpace(content) {
   114  			log(logging.Warn, "%s uses an old fizz syntax. please use\n%s", mf.Path, fixed)
   115  		}
   116  		content = fixed
   117  	}
   118  
   119  	t := template.Must(template.New("sql").Parse(content))
   120  	var bb bytes.Buffer
   121  	err = t.Execute(&bb, c.Dialect.Details())
   122  	if err != nil {
   123  		return "", errors.Wrapf(err, "could not execute migration template %s", mf.Path)
   124  	}
   125  	content = bb.String()
   126  
   127  	if mf.Type == "fizz" {
   128  		content, err = fizz.AString(content, c.Dialect.FizzTranslator())
   129  		if err != nil {
   130  			return "", errors.Wrapf(err, "could not fizz the migration %s", mf.Path)
   131  		}
   132  	}
   133  	return content, nil
   134  }