discharger 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3ccade0fc5dd3187492ca714a564007743c6b4b18633293fcd95de540e8b8cb
4
- data.tar.gz: 55833bfc91a090b58e1a853df4138699eab4dee9b5f60f5a5d01911d5adf580c
3
+ metadata.gz: 487fc4990548d3bae797d7538d41363f6c02064c1e0682ff3ef11bdeac4de7de
4
+ data.tar.gz: 7125f6b47e9fea4e457d418fcda09ef12045ed72c5c96200b4d1887ae832a658
5
5
  SHA512:
6
- metadata.gz: e679dfc9501cc0d0739143785af0ef29de20295577ed0971c21fb66a4cb9506d67b3d644a19497e4a0353f925180413ae978d8366fb614b4a544150f3014b99d
7
- data.tar.gz: a92efb287ab888f7ff550797cf54bbdd6d8bad18d23cedfbc1f673309c4969ac86f1b9afc4fb7b2d8d9902a6d250854592d79056e5d2692635c1f9ef5879dfde
6
+ metadata.gz: 5e485c19d5c6285108dad2f14172b818046edcc4cec7fd9a3e5936c76e2a76df65fb4db0729b9862fc9f6e6ebb0750cb18f1fb23080920321438e19078626d5b
7
+ data.tar.gz: 61431222b20ebd419407d1db9d2f969df34993cbdbce465c302d26bd80081aabacda5085f85e5e2aae758c23641f31124c2c9edbce1ff9ff733a9e5b014dbb73
data/CHANGELOG.md CHANGED
@@ -5,14 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
- ## [0.2.31] - 2026-04-09
8
+ ## [0.3.2] - 2026-07-15
9
9
 
10
- ### Added
10
+ ### Changed
11
11
 
12
- - Unified setup entry point with pre-steps support (1459633)
12
+ - Treat generated pg_tools setup as opt-in for apps that use structure.sql or PostgreSQL CLI workflows.
13
13
 
14
- ## [0.2.30] - 2026-03-11
14
+ ### Fixed
15
15
 
16
- ### Changed
16
+ - Retry `brew bundle` once before failing setup, so transient Homebrew lock contention (e.g. two `bin/setup` runs on the same host) doesn't hard-fail the whole run.
17
+ - pg-tools wrappers no longer forward localhost -h/-p flags into docker exec, where the host-mapped port doesn't exist; remote hosts still pass through untouched (a3869d2)
18
+
19
+ ## [0.3.1] - 2026-05-13
20
+
21
+ ### Added
17
22
 
18
- - Use git trailer fragments for changelog generation (70396ca)
23
+ - database.prefer_docker setup.yml option for explicit Docker vs native PostgreSQL selection (2fca81e)
data/README.md CHANGED
@@ -163,6 +163,12 @@ database:
163
163
  name: "db-your-app"
164
164
  version: "14"
165
165
  password: "postgres"
166
+ # Optional. Controls how the docker step handles a native PostgreSQL
167
+ # already listening on the configured port:
168
+ # omitted/nil/false - silently skip Docker and use the native instance (legacy default)
169
+ # true - always create the Docker container; fail if a native instance holds the port
170
+ # "prompt" - ask the developer (interactive shells only; non-interactive runs fall back to native)
171
+ prefer_docker: "prompt"
166
172
 
167
173
  redis:
168
174
  port: 6379
@@ -193,12 +199,12 @@ The `pre_steps` array defines commands that run **before Rails loads**. These ar
193
199
 
194
200
  Built-in pre_steps:
195
201
  - `homebrew` - Installs Homebrew if not present (macOS)
196
- - `postgresql_tools` - Installs PostgreSQL client tools (`pg_dump`, `psql`)
202
+ - `postgresql_tools` - Optionally installs PostgreSQL client tools (`pg_dump`, `psql`) for apps that use `structure.sql` or call those tools directly
197
203
 
198
204
  ```yaml
199
205
  pre_steps:
200
206
  - homebrew
201
- - postgresql_tools
207
+ # - postgresql_tools
202
208
  ```
