diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..357c40e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: Run Tests on Multiple Versions of PostgreSQL + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: '1.17' + + - name: Lint + run: | + go install golang.org/x/lint/golint@latest + golint ./... + go vet ./... + + test: + strategy: + matrix: + postgres-version: [10, 11, 12, 15, 17] + runs-on: ubuntu-latest + container: golang:alpine + + services: + postgres: + image: postgres:${{ matrix.postgres-version }} + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 54${{ matrix.postgres-version }}:5432 + + steps: + - uses: actions/checkout@v3 + + - name: Install PostgreSQL (client) + run: | + apk --update add postgresql-client + + - name: Check PostgreSQL version + run: | + psql --version + + - name: Install dependencies + run: | + go mod download + + - name: Run tests + env: + PGMGR_TEST_HOST: postgres + PGHOST: postgres + PGPORT: 54${{ matrix.postgres-version}} + PGMGR_TEST_PORT: 54${{ matrix.postgres-version}} + PGUSER: postgres + PGPASSWORD: postgres + run: | + go test -timeout=30s ./... diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3315e63..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: go - -go: - - '1.12' - - stable - - master - -env: - global: - - GO111MODULE=on - -addons: - postgresql: "10" - -matrix: - allow_failures: - - go: master - -script: - - go get -u golang.org/x/lint/golint - - test -z "$(golint ./...)" - - test -z "$(gofmt -l .)" - - createuser pgmgr -s - - go test -v ./... diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dd38080 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# v1.1.6 + +* Migrated to GitHub actions for CI. diff --git a/README.md b/README.md index 70a20fd..333ca8a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,18 @@ $ pgmgr db migrate == Completed in 8 ms == ``` +## Internals + +Under the hood, pgmgr needs to manage an internal schema migrations table to +track which migrations have been applied to the database. This table is +automatically created when the `pgmgr db migrate` command is run for the first +time, or you can create it manually. + +To apply each migration, pgmgr uses its configured driver (`pq`, deprecated, or +`psql`) to run each migration code. It wraps the code in a transaction (unless +the migration is named with `.no_txn.` in its filename at any point) and adds +code at the end to log the migration in the schema migrations table. + ## Configuration `pgmgr` supports file-based configuration (useful for checking into your @@ -135,6 +147,7 @@ look at the standard Postgres env vars (`PGHOST`, `PGUSERNAME`, etc). ``` pgmgr migration MigrationName # generates files for a new migration +pgmgr migration --no-txn MName # generate a migration which will run without wrapping transaction pgmgr db create # creates the database if it doesn't exist pgmgr db drop # drop the database pgmgr db migrate # apply un-applied migrations @@ -142,3 +155,21 @@ pgmgr db rollback # reverts the latest migration, if possible. pgmgr db load # loads the schema dump file from PGMGR_DUMP_FILE pgmgr db dump # dumps the database structure & seeds to PGMGR_DUMP_FILE ``` + +## Development + +### Running tests + +pgmgr tests depend on a running Postgres instance. The easiest way to get all that spun up +quickly is to use the `act` tool to run the GitHub Actions worfklow manually: + +``` +$ act +``` + +You can also ensure you have a user with name `postgres` and password `postgres` created in +your local postgres instance and run the tests directly: + +``` +$ go test ./... +``` diff --git a/pgmgr/config_test.go b/pgmgr/config_test.go index d612213..02bb5e5 100644 --- a/pgmgr/config_test.go +++ b/pgmgr/config_test.go @@ -1,6 +1,7 @@ package pgmgr import ( + "fmt" "os" "testing" ) @@ -31,12 +32,12 @@ func TestDefaults(t *testing.T) { LoadConfig(c, &TestContext{}) - if c.Port != 5432 { - t.Fatal("config's port should default to 5432") + 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) } - if c.Host != "localhost" { - t.Fatal("config's host should default to localhost, but was ", c.Host) + if c.Host != getEnv("PGMGR_TEST_HOST", "localhost") { + t.Fatal("config's host should default to localhost (or value of $PGMGR_TEST_HOST), but was ", c.Host) } if c.MigrationTable != "schema_migrations" { diff --git a/pgmgr/pgmgr.go b/pgmgr/pgmgr.go index 6d72cbd..cc66994 100644 --- a/pgmgr/pgmgr.go +++ b/pgmgr/pgmgr.go @@ -49,7 +49,7 @@ func Create(c *Config) error { return err } - return sh("createdb", []string{c.Database}) + return sh("createdb", []string{"-w", c.Database}) } // Drop drops the database specified by the configuration. @@ -58,7 +58,7 @@ func Drop(c *Config) error { return err } - return sh("dropdb", []string{c.Database}) + return sh("dropdb", []string{"-w", c.Database}) } // Dump dumps the schema and contents of the database to the dump file. diff --git a/pgmgr/pgmgr_test.go b/pgmgr/pgmgr_test.go index f5192b3..b2f84fa 100644 --- a/pgmgr/pgmgr_test.go +++ b/pgmgr/pgmgr_test.go @@ -3,6 +3,7 @@ package pgmgr import ( "fmt" "io/ioutil" + "os" "os/exec" "path" "path/filepath" @@ -17,11 +18,19 @@ const ( dumpFile = "/tmp/pgmgr_dump.sql" ) +func getEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} + func globalConfig() *Config { return &Config{ - Username: "pgmgr", + Username: "postgres", + Password: "postgres", Database: testDBName, - Host: "localhost", + Host: getEnv("PGMGR_TEST_HOST", "localhost"), Port: 5432, MigrationFolder: migrationFolder, MigrationTable: "schema_migrations", @@ -31,13 +40,19 @@ func globalConfig() *Config { } func TestCreate(t *testing.T) { - dropDB(t) + if err := dropDB(t); err != nil { + t.Log("database already does not exist; skipping dropdb") + } + + t.Log("creating database...") if err := Create(globalConfig()); err != nil { t.Log(err) t.Fatal("Could not create database") } + t.Log("creating database done") + // if we can't remove that db, it couldn't have been created by us above. if err := dropDB(t); err != nil { t.Fatal("database doesn't seem to have been created!")