Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,24 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: '1.17'
go-version: '1.24'

- name: Lint
run: |
go install golang.org/x/lint/golint@latest
golint ./...
go vet ./...
uses: golangci/golangci-lint-action@v6
with:
version: latest

test:
strategy:
matrix:
postgres-version: [10, 11, 12, 15, 17]
runs-on: ubuntu-latest
container: golang:alpine
container: golang:1.24-alpine

services:
postgres:
Expand All @@ -44,7 +43,7 @@ jobs:
- 54${{ matrix.postgres-version }}:5432

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Install PostgreSQL (client)
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.worktrees/
11 changes: 8 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
module github.com/rnubel/pgmgr

go 1.12
go 1.21

require (
github.com/lib/pq v1.2.0
github.com/urfave/cli v1.22.1
github.com/lib/pq v1.12.0
github.com/urfave/cli v1.22.17
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
)
37 changes: 25 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo=
github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
11 changes: 6 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,19 @@ func main() {
Name: "rollback",
Usage: "rolls back the latest migration",
Action: func(c *cli.Context) error {
pgmgr.Rollback(config)
return nil
return displayErrorOrMessage(pgmgr.Rollback(config))
},
},
},
},
}

app.Action = func(c *cli.Context) error {
app.Command("help").Run(c)
return nil
return app.Command("help").Run(c)
}