203
209
 
204
210
  You can also define custom pre_steps with shell commands:
@@ -222,6 +228,7 @@ The `steps` array specifies which built-in setup commands to run. Available comm
222
228
  - `yarn` - Install JavaScript packages
223
229
  - `config` - Copy configuration files
224
230
  - `docker` - Setup Docker containers
231
+ - `pg_tools` - Create Docker-aware `pg_dump` and `psql` wrappers for apps that use `structure.sql` or call those tools directly
225
232
  - `env` - Configure environment variables
226
233
  - `database` - Setup and migrate database
227
234
 
@@ -6,10 +6,12 @@ module Discharger
6
6
  module SetupRunner
7
7
  module Commands
8
8
  class BrewCommand < BaseCommand
9
+ RETRY_DELAY_SECONDS = 2
10
+
9
11
  def execute
10
12
  proceed_with "brew bundle" do
11
13
  log "Ensuring brew dependencies"
12
- system! "brew bundle"
14
+ run_brew_bundle
13
15
  end
14
16
  end
15
17
 
@@ -20,6 +22,23 @@ module Discharger
20
22
  def description
21
23
  "Install Homebrew dependencies"
22
24
  end
25
+
26
+ private
27
+
28
+ # brew bundle can fail transiently (e.g. Homebrew lock contention from a
29
+ # concurrent `brew` invocation) unrelated to the app's actual dependencies.
30
+ # Retry once before treating it as a real setup failure.
31
+ def run_brew_bundle
32
+ system! "brew bundle"
33
+ rescue => e
34
+ log "brew bundle failed, retrying once: #{e.message}"
35
+ sleep retry_delay
36
+ system! "brew bundle"
37
+ end
38
+
39
+ def retry_delay
40
+ RETRY_DELAY_SECONDS
41
+ end
23
42
  end
24
43
  end
25
44
  end
@@ -7,35 +7,8 @@ module Discharger
7
7
  module Commands
8
8
  class DockerCommand < BaseCommand
9
9
  def execute
10
- # Setup database container if configured
11
- if database_configured?
12
- puts " → Checking database configuration..." unless ENV["QUIET_SETUP"]
13
- if native_postgresql_available?
14
- puts " → Native PostgreSQL detected on port #{native_postgresql_port}, skipping Docker setup" unless ENV["QUIET_SETUP"]
15
- ENV["DB_PORT"] ||= native_postgresql_port.to_s
16
- else
17
- puts " → No native PostgreSQL found, setting up Docker container..." unless ENV["QUIET_SETUP"]
18
- ensure_docker_running
19
- setup_container(
20
- name: database_config.name || "db-app",
21
- port: database_config.port || 5432,
22
- image: "postgres:#{database_config.version || "14"}",
23
- env: {"POSTGRES_PASSWORD" => database_config.password || "postgres"},
24
- volume: "#{database_config.name || "db-app"}:/var/lib/postgresql/data",
25
- internal_port: 5432
26
- )
27
- end
28
- end
29
-
30
- # Setup Redis container if configured
31
- if redis_configured?
32
- setup_container(
33
- name: redis_config.name || "redis-app",
34
- port: redis_config.port || 6379,
35
- image: "redis:#{redis_config.version || "latest"}",
36
- internal_port: 6379
37
- )
38
- end
10
+ setup_database if database_configured?
11
+ setup_redis if redis_configured?
39
12
  end
40
13
 
41
14
  def can_execute?
@@ -49,6 +22,84 @@ module Discharger
49
22
 
50
23
  private
51
24
 
