bamboolab_kaboom 0.1.0
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 +7 -0
- data/bin/kaboom +4 -0
- data/lib/kaboom/backup.rb +141 -0
- data/lib/kaboom/helpers/config.rb +86 -0
- data/lib/kaboom/helpers/database_helper.rb +33 -0
- data/lib/kaboom/helpers/utils.rb +22 -0
- data/lib/kaboom/version.rb +5 -0
- data/lib/kaboom.rb +61 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b6131743c73049e741f8e78b6523a920f7f43a169f268d7dee816a35b1cbfe5c
|
4
|
+
data.tar.gz: 0df5be6710bde734916305e1a9402ef83a040120cce5ede56430f289c8ce77f4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60d8e4c9ce9a98bcbcf2a52bf75f068d785c5e7192700fd51b95ce22eae1e013eb99dc1d128dc206a438fb1d8a50d156f2003abe10cc3ff8170a386bf82fb87a
|
7
|
+
data.tar.gz: 5a5400c999c25135c3fb711df4805b2ce42eb6876a56ddc37805b4ee6728c7106879b4c48ee87d74042dea1917e2b58bab33558d5e6ba56e94e3a0c2c5d8b124
|
data/bin/kaboom
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'google/cloud/storage'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tty-progressbar'
|
4
|
+
|
5
|
+
module Kaboom
|
6
|
+
class Backup < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
include Utils
|
9
|
+
include Config
|
10
|
+
|
11
|
+
desc 'pull', 'Pull the latest assets and database backup from the deploy destination'
|
12
|
+
def pull(destination = 'staging')
|
13
|
+
database_pull(destination)
|
14
|
+
puts "\n"
|
15
|
+
assets_pull(destination)
|
16
|
+
say "Completed!", :green
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'apply', 'Apply a local backup to the database'
|
20
|
+
method_option :file, aliases: '-f', type: :string, desc: 'Backup file in to apply', default: 'db/latest.sql'
|
21
|
+
def apply
|
22
|
+
backup_file = options[:file]
|
23
|
+
unless File.exist?(backup_file)
|
24
|
+
say_and_exit "Error: Backup file #{backup_file} not found", :red
|
25
|
+
end
|
26
|
+
confirm_proceed("Do you want to apply the backup to your local database? THIS WILL REMOVE YOUR EXISTING LOCAL DATABASE")
|
27
|
+
|
28
|
+
local_db_config = database_config('development')
|
29
|
+
if DatabaseHelper.apply_backup(local_db_config, backup_file)
|
30
|
+
say "Backup applied successfully to local database!", :green
|
31
|
+
else
|
32
|
+
say_and_exit \
|
33
|
+
"Error: Backup restore failed. Is there an open connection to the local database? Stop the Rails application and try again.",
|
34
|
+
:red
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def database_pull(destination)
|
41
|
+
validate_repository!
|
42
|
+
validate_destination(destination)
|
43
|
+
confirm_production_action('database backup pull') if destination == 'production'
|
44
|
+
|
45
|
+
load_env(destination)
|
46
|
+
db_config = database_config(destination)
|
47
|
+
connection_string = DatabaseHelper.connection_string(db_config)
|
48
|
+
|
49
|
+
database_size = DatabaseHelper.database_size(connection_string, destination)
|
50
|
+
|
51
|
+
if confirm_proceed("Database backup size will be around #{database_size}. Do you want to continue?", exit_on_no: false)
|
52
|
+
backup_file = ask_for_backup_filename
|
53
|
+
|
54
|
+
say "Started pulling backup from #{destination} | #{DateTime.now.strftime("%H:%M:%S")}"
|
55
|
+
if DatabaseHelper.pull_backup(connection_string, destination, backup_file)
|
56
|
+
say "Backup pulled successfully to db/#{backup_file}.sql | #{DateTime.now.strftime("%H:%M:%S")}", :green
|
57
|
+
else
|
58
|
+
say_and_exit "Error: Backup pull failed", :red
|
59
|
+
end
|
60
|
+
|
61
|
+
if confirm_proceed("Do you want to apply the backup to your local database? THIS WILL REMOVE YOUR EXISTING LOCAL DATABASE")
|
62
|
+
say "Applying backup to local database...", :white
|
63
|
+
local_db_config = database_config('development')
|
64
|
+
if DatabaseHelper.apply_backup(local_db_config, "db/#{backup_file}.sql")
|
65
|
+
say "Backup applied successfully to local database!", :green
|
66
|
+
else
|
67
|
+
say_and_exit \
|
68
|
+
"Error: Backup restore failed. Is there an open connection to the local database? Stop the Rails application and try again.",
|
69
|
+
:red
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
remove_backup_file(backup_file) if confirm_proceed("Do you want to remove the local backup file?", exit_on_no: false)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def assets_pull(destination)
|
79
|
+
validate_repository!
|
80
|
+
validate_destination(destination)
|
81
|
+
confirm_production_action('assets pull') if destination == 'production'
|
82
|
+
|
83
|
+
if confirm_proceed("Do you want to pull assets from #{destination}? Your local uploads folder will be deleted.")
|
84
|
+
credentials = load_credentials(destination)
|
85
|
+
say_and_exit("Error: Google Storage credentials not found in #{destination}.yml", :red) if credentials[:google_storage].nil?
|
86
|
+
|
87
|
+
project_name = credentials[:google_storage][:project_name]
|
88
|
+
bucket_name = credentials[:google_storage][:bucket_name]
|
89
|
+
|
90
|
+
File.open('.keyfile.json', 'w'){ |f| f.write(credentials[:google_storage][:json_key_string]) }
|
91
|
+
storage = Google::Cloud::Storage.new(
|
92
|
+
project_id: project_name,
|
93
|
+
credentials: ".keyfile.json", # It apsolutely needs to be a file, for some reason
|
94
|
+
)
|
95
|
+
FileUtils.rm('.keyfile.json')
|
96
|
+
|
97
|
+
bucket = storage.bucket(bucket_name, skip_lookup: true)
|
98
|
+
|
99
|
+
files_number = bucket.files(prefix: 'uploads/').all(request_limit: 10_000).count
|
100
|
+
if confirm_proceed("#{files_number} files will be downloaded. Do you want to continue?", exit_on_no: false)
|
101
|
+
say "WARNING: There is no way to estimate the total size of all assets, make sure you have enough disk space!", :yellow
|
102
|
+
say "WARNING: This will take a while, depending on the number of files", :yellow
|
103
|
+
say "Started pulling assets from #{destination} | #{DateTime.now.strftime("%H:%M:%S")}", :white
|
104
|
+
|
105
|
+
FileUtils.rm_rf('public/uploads')
|
106
|
+
FileUtils.mkdir_p('public/uploads')
|
107
|
+
local_folder = 'public/uploads'
|
108
|
+
|
109
|
+
progress_bar = TTY::ProgressBar.new("Downloading [:bar] :current/:total files", total: files_number, width: 60)
|
110
|
+
|
111
|
+
bucket = storage.bucket(bucket_name, skip_lookup: true)
|
112
|
+
bucket.files(prefix: 'uploads/').all(request_limit: 1000) do |file|
|
113
|
+
local_file_path = File.join(local_folder, file.name.sub('uploads/', ''))
|
114
|
+
FileUtils.mkdir_p(File.dirname(local_file_path))
|
115
|
+
|
116
|
+
file.download(local_file_path)
|
117
|
+
|
118
|
+
progress_bar.advance
|
119
|
+
end
|
120
|
+
|
121
|
+
say "Assets pulled successfully to #{local_folder} | #{DateTime.now.strftime("%H:%M:%S")}", :green
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def ask_for_backup_filename
|
127
|
+
prompt = TTY::Prompt.new
|
128
|
+
prompt.ask("Enter the backup filename:", required: true) do |q|
|
129
|
+
q.modify :trim
|
130
|
+
q.validate(/^[\w\-. ]+$/, "Filename must contain only valid characters (letters, digits, dashes, underscores, periods, and spaces)")
|
131
|
+
q.default 'latest'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def remove_backup_file(backup_file)
|
136
|
+
File.delete("db/#{backup_file}.sql")
|
137
|
+
say "Local backup file removed", :white
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'active_support/encrypted_file'
|
4
|
+
require 'base64'
|
5
|
+
require 'yaml'
|
6
|
+
require 'openssl'
|
7
|
+
require 'bundler'
|
8
|
+
|
9
|
+
module Kaboom
|
10
|
+
module Config
|
11
|
+
include Thor::Actions
|
12
|
+
include Utils
|
13
|
+
|
14
|
+
DESTINATIONS = Dir.glob("./config/deploy.*.yml").map { |file| file.match(/deploy\.(\w+)\.yml/)[1].to_sym }.freeze
|
15
|
+
|
16
|
+
def validate_repository!
|
17
|
+
unless Dir.exist?('.git')
|
18
|
+
say_and_exit "Error: This command must be run in a Git repository", :red
|
19
|
+
end
|
20
|
+
|
21
|
+
unless File.exist?('Gemfile') && File.exist?('config/application.rb') && File.exist?('config/routes.rb')
|
22
|
+
say_and_exit "Error: This command must be run in a root folder of a Rails application", :red
|
23
|
+
end
|
24
|
+
|
25
|
+
unless File.exist?('config/credentials/development.key')
|
26
|
+
say_and_exit "Error: development.key credentials file not found (source it from 1Password)", :red
|
27
|
+
end
|
28
|
+
|
29
|
+
if DESTINATIONS.empty? && !File.exist?('./config/deploy.yml')
|
30
|
+
say_and_exit "Error: No deploy configuration files found in config directory.", :red
|
31
|
+
end
|
32
|
+
|
33
|
+
if Bundler::Dsl.evaluate('Gemfile', nil, {})&.ruby_version&.versions&.first != RUBY_VERSION
|
34
|
+
say_and_exit "Error: Ruby version in current projects Gemfile is not the same as the currently running Ruby version.", :red
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_env(destination)
|
39
|
+
env_file = ".env.#{destination}"
|
40
|
+
if File.exist?(env_file)
|
41
|
+
ENV.update(Dotenv::Environment.new(env_file))
|
42
|
+
else
|
43
|
+
|
44
|
+
say_and_exit "Environment file #{env_file} not found, run 'kamal envify -d destination'", :red
|
45
|
+
end
|
46
|
+
|
47
|
+
destination_clear_envs = YAML.load_file("config/deploy.#{destination}.yml")['env']['clear']
|
48
|
+
ENV.update(destination_clear_envs.transform_values(&:to_s))
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_credentials(destination)
|
52
|
+
credentials_key_file = "config/credentials/#{destination}.key"
|
53
|
+
credentials_file = "config/credentials/#{destination}.yml.enc"
|
54
|
+
|
55
|
+
unless File.exist?(credentials_file) && File.exist?(credentials_key_file)
|
56
|
+
say_and_exit "Credentials file #{credentials_file} not found, make sure it is present", :red
|
57
|
+
end
|
58
|
+
|
59
|
+
key = [File.read(credentials_key_file)].pack("H*")
|
60
|
+
encrypted_credentials = File.binread(credentials_file)
|
61
|
+
|
62
|
+
cipher = OpenSSL::Cipher.new('aes-128-gcm')
|
63
|
+
encrypted_data, iv, auth_tag = encrypted_credentials.split("--".freeze).map { |v| Base64.strict_decode64(v) }
|
64
|
+
|
65
|
+
cipher.decrypt
|
66
|
+
cipher.key = key
|
67
|
+
cipher.iv = iv
|
68
|
+
cipher.auth_tag = auth_tag
|
69
|
+
cipher.auth_data = ""
|
70
|
+
|
71
|
+
decrypted_data = cipher.update(encrypted_data)
|
72
|
+
decrypted_data << cipher.final
|
73
|
+
|
74
|
+
return YAML.safe_load(Marshal.load(decrypted_data), symbolize_names: true)
|
75
|
+
end
|
76
|
+
|
77
|
+
def database_config(destination)
|
78
|
+
config = YAML.load_file('config/database.yml')[destination]
|
79
|
+
{
|
80
|
+
name: ERB.new(config['database']).result,
|
81
|
+
username: ERB.new(config['username']).result.presence || 'rails',
|
82
|
+
password: ERB.new(config['password']).result
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kaboom
|
2
|
+
class DatabaseHelper
|
3
|
+
def self.connection_string(config)
|
4
|
+
"postgresql://#{config[:username]}:#{config[:password]}@127.0.0.2/#{config[:name]}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.database_size(connection_string, destination)
|
8
|
+
query = "SELECT pg_size_pretty(pg_database_size(current_database()));"
|
9
|
+
command = %Q{kamal accessory exec db --reuse -i -q "psql #{connection_string} -c \\"#{query}\\"" -d #{destination} 2>/dev/null}
|
10
|
+
result = `#{command}`.strip
|
11
|
+
result.match(/\n\s*(.+?)\s*\n\(/) ? $1 : "Unknown MB"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.pull_backup(connection_string, destination, backup_file)
|
15
|
+
command = %Q{kamal accessory exec db --reuse -i -q "pg_dump #{connection_string}" -d #{destination} > db/#{backup_file}.sql 2>/dev/null}
|
16
|
+
system(command)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.apply_backup(config, backup_file)
|
20
|
+
local_connection_string = "postgresql://#{config[:username]}:#{config[:password]}@localhost/template1"
|
21
|
+
|
22
|
+
commands = [
|
23
|
+
%Q{psql #{local_connection_string} -c "DROP DATABASE IF EXISTS #{config[:name]}" >/dev/null 2>&1},
|
24
|
+
%Q{psql #{local_connection_string} -c "CREATE DATABASE #{config[:name]};" >/dev/null 2>&1},
|
25
|
+
%Q{psql #{local_connection_string.gsub('template1', config[:name])} < #{backup_file} >/dev/null 2>&1},
|
26
|
+
%Q{RAILS_MASTER_KEY=`cat config/credentials/development.key` env bin/rails db:environment:set RAILS_ENV=development >/dev/null 2>&1},
|
27
|
+
%Q{psql #{local_connection_string} -c "ALTER DATABASE #{config[:name]} OWNER TO #{config[:username]};" >/dev/null 2>&1},
|
28
|
+
]
|
29
|
+
#require 'pry'; binding.pry
|
30
|
+
commands.all? { |cmd| system(cmd) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Kaboom
|
2
|
+
module Utils
|
3
|
+
def say_and_exit(message, color)
|
4
|
+
say message, color
|
5
|
+
exit(1)
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate_destination(destination)
|
9
|
+
unless Config::DESTINATIONS.include?(destination.to_sym)
|
10
|
+
say_and_exit("Error: Unknown destination '#{destination}', valid destinations are: #{Config::DESTINATIONS.join(', ')}", :red)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def confirm_production_action(command)
|
15
|
+
confirm_proceed("You are about to run a #{command} on a production environment. Are you sure you want to continue?")
|
16
|
+
end
|
17
|
+
|
18
|
+
def confirm_proceed(message, exit_on_no: true)
|
19
|
+
yes?(message + " (y/n):", :yellow) || (exit_on_no ? (say("Action aborted", :red) && exit(1)) : false)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/kaboom.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "kaboom/version"
|
4
|
+
require 'yaml'
|
5
|
+
require 'erb'
|
6
|
+
require 'dotenv'
|
7
|
+
require 'thor'
|
8
|
+
require 'tty-prompt'
|
9
|
+
require 'kamal'
|
10
|
+
require 'google/cloud/storage'
|
11
|
+
|
12
|
+
require_relative 'kaboom/helpers/utils'
|
13
|
+
require_relative 'kaboom/helpers/config'
|
14
|
+
require_relative 'kaboom/helpers/database_helper'
|
15
|
+
|
16
|
+
require_relative 'kaboom/backup'
|
17
|
+
|
18
|
+
module Kaboom
|
19
|
+
class Error < StandardError; end
|
20
|
+
|
21
|
+
class CLI < Thor
|
22
|
+
include Thor::Actions
|
23
|
+
include Utils
|
24
|
+
include Config
|
25
|
+
|
26
|
+
def self.exit_on_failure?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'backup', 'Run database backup/restore related commands'
|
31
|
+
subcommand 'backup', Kaboom::Backup
|
32
|
+
|
33
|
+
desc 'console DESTINATION', 'Run Rails console for deployed apps, defaults to staging'
|
34
|
+
method_option :role, aliases: '-r', type: :string, desc: 'Role to run the console at (web, sidekiq, cron...)', default: 'web'
|
35
|
+
def console(destination = 'staging')
|
36
|
+
validate_repository!
|
37
|
+
validate_destination(destination)
|
38
|
+
|
39
|
+
confirm_production_action('console') if destination == 'production'
|
40
|
+
run_command("bundle exec rails c", destination)
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'ssh DESTINATION', 'Run a bash SSH for deployed apps, defaults to staging'
|
44
|
+
method_option :role, aliases: '-r', type: :string, desc: 'Role to run the SSH at (web, sidekiq, cron...)', default: 'web'
|
45
|
+
def ssh(destination = 'staging')
|
46
|
+
validate_repository!
|
47
|
+
validate_destination(destination)
|
48
|
+
|
49
|
+
confirm_production_action('SSH') if destination == 'production'
|
50
|
+
run_command("bash", destination)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def run_command(command, destination)
|
56
|
+
say "Running #{command} on #{destination}...", :green
|
57
|
+
exec("kamal app exec -i --reuse -q '#{command}' -d #{destination} --roles=#{options[:role]}")
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bamboolab_kaboom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Your Name
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-08-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dotenv
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: kamal
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tty-prompt
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yaml
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: erb
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: google-cloud-storage
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: tty-progressbar
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: activesupport
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '6.0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '6.0'
|
153
|
+
description: Kaboom is a CLI tool to handle app maintenence, SSH connections, database
|
154
|
+
and assets backups for apps deployed by Kamal.
|
155
|
+
email:
|
156
|
+
- your.email@example.com
|
157
|
+
executables:
|
158
|
+
- kaboom
|
159
|
+
extensions: []
|
160
|
+
extra_rdoc_files: []
|
161
|
+
files:
|
162
|
+
- bin/kaboom
|
163
|
+
- lib/kaboom.rb
|
164
|
+
- lib/kaboom/backup.rb
|
165
|
+
- lib/kaboom/helpers/config.rb
|
166
|
+
- lib/kaboom/helpers/database_helper.rb
|
167
|
+
- lib/kaboom/helpers/utils.rb
|
168
|
+
- lib/kaboom/version.rb
|
169
|
+
homepage:
|
170
|
+
licenses:
|
171
|
+
- MIT
|
172
|
+
metadata: {}
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 2.7.0
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubygems_version: 3.5.11
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: A CLI for managing Rails apps deployed by Kamal.
|
192
|
+
test_files: []
|