├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── commands ├── commands.go ├── cronjobs_commands.go └── migrate_commands.go ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | journey 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - "1.11" 4 | - "1.12" 5 | - "1.13" 6 | - tip 7 | 8 | before_deploy: 9 | - GOOS=linux GOARCH=amd64 ENABLE_CGO=1 go build -a -o build/journey.linux-amd64 -ldflags '-s -w' . 10 | - find build -name 'journey*' | xargs -I{} tar czf {}.tar.gz {} 11 | - shasum -a 256 build/* > build/sha256sum.txt 12 | - cat build/sha256sum.txt 13 | 14 | deploy: 15 | - provider: releases 16 | api_key: 17 | secure: Y47mTXZIluEegpjVn5bhV5wcgxBJqsQ6L40Pjm1beJWPdmflVzEh0iHxYR1RcaWcJW3XEsUwS/r81fpflO9bxYcDtV/zNJTuTK9w48sJZjkh5tLJ9LsDsIjmKk1iNYSLhEW+AaUBLNOYRB2grI17E4lmh1r29n1FDl/vpNehFoEyqnKdx28zh/jLxRIqMGsj2NrjVI5EG/9M/F/gXelsrbuMZHoec/i5bk1QMpawAGabjXNZujbmZVbOzNxlAfDP6Fdxb0C/y6+C2EKIxvT5o5hhIsesgMVgpI/5k3tycprYqA7e9GjqO39XHfHaM0J8TKBNiuQMDV8TNCR4ohBTpVOOM2Kz7/IWBKtTp24o0UCX6/a3QcY5e8xdVCDI2noPm/9g1ORSz//7NJO3sPMSqKsTRiiwBAxhSo1iFYaZ7w9KJGgdWQFVr1OT6cXBGhxO6RGnPQ3R6jbDR7bxk6IcQpjKvRMdBiw+UnBQMq5YxUMD6+jDs6B6hMVXpb5mblCcqybNFJe10V7qSc+OZX5ZQZdQOWxb2XlelNP3yuPV5xU5DBwu7R0tNIahJ/t2EPMtkq0GGoKz0DhnmJeRxYrYOsMxBiY+WkbV8QOUPCwffxC8PPka5eMiCsKAzadtembDUoUR9HOtFzdwI+9wizV9A4WlhQEGrxQTT+fThcb7U4s= 18 | skip_cleanup: true 19 | on: 20 | go: 1.9 21 | repo: db-journey/journey 22 | tags: true 23 | file: 24 | - build/journey.linux-amd64.tar.gz 25 | - build/sha256sum.txt 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Journey Changelog 2 | 3 | ## 2.2.6 - 2020-02-17 4 | 5 | - Update urfave/cli dependency to v2 6 | 7 | ## 2.2.5 - 2020-02-16 8 | 9 | - Update cronjobs and migrate dependencies 10 | 11 | ## 2.2.4 - 2019-12-09 12 | 13 | - Update cronjobs dependency 14 | 15 | ## 2.2.3 - 2019-11-29 16 | 17 | - Update dependencies 18 | 19 | ## 2.2.2 - 2019-11-04 20 | 21 | * Update dependencies, fix compilation for go >= 1.11 22 | 23 | ## 2.2.1 - 2019-09-14 24 | 25 | * Add go modules support 26 | 27 | ## 2.2.0 - 2018-01-22 28 | 29 | * Update to migrate [v2.0.0](https://github.com/db-journey/migrate/releases/tag/v2.0.0) 30 | * [Feature] apply specific version (run up migration) 31 | * [Feature] rollback specific version (run down migration) 32 | 33 | ## 2.1.1 - 2017-12-12 34 | 35 | * Fix docker image building 36 | 37 | ## 2.1.0 - 2017-07-07 38 | 39 | * [migrate] Add templating support in migration files 40 | * [scheduler] Add jobs duration to log 41 | 42 | ## 2.0.0 - 2017-04-06 43 | 44 | This is a breaking change release. 45 | The cli now expose two main commands: 46 | 47 | * "migrate", the old behaviour 48 | * "cronjobs", added in this release 49 | 50 | ie: old params and commands are working if using `journey migrate [...]` 51 | 52 | * Add cronjobs support (https://github.com/db-journey/cronjobs) 53 | * Switch to urfave cli 54 | * Provide commands as a package 55 | 56 | ## 1.4.2 - 2017-02-07 57 | 58 | * Split repos from gemnasium/migrate (a fork of mattes/migrate) 59 | 60 | ## v1.4.1 - 2016-12-16 61 | 62 | * [cassandra] Add [disable_init_host_lookup](https://github.com/gocql/gocql/blob/master/cluster.go#L92) url param (@GeorgeMac / #17) 63 | 64 | ## v1.4.0 - 2016-11-22 65 | 66 | * [crate] Add [Crate](https://crate.io) database support, based on the Crate sql driver by [herenow](https://github.com/herenow/go-crate) (@dereulenspiegel / #16) 67 | 68 | ## v1.3.2 - 2016-11-11 69 | 70 | * [sqlite] Allow multiple statements per migration (dklimkin / #11) 71 | 72 | ## v1.3.1 - 2016-08-16 73 | 74 | * Make MySQL driver aware of SSL certificates for TLS connection by scanning ENV variables (https://github.com/mattes/migrate/pull/117/files) 75 | 76 | ## v1.3.0 - 2016-08-15 77 | 78 | * Initial changelog release 79 | * Timestamp migration, instead of increments (https://github.com/mattes/migrate/issues/102) 80 | * Versions will now be tagged 81 | * Added consistency parameter to cassandra connection string (https://github.com/mattes/migrate/pull/114) 82 | 83 | 84 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian 2 | RUN apt update && \ 3 | apt install -y curl && \ 4 | curl -OL --fail https://github.com/db-journey/journey/releases/download/v2.1.1/journey.linux-amd64.tar.gz && \ 5 | tar xvzf journey.linux-amd64.tar.gz && \ 6 | mv build/journey.linux-amd64 /usr/local/bin/journey && \ 7 | chmod +x /usr/local/bin/journey && \ 8 | apt remove -y curl && \ 9 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 10 | ENTRYPOINT /usr/local/bin/journey 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 db-journey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Journey 2 | 3 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 4 | [![Build Status](https://travis-ci.org/db-journey/journey.svg?branch=master)](https://travis-ci.org/db-journey/journey) 5 | [![GoDoc](https://godoc.org/github.com/db-journey/journey?status.svg)](https://godoc.org/github.com/db-journey/journey) 6 | 7 | Journey is based on the work of @mattes on his tool "migrate": https://github.com/mattes/migrate/ 8 | 9 | ## __Features__ 10 | 11 | * Super easy to implement [Driver interface](http://godoc.org/github.com/db-journey/migrate/v2/driver#Driver). 12 | * Gracefully quit running migrations on ``^C``. 13 | * No magic search paths routines, no hard-coded config files. 14 | * CLI is build on top of the ``migrate`` package. 15 | * Migration files templating 16 | 17 | 18 | ## Available Drivers 19 | 20 | * [PostgreSQL](https://github.com/db-journey/postgresql-driver) 21 | * [Cassandra](https://github.com/db-journey/cassandra-driver) 22 | * [SQLite](https://github.com/db-journey/sqlite3-driver) 23 | * [MySQL](https://github.com/db-journey/mysql-driver) ([experimental](https://github.com/mattes/migrate/issues/1#issuecomment-58728186)) 24 | * Bash (planned) 25 | 26 | Need another driver? Just implement the [Driver interface](http://godoc.org/github.com/db-journey/migrate/v2/driver#Driver) and open a PR. 27 | 28 | ## Usage from Terminal 29 | 30 | ```bash 31 | # install 32 | go get github.com/db-journey/journey/v2 33 | 34 | # create new migration file in path 35 | journey --url driver://url --path ./migrations migrate create migration_file_xyz 36 | 37 | # apply all available migrations 38 | journey --url driver://url --path ./migrations migrate up 39 | 40 | # roll back all migrations 41 | journey --url driver://url --path ./migrations migrate down 42 | 43 | # roll back the most recently applied migration, then run it again. 44 | journey --url driver://url --path ./migrations migrate redo 45 | 46 | # run down and then up command 47 | journey --url driver://url --path ./migrations migrate reset 48 | 49 | # show the current migration version 50 | journey --url driver://url --path ./migrations migrate version 51 | 52 | # apply the next n migrations 53 | journey --url driver://url --path ./migrations migrate migrate +1 54 | journey --url driver://url --path ./migrations migrate migrate +2 55 | journey --url driver://url --path ./migrations migrate migrate +n 56 | 57 | # roll back the previous n migrations 58 | journey --url driver://url --path ./migrations migrate migrate -1 59 | journey --url driver://url --path ./migrations migrate migrate -2 60 | journey --url driver://url --path ./migrations migrate migrate -n 61 | 62 | # go to specific migration 63 | journey --url driver://url --path ./migrations migrate goto 1 64 | journey --url driver://url --path ./migrations migrate goto 10 65 | journey --url driver://url --path ./migrations migrate goto v 66 | ``` 67 | 68 | ## CronJobs 69 | 70 | Journey also provides a command to run scheduled jobs on databases: 71 | 72 | 73 | ```bash 74 | journey --url driver://url --path ./cronjobs scheduler start 75 | ``` 76 | 77 | ## Migration files templating 78 | 79 | Journey supports dynamic migrations files, by using go templates. 80 | 81 | If a file in the migrations folder has the extension `.tpl` (it must match the driver file extensions, so `.sql.tpl` for sql drivers), it will parsed and executed using journey current environment. 82 | 83 | Example: 84 | 85 | ```bash 86 | $ echo "create table {{.TABLE}} (id int64, name text);" >> files/20170707204006_template.up.sql.tpl 87 | $ TABLE=a_table journey migrate 88 | ``` 89 | 90 | For more information about go templating, refer to the official doc: https://golang.org/pkg/text/template/ 91 | 92 | This feature is particularly usefull to avoid leaving sensitive data in migrations, or to make adjustments based on current environment. 93 | -------------------------------------------------------------------------------- /commands/commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/urfave/cli/v2" 5 | ) 6 | 7 | func Commands() cli.Commands { 8 | return cli.Commands{ 9 | { 10 | Name: "migrate", 11 | Aliases: []string{"m"}, 12 | Usage: "migrate database", 13 | Subcommands: MigrateCommands(), 14 | }, 15 | { 16 | Name: "schedule", 17 | Aliases: []string{"s"}, 18 | Usage: "Schedule cron jobs", 19 | Subcommands: CronjobsCommands(), 20 | }, 21 | } 22 | } 23 | 24 | func Flags() []cli.Flag { 25 | return []cli.Flag{ 26 | &cli.StringFlag{ 27 | Name: "url, u", 28 | Usage: "Driver URL", 29 | Value: "", 30 | EnvVars: []string{"DRIVER_URL"}, 31 | }, 32 | &cli.StringFlag{ 33 | Name: "path, p", 34 | Usage: "Files path", 35 | Value: "./files", 36 | EnvVars: []string{"FILES_PATH"}, 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /commands/cronjobs_commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/db-journey/cronjobs" 5 | "github.com/db-journey/migrate/v2/driver" 6 | "github.com/sirupsen/logrus" 7 | "github.com/urfave/cli/v2" 8 | ) 9 | 10 | var CronjobsFlags = []cli.Flag{} 11 | 12 | func CronjobsCommands() cli.Commands { 13 | return cli.Commands{ 14 | startCommand, 15 | } 16 | } 17 | 18 | var startCommand = &cli.Command{ 19 | Name: "start", 20 | Aliases: []string{"s"}, 21 | Usage: "Start scheduler", 22 | Flags: CronjobsFlags, 23 | Action: func(ctx *cli.Context) error { 24 | 25 | driver, err := driver.New(ctx.String("url")) 26 | if err != nil { 27 | logrus.WithError(err).Fatal("Can't initiate driver") 28 | } 29 | 30 | scheduler := cronjobs.New(driver) 31 | logrus.Info("Loading cron files from ", ctx.String("path")) 32 | err = scheduler.ReadFiles(ctx.String("path")) 33 | if err != nil { 34 | logrus.WithError(err).Fatal("Can't load files") 35 | } 36 | scheduler.Logger = func(runs chan *cronjobs.Run) { 37 | for run := range runs { 38 | logger := logrus.WithFields( 39 | logrus.Fields{ 40 | "name": run.Name, 41 | "took": run.Duration, 42 | }) 43 | if run.Error != nil { 44 | logger.WithError(run.Error).Error("Failed to run job") 45 | continue 46 | } 47 | logger.Info("Job successful") 48 | } 49 | } 50 | numJobs := len(scheduler.Entries()) 51 | if numJobs == 0 { 52 | logrus.Fatal("No cron job found in ", ctx.String("path")) 53 | } 54 | logrus.Infof("%d job(s) loaded", numJobs) 55 | logrus.Info("Starting Scheduler") 56 | scheduler.Start() 57 | select {} 58 | return nil 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /commands/migrate_commands.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | "strconv" 8 | "strings" 9 | 10 | log "github.com/sirupsen/logrus" 11 | 12 | "github.com/db-journey/migrate/v2" 13 | "github.com/db-journey/migrate/v2/file" 14 | "github.com/urfave/cli/v2" 15 | ) 16 | 17 | var MigrateFlags = []cli.Flag{} 18 | 19 | //Commands returns the application cli commands: 20 | //create Create a new migration 21 | //up Apply all -up- migrations 22 | //down Apply all -down- migrations 23 | //reset Down followed by Up 24 | //redo Roll back most recent migration, then apply it again 25 | //version Show current migration version 26 | //migrate Apply migrations -n|+n 27 | //goto Migrate to version v 28 | func MigrateCommands() cli.Commands { 29 | return cli.Commands{ 30 | createCommand, 31 | upCommand, 32 | downCommand, 33 | resetCommand, 34 | redoCommand, 35 | versionCommand, 36 | migrateCommand, 37 | applyCommand, 38 | rollbackCommand, 39 | gotoCommand, 40 | } 41 | } 42 | 43 | var createCommand = &cli.Command{ 44 | Name: "create", 45 | Aliases: []string{"c"}, 46 | Usage: "Create a new migration", 47 | ArgsUsage: "", 48 | Flags: MigrateFlags, 49 | Action: func(ctx *cli.Context) error { 50 | name := ctx.Args().First() 51 | if name == "" { 52 | log.Fatal("Please specify a name for the new migration") 53 | } 54 | // if more than one param is passed, create a concat name 55 | if ctx.NArg() != 1 { 56 | name = strings.Join(ctx.Args().Slice(), "_") 57 | } 58 | 59 | migrate, _, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 60 | defer cancel() 61 | 62 | migrationFile, err := migrate.Create(name) 63 | if err != nil { 64 | logErr(err).Fatal("Migration failed") 65 | } 66 | 67 | log.WithFields(log.Fields{ 68 | "up": migrationFile.UpFile.FileName, 69 | "down": migrationFile.DownFile.FileName, 70 | }).Infof("Version %v migration files created in %v:\n", migrationFile.Version, ctx.String("path")) 71 | return nil 72 | }, 73 | } 74 | 75 | var upCommand = &cli.Command{ 76 | Name: "up", 77 | Usage: "Apply all -up- migrations", 78 | Flags: MigrateFlags, 79 | Action: func(ctx *cli.Context) error { 80 | log.Info("Applying all -up- migrations") 81 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 82 | defer cancel() 83 | err := migrate.Up(mctx) 84 | if err != nil { 85 | logErr(err).Fatal("Failed to apply all -up- migrations") 86 | } 87 | logCurrentVersion(mctx, migrate) 88 | return nil 89 | }, 90 | } 91 | 92 | var downCommand = &cli.Command{ 93 | Name: "down", 94 | Usage: "Apply all -down- migrations", 95 | Flags: MigrateFlags, 96 | Action: func(ctx *cli.Context) error { 97 | log.Info("Applying all -down- migrations") 98 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 99 | defer cancel() 100 | err := migrate.Down(mctx) 101 | if err != nil { 102 | logErr(err).Fatal("Failed to apply all -down- migrations") 103 | } 104 | logCurrentVersion(mctx, migrate) 105 | return nil 106 | }, 107 | } 108 | 109 | var redoCommand = &cli.Command{ 110 | Name: "redo", 111 | Aliases: []string{"r"}, 112 | Usage: "Roll back most recent migration, then apply it again", 113 | Flags: MigrateFlags, 114 | Action: func(ctx *cli.Context) error { 115 | log.Info("Redoing last migration") 116 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 117 | defer cancel() 118 | err := migrate.Redo(mctx) 119 | if err != nil { 120 | logErr(err).Fatal("Failed to redo last migration") 121 | } 122 | logCurrentVersion(mctx, migrate) 123 | return nil 124 | }, 125 | } 126 | 127 | var versionCommand = &cli.Command{ 128 | Name: "version", 129 | Aliases: []string{"v"}, 130 | Usage: "Show current migration version", 131 | Flags: MigrateFlags, 132 | Action: func(ctx *cli.Context) error { 133 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 134 | defer cancel() 135 | version, err := migrate.Version(mctx) 136 | if err != nil { 137 | logErr(err).Fatal("Unable to fetch version") 138 | } 139 | 140 | log.Infof("Current version: %d", version) 141 | return nil 142 | }, 143 | } 144 | 145 | var resetCommand = &cli.Command{ 146 | Name: "reset", 147 | Usage: "Down followed by Up", 148 | Flags: MigrateFlags, 149 | Action: func(ctx *cli.Context) error { 150 | log.Info("Reseting database") 151 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 152 | defer cancel() 153 | err := migrate.Redo(mctx) 154 | if err != nil { 155 | logErr(err).Fatal("Failed to reset database") 156 | } 157 | logCurrentVersion(mctx, migrate) 158 | return nil 159 | }, 160 | } 161 | 162 | var migrateCommand = &cli.Command{ 163 | Name: "migrate", 164 | Aliases: []string{"m"}, 165 | Usage: "Apply migrations -n|+n", 166 | ArgsUsage: "", 167 | Flags: MigrateFlags, 168 | SkipFlagParsing: true, 169 | Action: func(ctx *cli.Context) error { 170 | relativeN := ctx.Args().First() 171 | relativeNInt, err := strconv.Atoi(relativeN) 172 | if err != nil { 173 | logErr(err).Fatal("Unable to parse param ") 174 | } 175 | 176 | log.Infof("Applying %d migrations", relativeNInt) 177 | 178 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 179 | defer cancel() 180 | err = migrate.Migrate(mctx, relativeNInt) 181 | if err != nil { 182 | logErr(err).Fatalf("Failed to apply %d migrations", relativeNInt) 183 | } 184 | logCurrentVersion(mctx, migrate) 185 | return nil 186 | }, 187 | } 188 | 189 | var applyCommand = &cli.Command{ 190 | Name: "apply", 191 | Aliases: []string{"a"}, 192 | Usage: "Run up migration for specific version", 193 | ArgsUsage: "", 194 | Flags: MigrateFlags, 195 | SkipFlagParsing: true, 196 | Action: func(ctx *cli.Context) error { 197 | version := ctx.Args().First() 198 | versionInt, err := strconv.Atoi(version) 199 | if err != nil { 200 | logErr(err).Fatal("Unable to parse param ") 201 | } 202 | 203 | log.Infof("Applying version %d", versionInt) 204 | 205 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 206 | defer cancel() 207 | err = migrate.ApplyVersion(mctx, file.Version(versionInt)) 208 | if err != nil { 209 | logErr(err).Fatalf("Failed to apply version %d", versionInt) 210 | } 211 | logCurrentVersion(mctx, migrate) 212 | return nil 213 | }, 214 | } 215 | 216 | var rollbackCommand = &cli.Command{ 217 | Name: "rollback", 218 | Aliases: []string{"r"}, 219 | Usage: "Run down migration for specific version", 220 | ArgsUsage: "", 221 | Flags: MigrateFlags, 222 | SkipFlagParsing: true, 223 | Action: func(ctx *cli.Context) error { 224 | version := ctx.Args().First() 225 | versionInt, err := strconv.Atoi(version) 226 | if err != nil { 227 | logErr(err).Fatal("Unable to parse param ") 228 | } 229 | 230 | log.Infof("Applying version %d", versionInt) 231 | 232 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 233 | defer cancel() 234 | err = migrate.RollbackVersion(mctx, file.Version(versionInt)) 235 | if err != nil { 236 | logErr(err).Fatalf("Failed to rollback version %d", versionInt) 237 | } 238 | logCurrentVersion(mctx, migrate) 239 | return nil 240 | }, 241 | } 242 | 243 | var gotoCommand = &cli.Command{ 244 | Name: "goto", 245 | Aliases: []string{"g"}, 246 | Usage: "Migrate to version ", 247 | ArgsUsage: "", 248 | Flags: MigrateFlags, 249 | Action: func(ctx *cli.Context) error { 250 | toVersion := ctx.Args().First() 251 | toVersionInt, err := strconv.Atoi(toVersion) 252 | if err != nil || toVersionInt < 0 { 253 | logErr(err).Fatal("Unable to parse param ") 254 | } 255 | 256 | log.Infof("Migrating to version %d", toVersionInt) 257 | 258 | migrate, mctx, cancel := newMigrateWithCtx(ctx.String("url"), ctx.String("path")) 259 | defer cancel() 260 | currentVersion, err := migrate.Version(mctx) 261 | if err != nil { 262 | logErr(err).Fatalf("failed to migrate to version %d", toVersionInt) 263 | } 264 | 265 | relativeNInt := toVersionInt - int(currentVersion) 266 | 267 | err = migrate.Migrate(mctx, relativeNInt) 268 | if err != nil { 269 | logErr(err).Fatalf("Failed to migrate to vefrsion %d", toVersionInt) 270 | } 271 | logCurrentVersion(mctx, migrate) 272 | return nil 273 | }, 274 | } 275 | 276 | func newMigrateWithCtx(url, migrationsPath string) (*migrate.Handle, context.Context, func()) { 277 | done := make(chan struct{}) 278 | m, err := migrate.Open(url, migrationsPath, migrate.WithHooks( 279 | func(f file.File) error { 280 | log.Infof("Applying %s migration for version %d (%s)", f.Direction, f.Version, f.Name) 281 | return nil 282 | }, 283 | func(f file.File) error { 284 | done <- struct{}{} 285 | return nil 286 | }, 287 | )) 288 | if err != nil { 289 | log.Fatalf("Initialization failed: %s", err) 290 | } 291 | ctx, cancel := newOsInterruptCtx(done) 292 | return m, ctx, cancel 293 | } 294 | 295 | // newOsInterruptCtx returns new context that will be cancelled 296 | // on os.Interrupt signal. 297 | func newOsInterruptCtx(done <-chan struct{}) (context.Context, func()) { 298 | ctx, cancel := context.WithCancel(context.Background()) 299 | c := make(chan os.Signal, 1) 300 | signal.Notify(c, os.Interrupt) 301 | go func() { 302 | exit := false 303 | for loop := true; loop; { 304 | select { 305 | case <-done: 306 | if exit { 307 | loop = false 308 | } 309 | case <-c: 310 | if exit { 311 | os.Exit(5) 312 | } 313 | cancel() 314 | exit = true 315 | log.Info("Aborting after this migration... Hit again to force quit.") 316 | } 317 | } 318 | signal.Stop(c) 319 | }() 320 | return ctx, cancel 321 | } 322 | 323 | func logErr(err error) *log.Entry { 324 | return log.WithError(err) 325 | } 326 | 327 | func logCurrentVersion(ctx context.Context, migrate *migrate.Handle) { 328 | version, err := migrate.Version(ctx) 329 | if err != nil { 330 | logErr(err).Fatal("Unable to fetch version") 331 | } 332 | log.WithField("current-version", version).Info("done") 333 | } 334 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/db-journey/journey/v2 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 7 | github.com/db-journey/cronjobs v1.0.6 8 | github.com/db-journey/migrate/v2 v2.1.1 9 | github.com/go-sql-driver/mysql v1.5.0 // indirect 10 | github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 // indirect 11 | github.com/golang/snappy v0.0.1 // indirect 12 | github.com/sirupsen/logrus v1.4.2 13 | github.com/urfave/cli/v2 v2.1.1 14 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= 3 | github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 4 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 5 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 6 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 7 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 8 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/db-journey/cronjobs v1.0.6 h1:+SFke9zD0KHIiymOcgQNuibwq0Uu5Fb2BF4CuDEA7p0= 14 | github.com/db-journey/cronjobs v1.0.6/go.mod h1:fGuahdozdj8WAVkUX0UomwLINqGngxMzFhj7LnCcFWI= 15 | github.com/db-journey/migrate/v2 v2.1.1 h1:t/o9cr7C2vX+K336eWvSkBZgMVtwEh1W7iWDAw490OY= 16 | github.com/db-journey/migrate/v2 v2.1.1/go.mod h1:JfZ8ZmVh0qARaybK/Y4gSZyev1PPyp4tqZzFuAqxymA= 17 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 18 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 19 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 20 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 21 | github.com/gocql/gocql v0.0.0-20190910075112-d63913db787c h1:c8axRoOul5Nf80UpwiLUiEC0s0fW4KFi4V7jYNdi/wQ= 22 | github.com/gocql/gocql v0.0.0-20190910075112-d63913db787c/go.mod h1:Q7Sru5153KG8D9zwueuQJB3ccJf9/bIwF/x8b3oKgT8= 23 | github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 h1:ZZVxQRCm4ewuoqqLBJ7LHpsk4OGx2PkyCsRKLq4oHgE= 24 | github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= 25 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk= 27 | github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 28 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 29 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 30 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= 31 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= 32 | github.com/herenow/go-crate v0.0.0-20190617151714-6f2215a33eca h1:kk1qCxy+FS5McLJ69dSpB6Y6kHCMa23UwHyglIzJ/bk= 33 | github.com/herenow/go-crate v0.0.0-20190617151714-6f2215a33eca/go.mod h1:gOwMnP1ahomuqS3xIzrYZ/lmEIpxSilOsnhoSA9iudA= 34 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 35 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 36 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 37 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 38 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 39 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 40 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 41 | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 42 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 43 | github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= 44 | github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 45 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 46 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 47 | github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E= 48 | github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 49 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 50 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 51 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 52 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 53 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 54 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 55 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 56 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 57 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 58 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 59 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 60 | github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= 61 | github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= 62 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 63 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 64 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= 66 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= 68 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 70 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 71 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 72 | google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= 73 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 74 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 75 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 76 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 77 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 78 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | journey "github.com/db-journey/journey/v2/commands" 8 | _ "github.com/db-journey/migrate/v2/drivers/bash-driver" 9 | _ "github.com/db-journey/migrate/v2/drivers/cassandra-driver" 10 | _ "github.com/db-journey/migrate/v2/drivers/crate-driver" 11 | _ "github.com/db-journey/migrate/v2/drivers/mysql-driver" 12 | _ "github.com/db-journey/migrate/v2/drivers/postgresql-driver" 13 | _ "github.com/db-journey/migrate/v2/drivers/sqlite3-driver" 14 | "github.com/urfave/cli/v2" 15 | ) 16 | 17 | func main() { 18 | app := App() 19 | if err := app.Run(os.Args); err != nil { 20 | log.Fatal(err) 21 | } 22 | } 23 | 24 | func App() *cli.App { 25 | app := cli.NewApp() 26 | app.Usage = "Migrations and cronjobs for databases" 27 | app.Version = "2.2.5" 28 | 29 | app.Flags = journey.Flags() 30 | 31 | app.Commands = journey.Commands() 32 | return app 33 | } 34 | --------------------------------------------------------------------------------