25
+ def setup_database
26
+ puts " → Checking database configuration..." unless ENV["QUIET_SETUP"]
27
+
28
+ case database_config.prefer_docker
29
+ when true
30
+ create_database_container_or_fail
31
+ when "prompt"
32
+ if native_postgresql_available? && wants_native_postgresql?
33
+ use_native_postgresql
34
+ else
35
+ create_database_container_or_fail
36
+ end
37
+ else
38
+ # Legacy default: prefer the native instance when one is available.
39
+ if native_postgresql_available?
40
+ use_native_postgresql
41
+ else
42
+ create_database_container
43
+ end
44
+ end
45
+ end
46
+
47
+ def setup_redis
48
+ setup_container(
49
+ name: redis_config.name || "redis-app",
50
+ port: redis_config.port || 6379,
51
+ image: "redis:#{redis_config.version || "latest"}",
52
+ internal_port: 6379
53
+ )
54
+ end
55
+
56
+ def use_native_postgresql
57
+ puts " → Native PostgreSQL detected on port #{native_postgresql_port}, skipping Docker setup" unless ENV["QUIET_SETUP"]
58
+ ENV["DB_PORT"] ||= native_postgresql_port.to_s
59
+ end
60
+
61
+ def create_database_container_or_fail
62
+ if native_postgresql_available?
63
+ raise "prefer_docker is set in the database config but native PostgreSQL is " \
64
+ "listening on port #{native_postgresql_port}. Stop the native instance " \
65
+ "(e.g., `brew services stop postgresql@15`) or remove prefer_docker."
66
+ end
67
+ create_database_container
68
+ end
69
+
70
+ def create_database_container
71
+ puts " → No native PostgreSQL found, setting up Docker container..." unless ENV["QUIET_SETUP"]
72
+ ensure_docker_running
73
+ setup_container(
74
+ name: database_config.name || "db-app",
75
+ port: database_config.port || 5432,
76
+ image: "postgres:#{database_config.version || "14"}",
77
+ env: {"POSTGRES_PASSWORD" => database_config.password || "postgres"},
78
+ volume: "#{database_config.name || "db-app"}:/var/lib/postgresql/data",
79
+ internal_port: 5432
80
+ )
81
+ end
82
+
83
+ # Ask the developer whether to use the detected native PostgreSQL or set up
84
+ # the configured Docker container instead. Falls back to native (the legacy
85
+ # behavior) when stdin is not a TTY, in CI, or when QUIET_SETUP is set,
86
+ # so non-interactive runs do not hang waiting for input.
87
+ def wants_native_postgresql?
88
+ unless interactive_prompt_available?
89
+ puts " → Non-interactive shell; defaulting to native PostgreSQL on port #{native_postgresql_port}" unless ENV["QUIET_SETUP"]
90
+ return true
91
+ end
92
+
93
+ puts "Native PostgreSQL detected on port #{native_postgresql_port}. Use the Docker container anyway?\n ===> Type Y to set up the Docker container\nOtherwise hit any key to use the native PostgreSQL."
94
+ $stdin.gets&.chomp != "Y"
95
+ end
96
+
97
+ def interactive_prompt_available?
98
+ return true if ENV["DISCHARGER_FORCE_INTERACTIVE"]
99
+ return false if ENV["CI"] || ENV["QUIET_SETUP"]
100
+ $stdin.tty?
101
+ end
102
+
52
103
  def setup_container(name:, port:, image:, internal_port:, env: {}, volume: nil)
53
104
  log "Checking #{name} container"
54
105
 
@@ -62,6 +62,8 @@ module Discharger
62
62
  # When running in Docker, we need to output to stdout and redirect locally
63
63
  OUTPUT_FILE=""
64
64
  HAS_USER=""
65
+ HOST=""
66
+ PORT=""
65
67
  ARGS=()
66
68
  while [[ $# -gt 0 ]]; do
67
69
  case $1 in
@@ -82,6 +84,38 @@ module Discharger
82
84
  ARGS+=("$1")
83
85
  shift
84
86
  ;;
87
+ -h|--host)
88
+ if [[ $# -lt 2 ]]; then
89
+ echo "Error: $1 requires an argument" >&2
90
+ exit 2
91
+ fi
92
+ HOST="$2"
93
+ shift 2
94
+ ;;
95
+ -h?*)
96
+ HOST="${1#-h}"
97
+ shift
98
+ ;;
99
+ --host=*)
100
+ HOST="${1#*=}"
101
+ shift
102
+ ;;
103
+ -p|--port)
104
+ if [[ $# -lt 2 ]]; then
105
+ echo "Error: $1 requires an argument" >&2
106
+ exit 2
107
+ fi
108
+ PORT="$2"
109
+ shift 2
110
+ ;;
111
+ -p?*)
112
+ PORT="${1#-p}"
113
+ shift
114
+ ;;
115
+ --port=*)
116
+ PORT="${1#*=}"
117
+ shift
118
+ ;;
85
119
  *)
