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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
### Breaking changes


## 12.6.0 2025-09-22

### Compatible changes

- `geordi dump`: Allow to forward the compression option to the underlying `dumple` command, e.g. `geordi dump --compress=zstd:3` (for PostgreSQL) or `geordi dump --compress` (for MySQL).
- `dumple`: Allow to specify a compression algorithm for PostgreSQL, e.g. `dumple --compress=zstd:3`. The already supported compression for MySQL `dumple --compress` is kept untouched.


## 12.5.0 2025-09-09

### Compatible changes
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
geordi (12.5.0)
geordi (12.6.0)
highline
thor (~> 1)

Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ Example: `geordi chromedriver_update`
This command will find and install the matching chromedriver for the currently
installed Chrome.

Setting `auto_update_chromedriver` to `true` in your global Geordi config file
(`~/.config/geordi/global.yml`), will automatically update chromedriver before
Setting `auto_update_chromedriver` to `true` in your global Geordi config file
(`~/.config/geordi/global.yml`), will automatically update chromedriver before
cucumber tests if a newer chromedriver version is available.


Expand Down Expand Up @@ -98,8 +98,8 @@ servers. When passed a number, directly connects to the selected server.
IRB flags can be given as `irb_flags: '...'` in the global or local Geordi config file
(`~/.config/geordi/global.yml` / `./.geordi.yml`). If you define irb_flags in both files, the local config file will be
used. For IRB >=1.2 in combination with Ruby <3 geordi automatically sets the `--nomultiline` flag, to prevent slow
pasting. You can override this behavior by setting `--multiline` in the global config file or by defining `irb_flags`
in the local config file. The latter will always turn off the automatic behavior, even if you don't set any values for
pasting. You can override this behavior by setting `--multiline` in the global config file or by defining `irb_flags`
in the local config file. The latter will always turn off the automatic behavior, even if you don't set any values for
the irb_flags key.

**Options**
Expand Down Expand Up @@ -191,8 +191,8 @@ and offer to delete them. Excluded are databases that are whitelisted. This come
in handy when you're keeping your currently active projects in the whitelist files
and perform regular housekeeping with Geordi.

Per default, Geordi will try to connect to the databases as a local user without
password authorization.
Per default, Geordi will try to connect to the databases as a local user without
password authorization.

Geordi will ask for confirmation before actually dropping databases and will
offer to edit the whitelist instead.
Expand Down Expand Up @@ -234,6 +234,7 @@ not match, please issue separate commands for dumping (`dump -d`) and sourcing
**Options**
- `-l, --load=[DUMP_FILE]`: Load a dump
- `-d, --database=NAME`: Target database, if there are multiple databases
- `-c, --compress=[ALGORITHM]`: Compress the dump file (default for PSQL)


### `geordi help [COMMAND]`
Expand Down Expand Up @@ -341,7 +342,7 @@ Run all employed tests.
When running `geordi tests` without any arguments, all unit tests, rspec specs
and cucumber features will be run.

When passing file paths or directories as arguments, Geordi will forward them to `rspec` and `cucumber`.
When passing file paths or directories as arguments, Geordi will forward them to `rspec` and `cucumber`.
All rspec specs and cucumber features matching the given paths will be run.


Expand Down Expand Up @@ -389,7 +390,9 @@ Stores a timestamped database dump for the given Rails environment in `~/dumps`:

**Options**
- `-i`: Print disk usage of `~/dumps`
- `--compress`: After dumping, run gzip to compress the dump in place
- `--fail-gently`: On error, do not crash but print a warning and exit(0)
- `--for-download`: Dump to `~/dumps/dump_for_download.dump`
- `--compress`: Compress the dump (default for PostgreSQL) and optionally set the compression algorithm (only available for PostgreSQL)


Contributing
Expand Down
24 changes: 18 additions & 6 deletions exe/dumple
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def cd_to_project_root(fail_gently)
end
end

def dump_command(dump_file, config)
def dump_command(dump_file, config, compress)
host = config['host']
port = config['port']