app.Run(os.Args)
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
24 changes: 15 additions & 9 deletions pgmgr/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"regexp"
"strconv"
Expand Down Expand Up @@ -56,7 +55,9 @@ func LoadConfig(config *Config, ctx argumentContext) error {
// load configuration from file first; then override with
// flags or env vars if they're present.
configFile := ctx.String("config-file")
config.populateFromFile(configFile)
if err := config.populateFromFile(configFile); err != nil {
return err
}

// apply defaults from Postgres environment variables, but allow
// them to be overridden in the next step
Expand All @@ -76,12 +77,16 @@ func LoadConfig(config *Config, ctx argumentContext) error {
return config.validate()
}

func (config *Config) populateFromFile(configFile string) {
contents, err := ioutil.ReadFile(configFile)
if err == nil {
json.Unmarshal(contents, &config)
} else {
fmt.Println("error reading config file: ", err)
func (config *Config) populateFromFile(configFile string) error {
if configFile == "" {
return nil
}
contents, err := os.ReadFile(configFile)
if err != nil {
return err
}
if err := json.Unmarshal(contents, &config); err != nil {
return err
}
if config.DumpFile != "" {
deprecatedDumpFieldWarning("dump-file")
Expand All @@ -91,6 +96,7 @@ func (config *Config) populateFromFile(configFile string) {
deprecatedDumpFieldWarning("seed-tables")
config.DumpConfig.IncludeTables = config.SeedTables
}
return nil
}

func (config *Config) populateFromPostgresVars() {
Expand Down Expand Up @@ -243,7 +249,7 @@ func (config *Config) validate() error {
}

if config.Format != "unix" && config.Format != "datetime" {
return errors.New(`Format must be "unix" or "datetime"`)
return errors.New(`format must be "unix" or "datetime"`)
}

if config.Format == "datetime" && config.ColumnType != "string" {
Expand Down
40 changes: 30 additions & 10 deletions pgmgr/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ func (t *TestContext) Bool(key string) bool {
func TestDefaults(t *testing.T) {
c := &Config{}

LoadConfig(c, &TestContext{})
if err := LoadConfig(c, &TestContext{}); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if fmt.Sprint(c.Port) != getEnv("PGMGR_TEST_PORT", "5432") {
t.Fatal("config's port should default to 5432 (or value of $PGMGR_TEST_PORT), but was ", c.Port)
Expand Down Expand Up @@ -65,9 +67,13 @@ func TestOverlays(t *testing.T) {
// it was passed-in explictly at runtime
c.Port = 123
ctx.IntVals["port"] = 456
os.Setenv("PGPORT", "789")
if err := os.Setenv("PGPORT", "789"); err != nil {
t.Fatal(err)
}

LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.Port != 456 {
t.Fatal("config's port should come from the context, but was", c.Port)
Expand All @@ -80,9 +86,13 @@ func TestOverlays(t *testing.T) {
// should prefer the value from PGPORT, since
// nothing was passed-in at runtime
c.Port = 123
os.Setenv("PGPORT", "789")
if err := os.Setenv("PGPORT", "789"); err != nil {
t.Fatal(err)
}

LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.Port != 789 {
t.Fatal("config's port should come from PGPORT, but was", c.Port)
Expand All @@ -95,9 +105,13 @@ func TestOverlays(t *testing.T) {
// should prefer the value in the struct, since
// nothing else is given
c.Port = 123
os.Setenv("PGPORT", "")
if err := os.Setenv("PGPORT", ""); err != nil {
t.Fatal(err)
}

LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.Port != 123 {
t.Fatal("config's port should not change, but was", c.Port)
Expand All @@ -112,7 +126,9 @@ func TestOverlays(t *testing.T) {
c.ColumnType = "integer"
ctx.StringVals["column-type"] = "string"

LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.ColumnType != "string" {
t.Fatal("config's column-type should come from the context, but was", c.ColumnType)
Expand All @@ -123,7 +139,9 @@ func TestURL(t *testing.T) {
c := &Config{}
c.URL = "postgres://foo@bar:5431/test-db.one?sslmode=verify-ca"

LoadConfig(c, &TestContext{})
if err := LoadConfig(c, &TestContext{}); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.Username != "foo" || c.Host != "bar" || c.Port != 5431 || c.Database != "test-db.one" || c.SslMode != "verify-ca" {
t.Fatal("config did not populate itself from the given URL:", c)
Expand All @@ -134,7 +152,9 @@ func TestURLWithPassword(t *testing.T) {
c := &Config{}
c.URL = "postgres://foo:baz@bar:5431/test-db.one?sslmode=verify-ca"

LoadConfig(c, &TestContext{})
if err := LoadConfig(c, &TestContext{}); err != nil {
t.Fatal("LoadConfig failed:", err)
}

if c.Username != "foo" || c.Password != "baz" || c.Host != "bar" || c.Port != 5431 || c.Database != "test-db.one" || c.SslMode != "verify-ca" {
t.Fatal("config did not populate itself from the given URL:", c)
Expand Down
12 changes: 9 additions & 3 deletions pgmgr/dump_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func TestDumpDefaults(t *testing.T) {
"dump-file": "dump.file.sql.gz",
},
}
LoadConfig(c, &dumpContext)
if err := LoadConfig(c, &dumpContext); err != nil {
t.Fatal("unexpected error from LoadConfig:", err)
}

if c.DumpConfig.DumpFile != "dump.file.sql" {
t.Fatal("dump config should strip '.gz' suffix, but was ", c.DumpConfig.DumpFile)
Expand All @@ -104,7 +106,9 @@ func TestDumpOverlays(t *testing.T) {
c.DumpConfig.DumpFile = "structval"
ctx.StringVals["dump-file"] = "stringval"

LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("unexpected error from LoadConfig:", err)
}

if c.DumpConfig.DumpFile != "stringval" {
t.Fatal("config's dump file should come from the context, but was", c.DumpConfig.DumpFile)
Expand All @@ -117,7 +121,9 @@ func TestDumpOverlays(t *testing.T) {
// should prefer the value in the struct, since
// nothing else is given
c.DumpConfig.DumpFile = "structval"
LoadConfig(c, ctx)
if err := LoadConfig(c, ctx); err != nil {
t.Fatal("unexpected error from LoadConfig:", err)
}

if c.DumpConfig.DumpFile != "structval" {
t.Fatal("config's dump file should not change, but was", c.DumpConfig.DumpFile)
Expand Down
Loading
Loading