86
120
  ARGS+=("$1")
87
121
  shift
@@ -94,6 +128,19 @@ module Discharger
94
128
  ARGS=("-U" "postgres" "${ARGS[@]}")
95
129
  fi
96
130
 
131
+ # Drop localhost host/port flags: they target the host-mapped
132
+ # port, but inside the container Postgres listens on its default
133
+ # port. Remote hosts pass through untouched.
134
+ case "$HOST" in
135
+ ""|localhost|127.0.0.1|::1) ;;
136
+ *)
137
+ ARGS+=("-h" "$HOST")
138
+ if [[ -n "$PORT" ]]; then
139
+ ARGS+=("-p" "$PORT")
140
+ fi
141
+ ;;
142
+ esac
143
+
97
144
  if [[ -n "$OUTPUT_FILE" ]]; then
98
145
  # Run pg_dump in container, output to stdout, redirect to local file
99
146
  docker exec -i "$CONTAINER" pg_dump "${ARGS[@]}" > "$OUTPUT_FILE"
@@ -133,6 +180,8 @@ module Discharger
133
180
  # so we pipe file content via stdin instead
134
181
  INPUT_FILE=""
135
182
  HAS_USER=""
183
+ HOST=""
184
+ PORT=""
136
185
  ARGS=()
137
186
  while [[ $# -gt 0 ]]; do
138
187
  case $1 in
@@ -153,6 +202,38 @@ module Discharger
153
202
  ARGS+=("$1")
154
203
  shift
155
204
  ;;
205
+ -h|--host)
206
+ if [[ $# -lt 2 ]]; then
207
+ echo "Error: $1 requires an argument" >&2
208
+ exit 2
209
+ fi
210
+ HOST="$2"
211
+ shift 2
212
+ ;;
213
+ -h?*)
214
+ HOST="${1#-h}"
215
+ shift
216
+ ;;
217
+ --host=*)
218
+ HOST="${1#*=}"
219
+ shift
220
+ ;;
221
+ -p|--port)
222
+ if [[ $# -lt 2 ]]; then
223
+ echo "Error: $1 requires an argument" >&2
224
+ exit 2
225
+ fi
226
+ PORT="$2"
227
+ shift 2
228
+ ;;
229
+ -p?*)
230
+ PORT="${1#-p}"
231
+ shift
232
+ ;;
233
+ --port=*)
234
+ PORT="${1#*=}"
235
+ shift
236
+ ;;
156
237
  *)
157
238
  ARGS+=("$1")
158
239
  shift
@@ -165,6 +246,19 @@ module Discharger
165
246
  ARGS=("-U" "postgres" "${ARGS[@]}")
166
247
  fi
167
248
 
249
+ # Drop localhost host/port flags: they target the host-mapped
250
+ # port, but inside the container Postgres listens on its default
251
+ # port. Remote hosts pass through untouched.
252
+ case "$HOST" in
253
+ ""|localhost|127.0.0.1|::1) ;;
254
+ *)
255
+ ARGS+=("-h" "$HOST")
256
+ if [[ -n "$PORT" ]]; then
257
+ ARGS+=("-p" "$PORT")
258
+ fi
259
+ ;;
260
+ esac
261
+
168
262
  if [[ -n "$INPUT_FILE" ]]; then
169
263
  docker exec -i "$CONTAINER" #{tool} "${ARGS[@]}" < "$INPUT_FILE"
170
264
  else
@@ -37,13 +37,14 @@ module Discharger
37
37
  end
38
38
 
39
39
  class DatabaseConfig