Expand Down Expand Up @@ -79,6 +79,7 @@ def dump_command(dump_file, config)
command << " pg_dump #{config['database']}"
command << " --clean"
command << " --format=custom"
command << " --compress=#{compress}" if compress.is_a?(String)
command << " --file=#{dump_file}"
command << " --username=\"#{config['username']}\""
command << " --host=#{host}" if host
Expand Down Expand Up @@ -124,7 +125,7 @@ def prepare_dump_path(config)
run "chmod 700 #{DUMPS_DIR}"
end

if ARGV.include? '--for_download'
if ARGV.include?('--for_download') || ARGV.include?('--for-download')
"#{DUMPS_DIR}/dump_for_download.dump"
else
"#{DUMPS_DIR}/#{config['database']}_#{Time.now.strftime("%Y%m%d_%H%M%S")}.dump"
Expand All @@ -133,21 +134,32 @@ end

begin
fail_gently = ARGV.include?("--fail-gently")
compress = ARGV.include?("--compress")
environment, database = ARGV.reject { |arg| arg[0].chr == '-' }
compress = ARGV.find do |argument|
if argument == '--compress'
break true
elsif argument.start_with?('--compress=')
break argument.split('=').last
end
end
environment, database = ARGV.reject { |argument| argument.start_with?('-') }

cd_to_project_root(fail_gently)
config = find_database_config(DB_CONFIG_PATH, environment, database)
dump_path = prepare_dump_path(config)

