mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
add rockhopper
This commit is contained in:
parent
8af89160e3
commit
2699c32b38
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
/.mod
|
||||||
|
|
||||||
/.env.local
|
/.env.local
|
||||||
/.env.*.local
|
/.env.*.local
|
||||||
|
|
29
go.mod
29
go.mod
|
@ -7,7 +7,9 @@ go 1.13
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/adshao/go-binance/v2 v2.2.1-0.20210108025425-9a582c63144e
|
github.com/adshao/go-binance/v2 v2.2.1-0.20210108025425-9a582c63144e
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
|
||||||
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b
|
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b
|
||||||
|
github.com/c9s/rockhopper v1.0.0 // indirect
|
||||||
github.com/codingconcepts/env v0.0.0-20200821220118-a8fbf8d84482
|
github.com/codingconcepts/env v0.0.0-20200821220118-a8fbf8d84482
|
||||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
||||||
github.com/go-redis/redis/v8 v8.4.0
|
github.com/go-redis/redis/v8 v8.4.0
|
||||||
|
@ -21,27 +23,42 @@ require (
|
||||||
github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4
|
github.com/leekchan/accounting v0.0.0-20191218023648-17a4ce5f94d4
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.2.0+incompatible
|
github.com/lestrrat-go/file-rotatelogs v2.2.0+incompatible
|
||||||
github.com/lestrrat-go/strftime v1.0.0 // indirect
|
github.com/lestrrat-go/strftime v1.0.0 // indirect
|
||||||
|
github.com/lib/pq v1.9.0
|
||||||
|
github.com/magiconair/properties v1.8.4 // indirect
|
||||||
github.com/markbates/pkger v0.17.1 // indirect
|
github.com/markbates/pkger v0.17.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.3.0
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||||
github.com/robfig/cron/v3 v3.0.0
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b
|
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/afero v1.5.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.3
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/viper v1.7.0
|
github.com/spf13/cobra v1.1.1
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
github.com/tebeka/strftime v0.1.3 // indirect
|
github.com/tebeka/strftime v0.1.3 // indirect
|
||||||
|
github.com/ugorji/go v1.1.4 // indirect
|
||||||
github.com/valyala/fastjson v1.5.1
|
github.com/valyala/fastjson v1.5.1
|
||||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2
|
github.com/x-cray/logrus-prefixed-formatter v0.5.2
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
|
||||||
|
github.com/ziutek/mymysql v1.5.4
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210113131315-ba0562f347e0 // indirect
|
||||||
|
golang.org/x/text v0.3.5 // indirect
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
gonum.org/v1/gonum v0.8.1
|
gonum.org/v1/gonum v0.8.1
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/tucnak/telebot.v2 v2.3.5
|
gopkg.in/tucnak/telebot.v2 v2.3.5
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
|
50
go.sum
50
go.sum
|
@ -43,6 +43,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b h1:4qsZTw8wHHTzFnwrfs3zLwz+cU2diGBdwoKRKiWOMvc=
|
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b h1:4qsZTw8wHHTzFnwrfs3zLwz+cU2diGBdwoKRKiWOMvc=
|
||||||
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b/go.mod h1:RaBe6PIVbQRqwrnjjSoHhlLM601JWdT7KZ0p6rhgI7I=
|
github.com/c9s/goose v0.0.0-20200415105707-8da682162a5b/go.mod h1:RaBe6PIVbQRqwrnjjSoHhlLM601JWdT7KZ0p6rhgI7I=
|
||||||
|
github.com/c9s/rockhopper v1.0.0 h1:IQnLETldYLhUtFhtahnb1WlYMkJQtpSWo8vdw13I7VE=
|
||||||
|
github.com/c9s/rockhopper v1.0.0/go.mod h1:FnLRi5pmIdXj+K+oxL/ocLKc74xMk+hdD5W3GQW6qJQ=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
|
@ -64,6 +66,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
@ -96,6 +100,7 @@ github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
|
||||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
@ -181,6 +186,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
@ -199,9 +205,13 @@ github.com/lestrrat-go/strftime v1.0.0/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR7
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||||
|
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||||
|
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
|
github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
|
||||||
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
@ -213,11 +223,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/Dav/9Z58LGpk7U=
|
github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/Dav/9Z58LGpk7U=
|
||||||
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||||
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
@ -228,6 +243,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
@ -248,10 +265,13 @@ github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDs
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||||
|
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
@ -284,6 +304,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b h1:4NIpokK7Rg/k6lSzNQzvGLphpHtfAAaLw9AWHxHQn0w=
|
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b h1:4NIpokK7Rg/k6lSzNQzvGLphpHtfAAaLw9AWHxHQn0w=
|
||||||
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
|
github.com/slack-go/slack v0.6.6-0.20200602212211-b04b8521281b/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
@ -294,17 +316,28 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
|
||||||
|
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
|
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||||
|
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
@ -327,6 +360,7 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJ
|
||||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
@ -342,8 +376,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -414,18 +451,24 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210113131315-ba0562f347e0 h1:rTJ72jiMIolMfCaiZLkdlJLN2Og7LKF6xE1cgwQnwzQ=
|
||||||
|
golang.org/x/sys v0.0.0-20210113131315-ba0562f347e0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||||
|
@ -500,6 +543,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||||
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
@ -511,12 +556,17 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-- +goose Up
|
-- +up
|
||||||
CREATE TABLE `trades` (
|
CREATE TABLE `trades` (
|
||||||
`gid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
`gid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,15 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/datatype"
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PnLReporterConfig struct {
|
type PnLReporterConfig struct {
|
||||||
AverageCostBySymbols StringSlice `json:"averageCostBySymbols" yaml:"averageCostBySymbols"`
|
AverageCostBySymbols datatype.StringSlice `json:"averageCostBySymbols" yaml:"averageCostBySymbols"`
|
||||||
Of StringSlice `json:"of" yaml:"of"`
|
Of datatype.StringSlice `json:"of" yaml:"of"`
|
||||||
When StringSlice `json:"when" yaml:"when"`
|
When datatype.StringSlice `json:"when" yaml:"when"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExchangeStrategyMount wraps the SingleExchangeStrategy with the Session name for mounting
|
// ExchangeStrategyMount wraps the SingleExchangeStrategy with the Session name for mounting
|
||||||
|
@ -105,7 +106,7 @@ type RedisPersistenceConfig struct {
|
||||||
Host string `json:"host" env:"REDIS_HOST"`
|
Host string `json:"host" env:"REDIS_HOST"`
|
||||||
Port string `json:"port" env:"REDIS_PORT"`
|
Port string `json:"port" env:"REDIS_PORT"`
|
||||||
Password string `json:"password" env:"REDIS_PASSWORD"`
|
Password string `json:"password" env:"REDIS_PASSWORD"`
|
||||||
DB int `json:"db" env:"REDIS_DB"`
|
DB int `json:"db" env:"REDIS_DB"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonPersistenceConfig struct {
|
type JsonPersistenceConfig struct {
|
||||||
|
@ -189,7 +190,6 @@ func Load(configFile string, loadStrategies bool) (*Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,2 @@
|
||||||
package bbgo
|
package bbgo
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StringSlice []string
|
|
||||||
|
|
||||||
func (s *StringSlice) decode(a interface{}) error {
|
|
||||||
switch d := a.(type) {
|
|
||||||
case string:
|
|
||||||
*s = append(*s, d)
|
|
||||||
|
|
||||||
case []string:
|
|
||||||
*s = append(*s, d...)
|
|
||||||
|
|
||||||
case []interface{}:
|
|
||||||
for _, de := range d {
|
|
||||||
if err := s.decode(de); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected type %T for StringSlice: %+v", d, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error) (err error) {
|
|
||||||
var ss []string
|
|
||||||
err = unmarshal(&ss)
|
|
||||||
if err == nil {
|
|
||||||
*s = ss
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var as string
|
|
||||||
err = unmarshal(&as)
|
|
||||||
if err == nil {
|
|
||||||
*s = append(*s, as)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StringSlice) UnmarshalJSON(b []byte) error {
|
|
||||||
var a interface{}
|
|
||||||
var err = json.Unmarshal(b, &a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.decode(a)
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,8 +6,9 @@ import (
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/buyandhold"
|
_ "github.com/c9s/bbgo/pkg/strategy/buyandhold"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
_ "github.com/c9s/bbgo/pkg/strategy/flashcrash"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
_ "github.com/c9s/bbgo/pkg/strategy/grid"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/trailingstop"
|
_ "github.com/c9s/bbgo/pkg/strategy/mirrormaker"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
_ "github.com/c9s/bbgo/pkg/strategy/pricealert"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/swing"
|
_ "github.com/c9s/bbgo/pkg/strategy/swing"
|
||||||
|
_ "github.com/c9s/bbgo/pkg/strategy/trailingstop"
|
||||||
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"
|
_ "github.com/c9s/bbgo/pkg/strategy/xpuremaker"
|
||||||
)
|
)
|
||||||
|
|
143
pkg/cmd/run.go
143
pkg/cmd/run.go
|
@ -44,6 +44,15 @@ func init() {
|
||||||
RootCmd.AddCommand(RunCmd)
|
RootCmd.AddCommand(RunCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var RunCmd = &cobra.Command{
|
||||||
|
Use: "run",
|
||||||
|
Short: "run strategies from config file",
|
||||||
|
|
||||||
|
// SilenceUsage is an option to silence usage when an error occurs.
|
||||||
|
SilenceUsage: true,
|
||||||
|
RunE: run,
|
||||||
|
}
|
||||||
|
|
||||||
var wrapperTemplate = template.Must(template.New("main").Parse(`package main
|
var wrapperTemplate = template.Must(template.New("main").Parse(`package main
|
||||||
// DO NOT MODIFY THIS FILE. THIS FILE IS GENERATED FOR IMPORTING STRATEGIES
|
// DO NOT MODIFY THIS FILE. THIS FILE IS GENERATED FOR IMPORTING STRATEGIES
|
||||||
import (
|
import (
|
||||||
|
@ -253,87 +262,79 @@ func runConfig(basectx context.Context, userConfig *bbgo.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var RunCmd = &cobra.Command{
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
Use: "run",
|
configFile, err := cmd.Flags().GetString("config")
|
||||||
Short: "run strategies from config file",
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// SilenceUsage is an option to silence usage when an error occurs.
|
if len(configFile) == 0 {
|
||||||
SilenceUsage: true,
|
return errors.New("--config option is required")
|
||||||
|
}
|
||||||
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
noCompile, err := cmd.Flags().GetBool("no-compile")
|
||||||
configFile, err := cmd.Flags().GetString("config")
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
userConfig, err := bbgo.Load(configFile, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldCompile := len(userConfig.Imports) > 0
|
||||||
|
|
||||||
|
// if there is no custom imports, we don't have to compile
|
||||||
|
if noCompile || !shouldCompile {
|
||||||
|
userConfig, err = bbgo.Load(configFile, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(configFile) == 0 {
|
log.Infof("running config without wrapper binary...")
|
||||||
return errors.New("--config option is required")
|
if err := runConfig(ctx, userConfig); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
noCompile, err := cmd.Flags().GetBool("no-compile")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
userConfig, err := bbgo.Load(configFile, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldCompile := len(userConfig.Imports) > 0
|
|
||||||
|
|
||||||
// if there is no custom imports, we don't have to compile
|
|
||||||
if noCompile || !shouldCompile {
|
|
||||||
userConfig, err = bbgo.Load(configFile, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("running config without wrapper binary...")
|
|
||||||
if err := runConfig(ctx, userConfig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var runArgs = []string{"run", "--no-compile"}
|
|
||||||
cmd.Flags().Visit(func(flag *flag.Flag) {
|
|
||||||
runArgs = append(runArgs, "--"+flag.Name, flag.Value.String())
|
|
||||||
})
|
|
||||||
runArgs = append(runArgs, args...)
|
|
||||||
|
|
||||||
goOS, err := cmd.Flags().GetString("os")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
goArch, err := cmd.Flags().GetString("arch")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runCmd, err := buildAndRun(ctx, userConfig, goOS, goArch, runArgs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sig := cmdutil.WaitForSignal(ctx, syscall.SIGTERM, syscall.SIGINT); sig != nil {
|
|
||||||
log.Infof("sending signal to the child process...")
|
|
||||||
if err := runCmd.Process.Signal(sig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runCmd.Wait(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
}
|
||||||
|
|
||||||
|
var runArgs = []string{"run", "--no-compile"}
|
||||||
|
cmd.Flags().Visit(func(flag *flag.Flag) {
|
||||||
|
runArgs = append(runArgs, "--"+flag.Name, flag.Value.String())
|
||||||
|
})
|
||||||
|
runArgs = append(runArgs, args...)
|
||||||
|
|
||||||
|
goOS, err := cmd.Flags().GetString("os")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
goArch, err := cmd.Flags().GetString("arch")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
runCmd, err := buildAndRun(ctx, userConfig, goOS, goArch, runArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig := cmdutil.WaitForSignal(ctx, syscall.SIGTERM, syscall.SIGINT); sig != nil {
|
||||||
|
log.Infof("sending signal to the child process...")
|
||||||
|
if err := runCmd.Process.Signal(sig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runCmd.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compile(buildDir string, userConfig *bbgo.Config) error {
|
func compile(buildDir string, userConfig *bbgo.Config) error {
|
||||||
|
|
57
pkg/datatype/string_slice.go
Normal file
57
pkg/datatype/string_slice.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package datatype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StringSlice []string
|
||||||
|
|
||||||
|
func (s *StringSlice) decode(a interface{}) error {
|
||||||
|
switch d := a.(type) {
|
||||||
|
case string:
|
||||||
|
*s = append(*s, d)
|
||||||
|
|
||||||
|
case []string:
|
||||||
|
*s = append(*s, d...)
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
for _, de := range d {
|
||||||
|
if err := s.decode(de); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected type %T for StringSlice: %+v", d, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error) (err error) {
|
||||||
|
var ss []string
|
||||||
|
err = unmarshal(&ss)
|
||||||
|
if err == nil {
|
||||||
|
*s = ss
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var as string
|
||||||
|
err = unmarshal(&as)
|
||||||
|
if err == nil {
|
||||||
|
*s = append(*s, as)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StringSlice) UnmarshalJSON(b []byte) error {
|
||||||
|
var a interface{}
|
||||||
|
var err = json.Unmarshal(b, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.decode(a)
|
||||||
|
}
|
5
pkg/migration/cmd/gopackmigration/main.go
Normal file
5
pkg/migration/cmd/gopackmigration/main.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
}
|
285
pkg/migration/dialect.go
Normal file
285
pkg/migration/dialect.go
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SQLDialect abstracts the details of specific SQL dialects
|
||||||
|
// for goose's few SQL specific statements
|
||||||
|
type SQLDialect interface {
|
||||||
|
createVersionTableSQL() string // sql string to create the db version table
|
||||||
|
insertVersionSQL() string // sql string to insert the initial version table row
|
||||||
|
deleteVersionSQL() string // sql string to delete version
|
||||||
|
migrationSQL() string // sql string to retrieve migrations
|
||||||
|
dbVersionQuery(db *sql.DB) (*sql.Rows, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialect SQLDialect = &PostgresDialect{}
|
||||||
|
|
||||||
|
// GetDialect gets the SQLDialect
|
||||||
|
func GetDialect() SQLDialect {
|
||||||
|
return dialect
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDialect sets the SQLDialect
|
||||||
|
func SetDialect(d string) error {
|
||||||
|
switch d {
|
||||||
|
case "postgres":
|
||||||
|
dialect = &PostgresDialect{}
|
||||||
|
case "mysql":
|
||||||
|
dialect = &MySQLDialect{}
|
||||||
|
case "sqlite3":
|
||||||
|
dialect = &Sqlite3Dialect{}
|
||||||
|
case "mssql":
|
||||||
|
dialect = &SqlServerDialect{}
|
||||||
|
case "redshift":
|
||||||
|
dialect = &RedshiftDialect{}
|
||||||
|
case "tidb":
|
||||||
|
dialect = &TiDBDialect{}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%q: unknown dialect", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// Postgres
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// PostgresDialect struct.
|
||||||
|
type PostgresDialect struct{}
|
||||||
|
|
||||||
|
func (pg PostgresDialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id serial NOT NULL,
|
||||||
|
version_id bigint NOT NULL,
|
||||||
|
is_applied boolean NOT NULL,
|
||||||
|
tstamp timestamp NULL default now(),
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg PostgresDialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES ($1, $2);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg PostgresDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m PostgresDialect) migrationSQL() string {
|
||||||
|
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=$1 ORDER BY tstamp DESC LIMIT 1", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg PostgresDialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=$1;", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// MySQL
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// MySQLDialect struct.
|
||||||
|
type MySQLDialect struct{}
|
||||||
|
|
||||||
|
func (m MySQLDialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id serial NOT NULL,
|
||||||
|
version_id bigint NOT NULL,
|
||||||
|
is_applied boolean NOT NULL,
|
||||||
|
tstamp timestamp NULL default now(),
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MySQLDialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MySQLDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MySQLDialect) migrationSQL() string {
|
||||||
|
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MySQLDialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// MSSQL
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// SqlServerDialect struct.
|
||||||
|
type SqlServerDialect struct{}
|
||||||
|
|
||||||
|
func (m SqlServerDialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
|
||||||
|
version_id BIGINT NOT NULL,
|
||||||
|
is_applied BIT NOT NULL,
|
||||||
|
tstamp DATETIME NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SqlServerDialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (@p1, @p2);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SqlServerDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied FROM %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SqlServerDialect) migrationSQL() string {
|
||||||
|
const tpl = `
|
||||||
|
WITH Migrations AS
|
||||||
|
(
|
||||||
|
SELECT tstamp, is_applied,
|
||||||
|
ROW_NUMBER() OVER (ORDER BY tstamp) AS 'RowNumber'
|
||||||
|
FROM %s
|
||||||
|
WHERE version_id=@p1
|
||||||
|
)
|
||||||
|
SELECT tstamp, is_applied
|
||||||
|
FROM Migrations
|
||||||
|
WHERE RowNumber BETWEEN 1 AND 2
|
||||||
|
ORDER BY tstamp DESC
|
||||||
|
`
|
||||||
|
return fmt.Sprintf(tpl, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SqlServerDialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=@p1;", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// sqlite3
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// Sqlite3Dialect struct.
|
||||||
|
type Sqlite3Dialect struct{}
|
||||||
|
|
||||||
|
func (m Sqlite3Dialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
version_id INTEGER NOT NULL,
|
||||||
|
is_applied INTEGER NOT NULL,
|
||||||
|
tstamp TIMESTAMP DEFAULT (datetime('now'))
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Sqlite3Dialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Sqlite3Dialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Sqlite3Dialect) migrationSQL() string {
|
||||||
|
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Sqlite3Dialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// Redshift
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// RedshiftDialect struct.
|
||||||
|
type RedshiftDialect struct{}
|
||||||
|
|
||||||
|
func (rs RedshiftDialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id integer NOT NULL identity(1, 1),
|
||||||
|
version_id bigint NOT NULL,
|
||||||
|
is_applied boolean NOT NULL,
|
||||||
|
tstamp timestamp NULL default sysdate,
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs RedshiftDialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES ($1, $2);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs RedshiftDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RedshiftDialect) migrationSQL() string {
|
||||||
|
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=$1 ORDER BY tstamp DESC LIMIT 1", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs RedshiftDialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=$1;", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// TiDB
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
// TiDBDialect struct.
|
||||||
|
type TiDBDialect struct{}
|
||||||
|
|
||||||
|
func (m TiDBDialect) createVersionTableSQL() string {
|
||||||
|
return fmt.Sprintf(`CREATE TABLE %s (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||||
|
version_id bigint NOT NULL,
|
||||||
|
is_applied boolean NOT NULL,
|
||||||
|
tstamp timestamp NULL default now(),
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);`, TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TiDBDialect) insertVersionSQL() string {
|
||||||
|
return fmt.Sprintf("INSERT INTO %s (version_id, is_applied) VALUES (?, ?);", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TiDBDialect) dbVersionQuery(db *sql.DB) (*sql.Rows, error) {
|
||||||
|
rows, err := db.Query(fmt.Sprintf("SELECT version_id, is_applied from %s ORDER BY id DESC", TableName()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TiDBDialect) migrationSQL() string {
|
||||||
|
return fmt.Sprintf("SELECT tstamp, is_applied FROM %s WHERE version_id=? ORDER BY tstamp DESC LIMIT 1", TableName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TiDBDialect) deleteVersionSQL() string {
|
||||||
|
return fmt.Sprintf("DELETE FROM %s WHERE version_id=?;", TableName())
|
||||||
|
}
|
||||||
|
|
492
pkg/migration/migrate.go
Normal file
492
pkg/migration/migrate.go
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const VERSION = "v2.7.0-rc3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
duplicateCheckOnce sync.Once
|
||||||
|
minVersion = int64(0)
|
||||||
|
maxVersion = int64((1 << 63) - 1)
|
||||||
|
timestampFormat = "20060102150405"
|
||||||
|
verbose = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetVerbose set the goose verbosity mode
|
||||||
|
func SetVerbose(v bool) {
|
||||||
|
verbose = v
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoCurrentVersion when a current migration version is not found.
|
||||||
|
ErrNoCurrentVersion = errors.New("no current version found")
|
||||||
|
// ErrNoNextVersion when the next migration version is not found.
|
||||||
|
ErrNoNextVersion = errors.New("no next version found")
|
||||||
|
// MaxVersion is the maximum allowed version.
|
||||||
|
MaxVersion int64 = 9223372036854775807 // max(int64)
|
||||||
|
|
||||||
|
registeredGoMigrations = map[int64]*Migration{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MigrationRecord struct.
|
||||||
|
type MigrationRecord struct {
|
||||||
|
VersionID int64
|
||||||
|
TStamp time.Time
|
||||||
|
IsApplied bool // was this a result of up() or down()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration struct.
|
||||||
|
type Migration struct {
|
||||||
|
Version int64
|
||||||
|
Next int64 // next version, or -1 if none
|
||||||
|
Previous int64 // previous version, -1 if none
|
||||||
|
Source string // path to .sql script
|
||||||
|
Registered bool
|
||||||
|
UpFn func(*sql.Tx) error // Up go migration function
|
||||||
|
DownFn func(*sql.Tx) error // Down go migration function
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Migration) String() string {
|
||||||
|
return fmt.Sprintf(m.Source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up runs an up migration.
|
||||||
|
func (m *Migration) Up(ctx context.Context, db *sql.DB) error {
|
||||||
|
if err := m.run(ctx, db, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Down runs a down migration.
|
||||||
|
func (m *Migration) Down(ctx context.Context, db *sql.DB) error {
|
||||||
|
if err := m.run(ctx, db, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Migration) run(ctx context.Context, db *sql.DB, direction bool) error {
|
||||||
|
switch filepath.Ext(m.Source) {
|
||||||
|
case ".sql":
|
||||||
|
f, err := os.Open(m.Source)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ERROR %v: failed to open SQL migration file", filepath.Base(m.Source))
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
statements, useTx, err := parseSQLMigration(f, direction)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "ERROR %v: failed to parse SQL migration file", filepath.Base(m.Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runSQLMigration(ctx, db, statements, useTx, m.Version, direction); err != nil {
|
||||||
|
return errors.Wrapf(err, "ERROR %v: failed to run SQL migration", filepath.Base(m.Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(statements) > 0 {
|
||||||
|
log.Println("OK ", filepath.Base(m.Source))
|
||||||
|
} else {
|
||||||
|
log.Println("EMPTY", filepath.Base(m.Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
case ".go":
|
||||||
|
if !m.Registered {
|
||||||
|
return errors.Errorf("ERROR %v: failed to run Go migration: Go functions must be registered and built into a custom binary (see https://github.com/c9s/goose/tree/master/examples/go-migrations)", m.Source)
|
||||||
|
}
|
||||||
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "ERROR failed to begin transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := m.UpFn
|
||||||
|
if !direction {
|
||||||
|
fn = m.DownFn
|
||||||
|
}
|
||||||
|
|
||||||
|
if fn != nil {
|
||||||
|
// Run Go migration function.
|
||||||
|
if err := fn(tx); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrapf(err, "ERROR %v: failed to run Go migration function %T", filepath.Base(m.Source), fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if direction {
|
||||||
|
if _, err := tx.Exec(GetDialect().insertVersionSQL(), m.Version, direction); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrap(err, "ERROR failed to execute transaction")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), m.Version); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrap(err, "ERROR failed to execute transaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrap(err, "ERROR failed to commit transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fn != nil {
|
||||||
|
log.Println("OK ", filepath.Base(m.Source))
|
||||||
|
} else {
|
||||||
|
log.Println("EMPTY", filepath.Base(m.Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractNumericComponent looks for migration scripts with names in the form:
|
||||||
|
// XXX_descriptivename.ext where XXX specifies the version number
|
||||||
|
// and ext specifies the type of migration
|
||||||
|
func ExtractNumericComponent(name string) (int64, error) {
|
||||||
|
base := filepath.Base(name)
|
||||||
|
|
||||||
|
if ext := filepath.Ext(base); ext != ".go" && ext != ".sql" {
|
||||||
|
return 0, errors.New("not a recognized migration file type")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := strings.Index(base, "_")
|
||||||
|
if idx < 0 {
|
||||||
|
return 0, errors.New("no separator found")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, e := strconv.ParseInt(base[:idx], 10, 64)
|
||||||
|
if e == nil && n <= 0 {
|
||||||
|
return 0, errors.New("migration IDs must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrations slice.
|
||||||
|
type Migrations []*Migration
|
||||||
|
|
||||||
|
// helpers so we can use pkg sort
|
||||||
|
func (ms Migrations) Len() int { return len(ms) }
|
||||||
|
func (ms Migrations) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
|
||||||
|
func (ms Migrations) Less(i, j int) bool {
|
||||||
|
if ms[i].Version == ms[j].Version {
|
||||||
|
panic(fmt.Sprintf("goose: duplicate version %v detected:\n%v\n%v", ms[i].Version, ms[i].Source, ms[j].Source))
|
||||||
|
}
|
||||||
|
return ms[i].Version < ms[j].Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current gets the current migration.
|
||||||
|
func (ms Migrations) Current(current int64) (*Migration, error) {
|
||||||
|
for i, migration := range ms {
|
||||||
|
if migration.Version == current {
|
||||||
|
return ms[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNoCurrentVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next gets the next migration.
|
||||||
|
func (ms Migrations) Next(current int64) (*Migration, error) {
|
||||||
|
for i, migration := range ms {
|
||||||
|
if migration.Version > current {
|
||||||
|
return ms[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNoNextVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous : Get the previous migration.
|
||||||
|
func (ms Migrations) Previous(current int64) (*Migration, error) {
|
||||||
|
for i := len(ms) - 1; i >= 0; i-- {
|
||||||
|
if ms[i].Version < current {
|
||||||
|
return ms[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNoNextVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last gets the last migration.
|
||||||
|
func (ms Migrations) Last() (*Migration, error) {
|
||||||
|
if len(ms) == 0 {
|
||||||
|
return nil, ErrNoNextVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return ms[len(ms)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versioned gets versioned migrations.
|
||||||
|
func (ms Migrations) versioned() (Migrations, error) {
|
||||||
|
var migrations Migrations
|
||||||
|
|
||||||
|
// assume that the user will never have more than 19700101000000 migrations
|
||||||
|
for _, m := range ms {
|
||||||
|
// parse version as timestmap
|
||||||
|
versionTime, err := time.Parse(timestampFormat, fmt.Sprintf("%d", m.Version))
|
||||||
|
|
||||||
|
if versionTime.Before(time.Unix(0, 0)) || err != nil {
|
||||||
|
migrations = append(migrations, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timestamped gets the timestamped migrations.
|
||||||
|
func (ms Migrations) timestamped() (Migrations, error) {
|
||||||
|
var migrations Migrations
|
||||||
|
|
||||||
|
// assume that the user will never have more than 19700101000000 migrations
|
||||||
|
for _, m := range ms {
|
||||||
|
// parse version as timestmap
|
||||||
|
versionTime, err := time.Parse(timestampFormat, fmt.Sprintf("%d", m.Version))
|
||||||
|
if err != nil {
|
||||||
|
// probably not a timestamp
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionTime.After(time.Unix(0, 0)) {
|
||||||
|
migrations = append(migrations, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return migrations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms Migrations) String() string {
|
||||||
|
str := ""
|
||||||
|
for _, m := range ms {
|
||||||
|
str += fmt.Sprintln(m)
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMigration adds a migration.
|
||||||
|
func AddMigration(up func(*sql.Tx) error, down func(*sql.Tx) error) {
|
||||||
|
_, filename, _, _ := runtime.Caller(1)
|
||||||
|
AddNamedMigration(filename, up, down)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNamedMigration : Add a named migration.
|
||||||
|
func AddNamedMigration(filename string, up func(*sql.Tx) error, down func(*sql.Tx) error) {
|
||||||
|
v, _ := ExtractNumericComponent(filename)
|
||||||
|
migration := &Migration{Version: v, Next: -1, Previous: -1, Registered: true, UpFn: up, DownFn: down, Source: filename}
|
||||||
|
|
||||||
|
if existing, ok := registeredGoMigrations[v]; ok {
|
||||||
|
panic(fmt.Sprintf("failed to add migration %q: version conflicts with %q", filename, existing.Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
registeredGoMigrations[v] = migration
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectMigrationsFromDir returns all the valid looking migration scripts in the
|
||||||
|
// migrations folder and go func registry, and key them by version.
|
||||||
|
func CollectMigrationsFromDir(dirpath string, current, target int64) (Migrations, error) {
|
||||||
|
if _, err := os.Stat(dirpath); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("%s directory does not exists", dirpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var migrations Migrations
|
||||||
|
|
||||||
|
// SQL migration files.
|
||||||
|
sqlMigrationFiles, err := filepath.Glob(dirpath + "/**.sql")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range sqlMigrationFiles {
|
||||||
|
v, err := ExtractNumericComponent(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if versionFilter(v, current, target) {
|
||||||
|
migration := &Migration{Version: v, Next: -1, Previous: -1, Source: file}
|
||||||
|
migrations = append(migrations, migration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go migrations registered via goose.AddMigration().
|
||||||
|
for _, migration := range registeredGoMigrations {
|
||||||
|
v, err := ExtractNumericComponent(migration.Source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if versionFilter(v, current, target) {
|
||||||
|
migrations = append(migrations, migration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go migration files
|
||||||
|
goMigrationFiles, err := filepath.Glob(dirpath + "/**.go")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range goMigrationFiles {
|
||||||
|
v, err := ExtractNumericComponent(file)
|
||||||
|
if err != nil {
|
||||||
|
continue // Skip any files that don't have version prefix.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip migrations already existing migrations registered via goose.AddMigration().
|
||||||
|
if _, ok := registeredGoMigrations[v]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionFilter(v, current, target) {
|
||||||
|
migration := &Migration{Version: v, Next: -1, Previous: -1, Source: file, Registered: false}
|
||||||
|
migrations = append(migrations, migration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
migrations = sortAndConnectMigrations(migrations)
|
||||||
|
|
||||||
|
return migrations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortAndConnectMigrations(migrations Migrations) Migrations {
|
||||||
|
sort.Sort(migrations)
|
||||||
|
|
||||||
|
// now that we're sorted in the appropriate direction,
|
||||||
|
// populate next and previous for each migration
|
||||||
|
for i, m := range migrations {
|
||||||
|
prev := int64(-1)
|
||||||
|
if i > 0 {
|
||||||
|
prev = migrations[i-1].Version
|
||||||
|
migrations[i-1].Next = m.Version
|
||||||
|
}
|
||||||
|
migrations[i].Previous = prev
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrations
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionFilter(v, current, target int64) bool {
|
||||||
|
|
||||||
|
if target > current {
|
||||||
|
return v > current && v <= target
|
||||||
|
}
|
||||||
|
|
||||||
|
if target < current {
|
||||||
|
return v <= current && v > target
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create the db version table
|
||||||
|
// and insert the initial 0 value into it
|
||||||
|
func createVersionTable(db *sql.DB) error {
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := GetDialect()
|
||||||
|
|
||||||
|
if _, err := txn.Exec(d.createVersionTableSQL()); err != nil {
|
||||||
|
txn.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
version := 0
|
||||||
|
applied := true
|
||||||
|
if _, err := txn.Exec(d.insertVersionSQL(), version, applied); err != nil {
|
||||||
|
txn.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return txn.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Run a migration specified in raw SQL.
|
||||||
|
//
|
||||||
|
// Sections of the script can be annotated with a special comment,
|
||||||
|
// starting with "-- +goose" to specify whether the section should
|
||||||
|
// be applied during an Up or Down migration
|
||||||
|
//
|
||||||
|
// All statements following an Up or Down directive are grouped together
|
||||||
|
// until another direction directive is found.
|
||||||
|
func runSQLMigration(ctx context.Context, db *sql.DB, statements []string, useTx bool, v int64, direction bool) error {
|
||||||
|
if useTx {
|
||||||
|
// TRANSACTION.
|
||||||
|
|
||||||
|
verboseInfo("Begin transaction")
|
||||||
|
|
||||||
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to begin transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, query := range statements {
|
||||||
|
verboseInfo("Executing statement: %s\n", clearStatement(query))
|
||||||
|
if _, err = tx.Exec(query); err != nil {
|
||||||
|
verboseInfo("Rollback transaction")
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if direction {
|
||||||
|
if _, err := tx.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||||
|
verboseInfo("Rollback transaction")
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrap(err, "failed to insert new goose version")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := tx.Exec(GetDialect().deleteVersionSQL(), v); err != nil {
|
||||||
|
verboseInfo("Rollback transaction")
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.Wrap(err, "failed to delete goose version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verboseInfo("Commit transaction")
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to commit transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NO TRANSACTION.
|
||||||
|
for _, query := range statements {
|
||||||
|
verboseInfo("Executing statement: %s", clearStatement(query))
|
||||||
|
if _, err := db.Exec(query); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to execute SQL query %q", clearStatement(query))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(GetDialect().insertVersionSQL(), v, direction); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to insert new goose version")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
grayColor = "\033[90m"
|
||||||
|
resetColor = "\033[00m"
|
||||||
|
)
|
||||||
|
|
||||||
|
func verboseInfo(s string, args ...interface{}) {
|
||||||
|
log.Printf(grayColor+s+resetColor, args...)
|
||||||
|
}
|
||||||
|
|
5
pkg/migration/pack.go
Normal file
5
pkg/migration/pack.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
//go:generate gopackmigration -dir ../../migrations
|
||||||
|
|
||||||
|
|
13
pkg/migration/sql.go
Normal file
13
pkg/migration/sql.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
var (
|
||||||
|
matchSQLComments = regexp.MustCompile(`(?m)^--.*$[\r\n]*`)
|
||||||
|
matchEmptyEOL = regexp.MustCompile(`(?m)^$[\r\n]*`) // TODO: Duplicate
|
||||||
|
)
|
||||||
|
|
||||||
|
func clearStatement(s string) string {
|
||||||
|
s = matchSQLComments.ReplaceAllString(s, ``)
|
||||||
|
return matchEmptyEOL.ReplaceAllString(s, ``)
|
||||||
|
}
|
212
pkg/migration/sql_parser.go
Normal file
212
pkg/migration/sql_parser.go
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type parserState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
start parserState = iota // 0
|
||||||
|
upStatement // 1
|
||||||
|
upStatementBegin // 2
|
||||||
|
upStatementEnd // 3
|
||||||
|
downStatement // 4
|
||||||
|
downStatementBegin // 5
|
||||||
|
downStatementEnd // 6
|
||||||
|
)
|
||||||
|
|
||||||
|
type stateMachine parserState
|
||||||
|
|
||||||
|
func (s *stateMachine) Get() parserState {
|
||||||
|
return parserState(*s)
|
||||||
|
}
|
||||||
|
func (s *stateMachine) Set(new parserState) {
|
||||||
|
*s = stateMachine(new)
|
||||||
|
}
|
||||||
|
|
||||||
|
const scanBufSize = 4 * 1024 * 1024
|
||||||
|
|
||||||
|
var matchEmptyLines = regexp.MustCompile(`^\s*$`)
|
||||||
|
|
||||||
|
var bufferPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, scanBufSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split given SQL script into individual statements and return
|
||||||
|
// SQL statements for given direction (up=true, down=false).
|
||||||
|
//
|
||||||
|
// The base case is to simply split on semicolons, as these
|
||||||
|
// naturally terminate a statement.
|
||||||
|
//
|
||||||
|
// However, more complex cases like pl/pgsql can have semicolons
|
||||||
|
// within a statement. For these cases, we provide the explicit annotations
|
||||||
|
// 'StatementBegin' and 'StatementEnd' to allow the script to
|
||||||
|
// tell us to ignore semicolons.
|
||||||
|
func parseSQLMigration(r io.Reader, direction bool) (stmts []string, useTx bool, err error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
scanBuf := bufferPool.Get().([]byte)
|
||||||
|
defer bufferPool.Put(scanBuf)
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
scanner.Buffer(scanBuf, scanBufSize)
|
||||||
|
|
||||||
|
stateMachine := stateMachine(start)
|
||||||
|
useTx = true
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "--") {
|
||||||
|
cmd := strings.TrimSpace(strings.TrimPrefix(line, "--"))
|
||||||
|
|
||||||
|
switch cmd {
|
||||||
|
case "+goose Up":
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case start:
|
||||||
|
stateMachine.Set(upStatement)
|
||||||
|
default:
|
||||||
|
return nil, false, errors.Errorf("duplicate '-- +goose Up' annotations; stateMachine=%v, see https://github.com/c9s/goose#sql-migrations", stateMachine)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "+goose Down":
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case upStatement, upStatementEnd:
|
||||||
|
stateMachine.Set(downStatement)
|
||||||
|
default:
|
||||||
|
return nil, false, errors.Errorf("must start with '-- +goose Up' annotation, stateMachine=%v, see https://github.com/c9s/goose#sql-migrations", stateMachine)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "+goose StatementBegin":
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case upStatement, upStatementEnd:
|
||||||
|
stateMachine.Set(upStatementBegin)
|
||||||
|
case downStatement, downStatementEnd:
|
||||||
|
stateMachine.Set(downStatementBegin)
|
||||||
|
default:
|
||||||
|
return nil, false, errors.Errorf("'-- +goose StatementBegin' must be defined after '-- +goose Up' or '-- +goose Down' annotation, stateMachine=%v, see https://github.com/c9s/goose#sql-migrations", stateMachine)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "+goose StatementEnd":
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case upStatementBegin:
|
||||||
|
stateMachine.Set(upStatementEnd)
|
||||||
|
case downStatementBegin:
|
||||||
|
stateMachine.Set(downStatementEnd)
|
||||||
|
default:
|
||||||
|
return nil, false, errors.New("'-- +goose StatementEnd' must be defined after '-- +goose StatementBegin', see https://github.com/c9s/goose#sql-migrations")
|
||||||
|
}
|
||||||
|
|
||||||
|
case "+goose NO TRANSACTION":
|
||||||
|
useTx = false
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Ignore comments.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore empty lines.
|
||||||
|
if matchEmptyLines.MatchString(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write SQL line to a buffer.
|
||||||
|
if _, err := buf.WriteString(line + "\n"); err != nil {
|
||||||
|
return nil, false, errors.Wrap(err, "failed to write to buf")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read SQL body one by line, if we're in the right direction.
|
||||||
|
//
|
||||||
|
// 1) basic query with semicolon; 2) psql statement
|
||||||
|
//
|
||||||
|
// Export statement once we hit end of statement.
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case upStatement, upStatementBegin, upStatementEnd:
|
||||||
|
if !direction /*down*/ {
|
||||||
|
buf.Reset()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case downStatement, downStatementBegin, downStatementEnd:
|
||||||
|
if direction /*up*/ {
|
||||||
|
buf.Reset()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, false, errors.Errorf("failed to parse migration: unexpected state %q on line %q, see https://github.com/c9s/goose#sql-migrations", stateMachine, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case upStatement:
|
||||||
|
if endsWithSemicolon(line) {
|
||||||
|
stmts = append(stmts, buf.String())
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
case downStatement:
|
||||||
|
if endsWithSemicolon(line) {
|
||||||
|
stmts = append(stmts, buf.String())
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
case upStatementEnd:
|
||||||
|
stmts = append(stmts, buf.String())
|
||||||
|
buf.Reset()
|
||||||
|
stateMachine.Set(upStatement)
|
||||||
|
case downStatementEnd:
|
||||||
|
stmts = append(stmts, buf.String())
|
||||||
|
buf.Reset()
|
||||||
|
stateMachine.Set(downStatement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, false, errors.Wrap(err, "failed to scan migration")
|
||||||
|
}
|
||||||
|
// EOF
|
||||||
|
|
||||||
|
switch stateMachine.Get() {
|
||||||
|
case start:
|
||||||
|
return nil, false, errors.New("failed to parse migration: must start with '-- +goose Up' annotation, see https://github.com/c9s/goose#sql-migrations")
|
||||||
|
case upStatementBegin, downStatementBegin:
|
||||||
|
return nil, false, errors.New("failed to parse migration: missing '-- +goose StatementEnd' annotation")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bufferRemaining := strings.TrimSpace(buf.String()); len(bufferRemaining) > 0 {
|
||||||
|
return nil, false, errors.Errorf("failed to parse migration: state %q, direction: %v: unexpected unfinished SQL query: %q: missing semicolon?", stateMachine, direction, bufferRemaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stmts, useTx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the line to see if the line has a statement-ending semicolon
|
||||||
|
// or if the line contains a double-dash comment.
|
||||||
|
func endsWithSemicolon(line string) bool {
|
||||||
|
scanBuf := bufferPool.Get().([]byte)
|
||||||
|
defer bufferPool.Put(scanBuf)
|
||||||
|
|
||||||
|
prev := ""
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(line))
|
||||||
|
scanner.Buffer(scanBuf, scanBufSize)
|
||||||
|
scanner.Split(bufio.ScanWords)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
word := scanner.Text()
|
||||||
|
if strings.HasPrefix(word, "--") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
prev = word
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasSuffix(prev, ";")
|
||||||
|
}
|
||||||
|
|
68
pkg/migration/up.go
Normal file
68
pkg/migration/up.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpTo migrates up to a specific version.
|
||||||
|
func UpTo(ctx context.Context, db *sql.DB, dir string, version int64) error {
|
||||||
|
migrations, err := CollectMigrationsFromDir(dir, minVersion, version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
current, err := GetDBVersion(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
next, err := migrations.Next(current)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrNoNextVersion {
|
||||||
|
log.Printf("no migrations to run. current version: %d\n", current)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = next.Up(ctx, db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up applies all available migrations.
|
||||||
|
func Up(ctx context.Context, db *sql.DB, dir string) error {
|
||||||
|
return UpTo(ctx, db, dir, maxVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpByOne migrates up by a single version.
|
||||||
|
func UpByOne(ctx context.Context, db *sql.DB, dir string) error {
|
||||||
|
migrations, err := CollectMigrationsFromDir(dir, minVersion, maxVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVersion, err := GetDBVersion(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
next, err := migrations.Next(currentVersion)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrNoNextVersion {
|
||||||
|
log.Printf("no migrations to run. current version: %d\n", currentVersion)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = next.Up(ctx, db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
91
pkg/migration/version.go
Normal file
91
pkg/migration/version.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version prints the current version of the database.
|
||||||
|
func Version(db *sql.DB, dir string) error {
|
||||||
|
current, err := GetDBVersion(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("cmd: version %v\n", current)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDBVersion is an alias for EnsureDBVersion, but returns -1 in error.
|
||||||
|
func GetDBVersion(db *sql.DB) (int64, error) {
|
||||||
|
version, err := EnsureDBVersion(db)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureDBVersion retrieves the current version for this DB.
|
||||||
|
// Create and initialize the DB version table if it doesn't exist.
|
||||||
|
func EnsureDBVersion(db *sql.DB) (int64, error) {
|
||||||
|
rows, err := GetDialect().dbVersionQuery(db)
|
||||||
|
if err != nil {
|
||||||
|
return 0, createVersionTable(db)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// The most recent record for each migration specifies
|
||||||
|
// whether it has been applied or rolled back.
|
||||||
|
// The first version we find that has been applied is the current version.
|
||||||
|
|
||||||
|
toSkip := make([]int64, 0)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var row MigrationRecord
|
||||||
|
if err = rows.Scan(&row.VersionID, &row.IsApplied); err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to scan row")
|
||||||
|
}
|
||||||
|
|
||||||
|
// have we already marked this version to be skipped?
|
||||||
|
skip := false
|
||||||
|
for _, v := range toSkip {
|
||||||
|
if v == row.VersionID {
|
||||||
|
skip = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// if version has been applied we're done
|
||||||
|
if row.IsApplied {
|
||||||
|
return row.VersionID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// latest version of migration has not been applied.
|
||||||
|
toSkip = append(toSkip, row.VersionID)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to get next row")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, ErrNoNextVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var tableName = "goose_db_version"
|
||||||
|
|
||||||
|
// TableName returns goose db version table name
|
||||||
|
func TableName() string {
|
||||||
|
return tableName
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTableName set goose db version table name
|
||||||
|
func SetTableName(n string) {
|
||||||
|
tableName = n
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user