40
- attr_accessor :port, :name, :version, :password
40
+ attr_accessor :port, :name, :version, :password, :prefer_docker
41
41
 
42
42
  def initialize
43
43
  @port = 5432
44
44
  @name = "db-app"
45
45
  @version = "14"
46
46
  @password = "postgres"
47
+ @prefer_docker = nil
47
48
  end
48
49
 
49
50
  def from_hash(hash)
@@ -51,6 +52,7 @@ module Discharger
51
52
  @name = hash["name"] if hash["name"]
52
53
  @version = hash["version"] if hash["version"]
53
54
  @password = hash["password"] if hash["password"]
55
+ @prefer_docker = hash["prefer_docker"] if hash.key?("prefer_docker")
54
56
  end
55
57
  end
56
58
 
@@ -55,14 +55,36 @@ module Discharger
55
55
  def set_db_port(db_config)
56
56
  if db_config["port"] && !ENV["DB_PORT"]
57
57
  ENV["DB_PORT"] = db_config["port"].to_s
58
- ENV["PGPORT"] = db_config["port"].to_s
58
+ set_pgport_if_needed(db_config["port"].to_s)
59
59
  puts " Setting DB_PORT=#{db_config["port"]} from config/setup.yml"
60
60
  elsif ENV["DB_PORT"]
61
61
  warn_if_mismatch("DB_PORT", ENV["DB_PORT"], db_config["port"].to_s)
62
- ENV["PGPORT"] = ENV["DB_PORT"]
62
+ set_pgport_if_needed(ENV["DB_PORT"])
63
63
  end
64
64
  end
65
65
 
66
+ def set_pgport_if_needed(port)
67
+ ENV["PGPORT"] = port if postgresql_cli_setup_enabled?
68
+ end
69
+
70
+ def postgresql_cli_setup_enabled?
71
+ configured_pre_steps.include?("postgresql_tools") ||
72
+ configured_steps.include?("pg_tools") ||
73
+ all_regular_steps_enabled?
74
+ end
75
+
76
+ def configured_pre_steps
77
+ Array(config["pre_steps"]).select { |step| step.is_a?(String) }
78
+ end
79
+
80
+ def configured_steps
81
+ Array(config["steps"]).select { |step| step.is_a?(String) }
82
+ end
83
+
84
+ def all_regular_steps_enabled?
85
+ !config.key?("steps") || Array(config["steps"]).empty?
86
+ end
87
+
66
88
  def set_db_name(db_config)
67
89
  if db_config["name"] && !ENV["DB_NAME"]
68
90
  container_name = db_config["name"].to_s
@@ -1,3 +1,3 @@
1
1
  module Discharger
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -24,7 +24,7 @@ redis:
24
24
  #
25
25
  # Built-in pre_steps:
26
26
  # - homebrew # Installs Homebrew if not present
27
- # - postgresql_tools # Installs PostgreSQL client tools (pg_dump, psql)
27
+ # - postgresql_tools # Optional: installs PostgreSQL client tools for structure.sql workflows
28
28
  #
29
29
  # Custom pre_steps (run shell commands):
30
30
  # - description: "Description of step"
@@ -33,7 +33,7 @@ redis:
33
33
  #
34
34
  pre_steps: []
35
35
  # - homebrew
36
- # - postgresql_tools
36
+ # - postgresql_tools # Optional: for apps that use structure.sql or pg_dump/psql directly
37
37
 
38
38
  # Built-in commands to run (empty array runs all available commands)
39
39
  # Available commands: brew, asdf, git, bundler, yarn, config, docker, pg_tools, env, database
@@ -45,10 +45,12 @@ steps:
45
45
  - yarn
46
46
  - config
47
47
  - docker
48
- - pg_tools
49
48
  - env
50
49
  - database
51
50
 
51
+ # Optional for apps that use structure.sql or other pg_dump/psql workflows:
52
+ # - pg_tools
53
+
52
54
  # Custom commands for application-specific setup (run AFTER Rails loads)
53
55
  custom_steps:
54
56
  - description: "Seed application data"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discharger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Gay