# Dump!
given_database = database ? %(#{database} ) : ""
command = dump_command(dump_path, config)
command = dump_command(dump_path, config, compress)
puts "> Dumping #{given_database}database for \"#{environment}\" environment ..."
run command or raise "x Creating the dump failed."
run "chmod 600 #{dump_path}"

if compress
if config['adapter'] == 'mysql' && compress.is_a?(String)
puts "> Cannot compress a MySQL dump with #{compress}, falling back to gzip."
end

# For PostgreSQL, #dump_command will do the compression. MySQL needs manual compression.
if config['adapter'] == 'mysql' && compress
puts "> Compressing the dump ..."
# gzip compresses in place
compress_success = run "gzip #{dump_path}"
Expand Down
42 changes: 35 additions & 7 deletions features/dump.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ Feature: The dump command

Scenario: Creating a dump of a remote database
Given a file named "Capfile" with "Capfile exists"
And a file named "config/deploy.rb" with:
"""
"""
And a file named "config/deploy.rb" with "deploy.rb exists"
And a file named "config/deploy/staging.rb" with:
"""
set :rails_env, 'staging'
Expand All @@ -32,7 +30,7 @@ Feature: The dump command
When I run `geordi dump staging`
Then the output should contain "# Dumping the database of staging"
And the output should contain "> Connecting to www.example.com"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging --for_download'"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging --for-download'"
And the output should contain "> Downloading remote dump to tmp/staging.dump"
# Omitting the absolute path in this regex (.*)
And the output should match:
Expand Down Expand Up @@ -62,7 +60,7 @@ Feature: The dump command

When I run `geordi dump staging --load`
Then the output should contain "# Dumping the database of staging"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging --for_download'"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging --for-download'"
And the output should contain "> Dumped the staging database to tmp/staging.dump"

# Loading the dump
Expand All @@ -85,7 +83,7 @@ Feature: The dump command

When I run `geordi dump staging --database primary`
Then the output should contain "# Dumping the database of staging (primary database)"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging primary --for_download'"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging primary --for-download'"
And the output should contain "> Dumped the primary staging database to tmp/staging.dump"


Expand All @@ -110,7 +108,7 @@ Feature: The dump command

When I run `geordi dump staging --database primary --load`
Then the output should contain "# Dumping the database of staging (primary database)"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging primary --for_download'"
And the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging primary --for-download'"
And the output should contain "> Dumped the primary staging database to tmp/staging.dump"

# Loading the dump
Expand Down Expand Up @@ -176,6 +174,7 @@ Feature: The dump command
And the output should contain "Util.run! dropdb --if-exists test-other && createdb test-other && pg_restore --no-owner --no-acl --dbname=test-other tmp/production.dump"
But the output should not contain "Sourcing dump into the test-primary db"


Scenario: Sourcing a dump into one of multiple databases without specifying a db
Given a file named "tmp/production.dump" with "some content"
And a file named "config/database.yml" with:
Expand All @@ -193,3 +192,32 @@ Feature: The dump command
And the output should contain "Source file: tmp/production.dump"
And the output should contain "Util.run! dropdb --if-exists test-primary && createdb test-primary && pg_restore --no-owner --no-acl --dbname=test-primary tmp/production.dump"
But the output should not contain "Sourcing dump into the test-other db"


Scenario: Enforcing compression
When I run `geordi dump --compress`
Then the output should contain "Util.run! dumple development --compress"
And the output should contain "Successfully dumped the development database"


Scenario: Setting a custom compression algorithm
When I run `geordi dump --compress=zstd:3`
Then the output should contain "Util.run! dumple development --compress=zstd:3"
And the output should contain "Successfully dumped the development database"


Scenario: Setting a custom compression algorithm for a remote target
Given a file named "Capfile" with "Capfile exists"
And a file named "config/deploy.rb" with "deploy.rb exists"
And a file named "config/deploy/staging.rb" with:
"""
set :rails_env, 'staging'
set :deploy_to, '/var/www/example.com'
set :user, 'user'

server 'www.example.com'
"""

When I run `geordi dump staging --compress=zstd:3`
Then the output should contain "Util.run! ssh, [email protected], -t, cd /var/www/example.com/current && bash --login -c 'dumple staging --compress=zstd:3 --for-download'"
And the output should contain "> Dumped the staging database to tmp/staging.dump"
65 changes: 61 additions & 4 deletions features/dumple.feature
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Feature: Creating Rails database dumps with the "dumple" script

The --for_download option generates a deterministic file name.
The --for-download option generates a deterministic file name.

Scenario: Execution outside of a Rails application
When I run `dumple development`
Expand All @@ -20,7 +20,7 @@ Feature: Creating Rails database dumps with the "dumple" script
Since dumple won't actually dump in tests, we prepare the dump file in advance.
"""

When I run `dumple development --for_download`
When I run `dumple development --for-download`
Then the output should match /Dumping database for "development" environment/
And the output should contain "> Dumped to /home/"
And the output should contain "/geordi/tmp/aruba/dumps/dump_for_download.dump (0 KB)"
Expand All @@ -36,7 +36,7 @@ Feature: Creating Rails database dumps with the "dumple" script
"""
And a mocked home directory

When I run `dumple development --for_download`
When I run `dumple development --for-download`
Then the output should match %r<system mysqldump -u"user" -p"password" -r /home/.*/aruba/dumps/dump_for_download.dump --single-transaction --quick -hlocalhost>


Expand All @@ -50,7 +50,7 @@ Feature: Creating Rails database dumps with the "dumple" script
"""
And a mocked home directory

When I run `dumple development --for_download`
When I run `dumple development --for-download`
Then the output should match %r<system PGPASSWORD="password" pg_dump --clean --format=custom --file=/home/.*/aruba/dumps/dump_for_download.dump --username="user">


Expand Down Expand Up @@ -133,3 +133,60 @@ Feature: Creating Rails database dumps with the "dumple" script

When I run `dumple development sub`
Then the output should match /Could not select "sub" database in a single-db environment/


Scenario: Instructing MySQL to compress the dump
Given a file named "config/database.yml" with:
"""
development:
adapter: mysql
database: geordi_development
"""
And a mocked home directory
And a file named "dumps/dump_for_download.dump.gz" with:
"""
Since dumple won't actually dump in tests, we prepare the dump file in advance.
"""

When I run `dumple development --for-download --compress`
Then the output should match /Dumping database for "development" environment/
And the output should contain "> Compressing the dump ..."
And the output should contain "system gzip"
And the output should contain "> Dumped to /home/"
And the output should contain "/geordi/tmp/aruba/dumps/dump_for_download.dump.gz (0 KB)"


Scenario: Instructing MySQL to compress the dump with a custom compression algorithm shows a warning
Given a file named "config/database.yml" with:
"""
development:
adapter: mysql
database: geordi_development
"""
And a mocked home directory
And a file named "dumps/dump_for_download.dump.gz" with:
"""
Since dumple won't actually dump in tests, we prepare the dump file in advance.
"""

When I run `dumple development --for-download --compress=zstd:3`
Then the output should match /Dumping database for "development" environment/
And the output should contain "> Cannot compress a MySQL dump with zstd:3, falling back to gzip."
And the output should contain "> Compressing the dump ..."
And the output should contain "system gzip"
And the output should contain "> Dumped to /home/"
And the output should contain "/geordi/tmp/aruba/dumps/dump_for_download.dump.gz (0 KB)"


Scenario: Setting a custom compression algorithm for PostgreSQL
Given a file named "config/database.yml" with:
"""
development:
adapter: postgres
username: user
password: password
"""
And a mocked home directory

When I run `dumple development --for-download --compress=zstd:3`
Then the output should match %r<system PGPASSWORD="password" pg_dump --clean --format=custom --compress=zstd:3 --file=/home/.*/aruba/dumps/dump_for_download.dump --username="user">
13 changes: 8 additions & 5 deletions lib/geordi/commands/dump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@

option :load, aliases: '-l', type: :string, desc: 'Load a dump', banner: '[DUMP_FILE]'
option :database, aliases: '-d', type: :string, desc: 'Target database, if there are multiple databases', banner: 'NAME'
option :compress, aliases: '-c', type: :string, desc: 'Compress the dump file (default for PSQL)', banner: '[ALGORITHM]'

def dump(target = nil, *_args)
require 'geordi/dump_loader'
require 'geordi/remote'
database = options[:database] ? "#{options[:database]} " : ''

if target.nil? # Local …
if options.load # … dump loading
Expand All @@ -46,14 +46,17 @@ def dump(target = nil, *_args)

else # … dump creation
Interaction.announce 'Dumping the development database'
Util.run!("dumple development #{database}")
Util.run!(Util.dumple_command('development', options))

database = "#{options[:database]} " if options[:database]
Interaction.success "Successfully dumped the #{database}development database."
end

else # Remote dumping …
database_label = options[:database] ? " (#{database}database)" : ""
database_label = target.dup
database_label << " (#{options[:database]} database)" if options[:database]

Interaction.announce "Dumping the database of #{target}#{database_label}"
Interaction.announce "Dumping the database of #{database_label}"
dump_path = Geordi::Remote.new(target).dump(options)

if options.load # … and dump loading
Expand All @@ -65,7 +68,7 @@ def dump(target = nil, *_args)
Util.run! "rm #{dump_path}"
Interaction.note "Dump file removed"

Interaction.success "Your #{loader.config['database']} database has now the data of #{target}#{database_label}."
Interaction.success "Your #{loader.config['database']} database has now the data of #{database_label}."
end
end

Expand Down
7 changes: 3 additions & 4 deletions lib/geordi/remote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ def select_server
end

def dump(options = {})
database = options[:database] ? " #{options[:database]}" : ''
# Generate dump on the server
shell options.merge({
remote_command: "dumple #{@config.env}#{database} --for_download",
})
dumple = Util.dumple_command(@config.env, options.merge(for_download: true))
shell(options.merge(remote_command: dumple))

destination_directory = File.join(@config.root, 'tmp')
FileUtils.mkdir_p destination_directory
Expand All @@ -48,6 +46,7 @@ def dump(options = {})
server = @config.primary_server
Util.run!("scp -C #{@config.user(server)}@#{server}:#{REMOTE_DUMP_PATH} #{destination_path}")

database = " #{options[:database]}" if options[:database]
Interaction.success "Dumped the#{database} #{@stage} database to #{relative_destination}."

destination_path
Expand Down
Loading