create.go

  1package goose
  2
  3import (
  4	"database/sql"
  5	"errors"
  6	"fmt"
  7	"os"
  8	"path/filepath"
  9	"text/template"
 10	"time"
 11)
 12
 13type tmplVars struct {
 14	Version   string
 15	CamelName string
 16}
 17
 18var (
 19	sequential = false
 20)
 21
 22// SetSequential set whether to use sequential versioning instead of timestamp based versioning
 23func SetSequential(s bool) {
 24	sequential = s
 25}
 26
 27// Create writes a new blank migration file.
 28func CreateWithTemplate(db *sql.DB, dir string, tmpl *template.Template, name, migrationType string) error {
 29	version := time.Now().UTC().Format(timestampFormat)
 30
 31	if sequential {
 32		// always use DirFS here because it's modifying operation
 33		migrations, err := collectMigrationsFS(osFS{}, dir, minVersion, maxVersion, registeredGoMigrations)
 34		if err != nil && !errors.Is(err, ErrNoMigrationFiles) {
 35			return err
 36		}
 37
 38		vMigrations, err := migrations.versioned()
 39		if err != nil {
 40			return err
 41		}
 42
 43		if last, err := vMigrations.Last(); err == nil {
 44			version = fmt.Sprintf(seqVersionTemplate, last.Version+1)
 45		} else {
 46			version = fmt.Sprintf(seqVersionTemplate, int64(1))
 47		}
 48	}
 49
 50	filename := fmt.Sprintf("%v_%v.%v", version, snakeCase(name), migrationType)
 51
 52	if tmpl == nil {
 53		if migrationType == "go" {
 54			tmpl = goSQLMigrationTemplate
 55		} else {
 56			tmpl = sqlMigrationTemplate
 57		}
 58	}
 59
 60	path := filepath.Join(dir, filename)
 61	if _, err := os.Stat(path); !os.IsNotExist(err) {
 62		return fmt.Errorf("failed to create migration file: %w", err)
 63	}
 64
 65	f, err := os.Create(path)
 66	if err != nil {
 67		return fmt.Errorf("failed to create migration file: %w", err)
 68	}
 69	defer f.Close()
 70
 71	vars := tmplVars{
 72		Version:   version,
 73		CamelName: camelCase(name),
 74	}
 75	if err := tmpl.Execute(f, vars); err != nil {
 76		return fmt.Errorf("failed to execute tmpl: %w", err)
 77	}
 78
 79	log.Printf("Created new file: %s", f.Name())
 80	return nil
 81}
 82
 83// Create writes a new blank migration file.
 84func Create(db *sql.DB, dir, name, migrationType string) error {
 85	return CreateWithTemplate(db, dir, nil, name, migrationType)
 86}
 87
 88var sqlMigrationTemplate = template.Must(template.New("goose.sql-migration").Parse(`-- +goose Up
 89-- +goose StatementBegin
 90SELECT 'up SQL query';
 91-- +goose StatementEnd
 92
 93-- +goose Down
 94-- +goose StatementBegin
 95SELECT 'down SQL query';
 96-- +goose StatementEnd
 97`))
 98
 99var goSQLMigrationTemplate = template.Must(template.New("goose.go-migration").Parse(`package migrations
100
101import (
102	"context"
103	"database/sql"
104	"github.com/pressly/goose/v3"
105)
106
107func init() {
108	goose.AddMigrationContext(up{{.CamelName}}, down{{.CamelName}})
109}
110
111func up{{.CamelName}}(ctx context.Context, tx *sql.Tx) error {
112	// This code is executed when the migration is applied.
113	return nil
114}
115
116func down{{.CamelName}}(ctx context.Context, tx *sql.Tx) error {
117	// This code is executed when the migration is rolled back.
118	return nil
119}
120`))