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
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ Execute scripts as one-off container jobs.
docker-compose run --no-deps -it init task db:init host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
```

```bash
# Roll back the last migration
docker-compose run --no-deps -it init task db:down host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
```

```bash
# Roll back to a specific migration version (rolls down until current <= target)
docker-compose run --no-deps -it init task db:down target=20240101000000 host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
```

```bash
# Inspect migration status / current version
docker-compose run --no-deps -it init task db:status host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
docker-compose run --no-deps -it init task db:version host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
```

```bash
# Dump PostgreSQL database to local filesystem
docker-compose run --no-deps -it init task db:dump host=$PG_HOST port=$PG_PORT db=$PG_DB user=$PG_USER password=$PG_PASSWORD
Expand Down Expand Up @@ -238,6 +254,20 @@ By default, the backup will take place at midnight every day.
* `user`: Defaults to `PG_USER` || `"postgres"`
* `password`: Defaults to `PG_PASS` || `""`

- `db:down`: Roll back the last migration, or down to a specific version.
* `db`: Defaults `PG_DB` || `"postgres"`
* `host`: Defaults to `PG_HOST` || `"localhost"`
* `port`: Defaults to `PG_PORT` || `5432`
* `user`: Defaults to `PG_USER` || `"postgres"`
* `password`: Defaults to `PG_PASS` || `""`
* `target`: Optional version (e.g. `20240101000000`). When supplied, rolls down repeatedly until the current version is `<= target`. Use `target=0` to revert all migrations.

- `db:redo`: Re-run the last migration (rolls back, then re-applies).

- `db:status`: Show migration status for each migration.

- `db:version`: Print the current database migration version.

- `db:clean`: Clean PostgreSQL Database by deleting old records.
* `db`: Defaults `PG_DB` || `"postgres"`
* `host`: Defaults to `PG_HOST` || `"localhost"`
Expand Down
53 changes: 53 additions & 0 deletions src/sam.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,59 @@ namespace "db" do
puts "PostgreSQL database '#{args["db"]}' restored successfully from dump file '#{args["path"]}'" if ret
end

desc "Roll back the last migration, or down to `target=<version>`"
task "down" do |_, args|
arguments = {
pg_host: (args["host"]? || PlaceOS::PG_HOST).to_s,
pg_port: (args["port"]? || PlaceOS::PG_PORT).to_i,
pg_db: (args["db"]? || PlaceOS::PG_DB).to_s,
pg_user: (args["user"]? || PlaceOS::PG_USER).try &.to_s,
pg_password: (args["password"]? || PlaceOS::PG_PASS).to_s,
target: args["target"]?.try(&.to_s.to_i64),
}

PlaceOS::Tasks.pg_migrate_down(**arguments)
end

desc "Re-run the last migration"
task "redo" do |_, args|
arguments = {
pg_host: (args["host"]? || PlaceOS::PG_HOST).to_s,
pg_port: (args["port"]? || PlaceOS::PG_PORT).to_i,
pg_db: (args["db"]? || PlaceOS::PG_DB).to_s,
pg_user: (args["user"]? || PlaceOS::PG_USER).try &.to_s,
pg_password: (args["password"]? || PlaceOS::PG_PASS).to_s,
}

PlaceOS::Tasks.pg_migrate_redo(**arguments)
end

desc "Show migration status for the PostgreSQL DB"
task "status" do |_, args|
arguments = {
pg_host: (args["host"]? || PlaceOS::PG_HOST).to_s,
pg_port: (args["port"]? || PlaceOS::PG_PORT).to_i,
pg_db: (args["db"]? || PlaceOS::PG_DB).to_s,
pg_user: (args["user"]? || PlaceOS::PG_USER).try &.to_s,
pg_password: (args["password"]? || PlaceOS::PG_PASS).to_s,
}

PlaceOS::Tasks.pg_migration_status(**arguments)
end

desc "Print the current PostgreSQL DB migration version"
task "version" do |_, args|
arguments = {
pg_host: (args["host"]? || PlaceOS::PG_HOST).to_s,
pg_port: (args["port"]? || PlaceOS::PG_PORT).to_i,
pg_db: (args["db"]? || PlaceOS::PG_DB).to_s,
pg_user: (args["user"]? || PlaceOS::PG_USER).try &.to_s,
pg_password: (args["password"]? || PlaceOS::PG_PASS).to_s,
}

PlaceOS::Tasks.pg_database_version(**arguments)
end

desc "Clean PostgreSQL Database by deleting old records"
task "clean" do |_, args|
arguments = {
Expand Down
74 changes: 74 additions & 0 deletions src/tasks/database.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,80 @@ module PlaceOS::Tasks::Database
end
end

private def configure_micrate_connection(pg_db, pg_host, pg_port, pg_user, pg_password)
pg_user = "postgres" if pg_user.nil?
pg_password = "" if pg_password.nil?
Micrate::DB.connection_url = "postgresql://#{pg_user}:#{pg_password}@#{pg_host}:#{pg_port}/#{pg_db}"
end

def pg_migrate_down(
pg_db : String,
pg_host : String,
pg_port : Int32,
pg_user : String? = nil,
pg_password : String? = nil,
target : Int64? = nil,
)
configure_micrate_connection(pg_db, pg_host, pg_port, pg_user, pg_password)
Micrate::DB.connect do |db|
if target.nil?
Micrate.down(db)
else
# Public API only exposes one-step `down`; loop until at or below target.
loop do
current = Micrate.dbversion(db)
break if current <= target
Micrate.down(db)
break if Micrate.dbversion(db) == current
end
end
end
end

def pg_migrate_redo(
pg_db : String,
pg_host : String,
pg_port : Int32,
pg_user : String? = nil,
pg_password : String? = nil,
)
configure_micrate_connection(pg_db, pg_host, pg_port, pg_user, pg_password)
Micrate::DB.connect do |db|
Micrate.redo(db)
end
end

def pg_migration_status(
pg_db : String,
pg_host : String,
pg_port : Int32,
pg_user : String? = nil,
pg_password : String? = nil,
)
configure_micrate_connection(pg_db, pg_host, pg_port, pg_user, pg_password)
Micrate::DB.connect do |db|
puts "Applied At Migration"
puts "======================================="
Micrate.migration_status(db).each do |migration, migrated_at|
ts = migrated_at.nil? ? "Pending" : migrated_at.to_s
puts "%-24s -- %s" % [ts, migration.name]
end
end
end

def pg_database_version(
pg_db : String,
pg_host : String,
pg_port : Int32,
pg_user : String? = nil,
pg_password : String? = nil,
)
configure_micrate_connection(pg_db, pg_host, pg_port, pg_user, pg_password)
Micrate::DB.connect do |db|
puts Micrate.dbversion(db)
end
end

def drop_pg_tables(
pg_db : String,
pg_host : String,
Expand Down
Loading