Module: Polyrun::Database::Provision

Defined in:
lib/polyrun/database/provision.rb

Overview

PostgreSQL-only provisioning via psql / createdb (spec2 §5.3). No pg gem. For other adapters, use Rails tasks or vendor CLIs; Polyrun::Database::UrlBuilder still emits DATABASE_URL for supported schemes.

Class Method Summary collapse

Class Method Details

.create_database_from_template!(new_db:, template_db:, host: nil, port: nil, username: nil, maintenance_db: "postgres") ⇒ Object

CREATE DATABASE new_db TEMPLATE template_db — connects to maintenance DB postgres.

Raises:



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/polyrun/database/provision.rb', line 35

def create_database_from_template!(new_db:, template_db:, host: nil, port: nil, username: nil, maintenance_db: "postgres")
  host ||= ENV["PGHOST"] || "localhost"
  port ||= ENV["PGPORT"] || "5432"
  username ||= ENV["PGUSER"] || "postgres"

  sql = "CREATE DATABASE #{quote_ident(new_db)} TEMPLATE #{quote_ident(template_db)};"
  cmd = ["psql", "-U", username, "-h", host, "-p", port.to_s, "-d", maintenance_db, "-v", "ON_ERROR_STOP=1", "-c", sql]
  _out, err, st = Open3.capture3(*cmd)
  raise Polyrun::Error, "create database failed: #{err}" unless st.success?

  true
end

.drop_database_if_exists!(database:, host: nil, port: nil, username: nil, maintenance_db: "postgres", force: false) ⇒ Object

DROP DATABASE IF EXISTS name; — maintenance DB postgres (or maintenance_db).

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/polyrun/database/provision.rb', line 16

def drop_database_if_exists!(database:, host: nil, port: nil, username: nil, maintenance_db: "postgres", force: false)
  host ||= ENV["PGHOST"] || "localhost"
  port ||= ENV["PGPORT"] || "5432"
  username ||= ENV["PGUSER"] || "postgres"

  sql =
    if force
      "DROP DATABASE IF EXISTS #{quote_ident(database)} WITH (FORCE);"
    else
      "DROP DATABASE IF EXISTS #{quote_ident(database)};"
    end
  cmd = ["psql", "-U", username, "-h", host, "-p", port.to_s, "-d", maintenance_db, "-v", "ON_ERROR_STOP=1", "-c", sql]
  _out, err, st = Open3.capture3(*cmd)
  raise Polyrun::Error, "drop database failed: #{err}" unless st.success?

  true
end

.prepare_template!(rails_root:, env:, silent: true) ⇒ Object

Runs bin/rails db:prepare with merged ENV (DATABASE_URL for primary, CACHE_DATABASE_URL, etc.). Multi-DB Rails apps must pass all template URLs in one invocation so each DB uses its own migrations_paths. Uses db:prepare (not db:migrate alone) so empty template databases load schema.rb first; apps that squash or archive migrations and keep only incremental files need that path.

Raises:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/polyrun/database/provision.rb', line 52

def prepare_template!(rails_root:, env:, silent: true)
  exe = File.join(rails_root, "bin", "rails")
  raise Polyrun::Error, "Provision: missing #{exe}" unless File.executable?(exe)

  child_env = ENV.to_h.merge(env)
  child_env["RAILS_ENV"] ||= ENV["RAILS_ENV"] || "test"
  rails_out, err, st = Open3.capture3(child_env, exe, "db:prepare", chdir: rails_root)
  Polyrun::Log.warn err if !silent && !err.to_s.empty?
  unless st.success?
    msg = +"db:prepare failed"
    msg << "\n--- stderr ---\n#{err}" unless err.to_s.strip.empty?
    # Rails often prints the first migration/SQL error on stdout; stderr may only show InFailedSqlTransaction.
    msg << "\n--- stdout ---\n#{rails_out}" unless rails_out.to_s.strip.empty?
    raise Polyrun::Error, msg
  end

  true
end

.quote_ident(name) ⇒ Object



11
12
13
# File 'lib/polyrun/database/provision.rb', line 11

def quote_ident(name)
  '"' + name.to_s.gsub('"', '""') + '"'
end