veksel 0.2.2 → 0.3.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 +4 -4
- data/bin/veksel +0 -2
- data/lib/tasks/veksel_tasks.rake +10 -33
- data/lib/veksel/cli.rb +34 -29
- data/lib/veksel/commands/clean.rb +6 -17
- data/lib/veksel/commands/fork.rb +10 -9
- data/lib/veksel/commands/list.rb +45 -0
- data/lib/veksel/pg_cluster.rb +21 -0
- data/lib/veksel/railtie.rb +9 -7
- data/lib/veksel/suffix.rb +4 -3
- data/lib/veksel/version.rb +1 -1
- data/lib/veksel.rb +17 -10
- metadata +4 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 692cda4c884d09fd6388bc304b72a0c41270942654815bebf08a91025a164e89
|
4
|
+
data.tar.gz: 81dabd57bfdf2096d6ab75a5bc870a3e39ff122bc790758551830d11414fbcd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38a76367d2ab7f8603a646f8236c027acea8f5104a4c342169cbacf9d35a02878b9f477a924afe4f9421dc1a9fe0e1abc89bc3918404296e9f883517fceabeae
|
7
|
+
data.tar.gz: f919b8fcbdfa107a15e8d784731570656561a0c843b32d489324166431da4897e10e357b4dac2989910c7f49ea3c060888777c0eabcfd97708206b0be760fb1b
|
data/bin/veksel
CHANGED
data/lib/tasks/veksel_tasks.rake
CHANGED
@@ -8,40 +8,13 @@ namespace :veksel do
|
|
8
8
|
|
9
9
|
desc "List forked databases"
|
10
10
|
task list: 'db:load_config' do
|
11
|
-
require 'veksel/commands/
|
11
|
+
require 'veksel/commands/list'
|
12
12
|
|
13
13
|
db = ActiveRecord::Base.configurations.find_db_config('development')
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
if databases.empty?
|
19
|
-
puts "No databases created by Veksel"
|
20
|
-
next
|
21
|
-
end
|
22
|
-
|
23
|
-
hash = {}
|
24
|
-
databases.each do |database|
|
25
|
-
branch = database.sub(command.prefix, '')
|
26
|
-
hash[branch] = database
|
27
|
-
end
|
28
|
-
|
29
|
-
longest_branch_name = hash.keys.max_by(&:length).length
|
30
|
-
longest_database_name = hash.values.max_by(&:length).length
|
31
|
-
puts "Databases created by Veksel:"
|
32
|
-
puts ""
|
33
|
-
puts "#{'Branch'.ljust(longest_branch_name)} #{'Database'.ljust(longest_database_name)} Active"
|
34
|
-
inactive_count = 0
|
35
|
-
hash.each do |branch, database|
|
36
|
-
# Print a formatted string padded to fit the longest branch name
|
37
|
-
active = active_branches.include?(branch) ? 'Yes' : 'No'
|
38
|
-
inactive_count += 1 if active == 'No'
|
39
|
-
puts "#{branch.ljust(longest_branch_name)} #{database.ljust(longest_database_name)} #{active}"
|
40
|
-
end
|
41
|
-
|
42
|
-
if inactive_count > 0
|
43
|
-
puts ""
|
44
|
-
puts "Clean inactive databases with bin/rails veksel:clean"
|
14
|
+
begin
|
15
|
+
Veksel::Commands::List.new(db).perform
|
16
|
+
rescue Veksel::AdapterNotSupported => e
|
17
|
+
$stderr.puts e.message
|
45
18
|
end
|
46
19
|
end
|
47
20
|
|
@@ -50,6 +23,10 @@ namespace :veksel do
|
|
50
23
|
require 'veksel/commands/clean'
|
51
24
|
|
52
25
|
db = ActiveRecord::Base.configurations.find_db_config('development')
|
53
|
-
|
26
|
+
begin
|
27
|
+
Veksel::Commands::Clean.new(db).perform
|
28
|
+
rescue Veksel::AdapterNotSupported => e
|
29
|
+
$stderr.puts "#{e.message} - clean skipped"
|
30
|
+
end
|
54
31
|
end
|
55
32
|
end
|
data/lib/veksel/cli.rb
CHANGED
@@ -2,39 +2,44 @@ require 'veksel'
|
|
2
2
|
|
3
3
|
module Veksel
|
4
4
|
module CLI
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
DbConfig = Struct.new('DbConfig', :configuration_hash)
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def fork
|
9
|
+
return if Veksel.skip_fork?
|
10
|
+
t1 = Time.now.to_f
|
11
|
+
|
12
|
+
require 'veksel/commands/fork'
|
13
|
+
require 'psych'
|
14
|
+
require 'fileutils'
|
15
|
+
|
16
|
+
config = read_config('config/database.yml')[:development]
|
17
|
+
if config.key?(:primary)
|
18
|
+
config = config[:primary]
|
19
|
+
$stderr.puts "Warning: Only your primary database will be forked"
|
20
|
+
end
|
21
|
+
|
22
|
+
target_database = config[:database] + Veksel.suffix
|
23
|
+
db = DbConfig.new(config)
|
24
|
+
if Veksel::Commands::Fork.new(db).perform
|
25
|
+
duration = ((Time.now.to_f - t1) * 1000).to_i
|
26
|
+
FileUtils.touch('tmp/restart.txt')
|
27
|
+
puts "Forked database in #{duration.to_i}ms"
|
28
|
+
end
|
29
|
+
rescue AdapterNotSupported => e
|
30
|
+
$stderr.puts "#{e.message} - fork skipped"
|
31
|
+
end
|
23
32
|
|
24
|
-
|
25
|
-
|
26
|
-
puts "Forked database in #{duration.to_i}ms"
|
27
|
-
end
|
33
|
+
def read_config(path)
|
34
|
+
raw_config = File.read(path)
|
28
35
|
|
29
|
-
|
30
|
-
|
36
|
+
if raw_config.include?('<%=')
|
37
|
+
require 'erb'
|
38
|
+
raw_config = ERB.new(raw_config).result
|
39
|
+
end
|
31
40
|
|
32
|
-
|
33
|
-
require 'erb'
|
34
|
-
raw_config = ERB.new(raw_config).result
|
41
|
+
Psych.safe_load(raw_config, aliases: true, symbolize_names: true)
|
35
42
|
end
|
36
|
-
|
37
|
-
Psych.safe_load(raw_config, aliases: true, symbolize_names: true)
|
38
43
|
end
|
39
44
|
end
|
40
45
|
end
|
@@ -1,33 +1,22 @@
|
|
1
|
-
require_relative '../pg_cluster'
|
2
|
-
|
3
1
|
module Veksel
|
4
2
|
module Commands
|
5
3
|
class Clean
|
6
|
-
attr_reader :prefix
|
7
|
-
|
8
4
|
def initialize(db, dry_run: false)
|
9
|
-
@
|
10
|
-
@prefix = Veksel.prefix(db.configuration_hash[:database])
|
5
|
+
@adapter = Veksel.adapter_for(db.configuration_hash)
|
11
6
|
@dry_run = dry_run
|
12
7
|
end
|
13
8
|
|
14
9
|
def perform
|
15
|
-
all_databases = @
|
10
|
+
all_databases = @adapter.forked_databases
|
11
|
+
active_branches = Veksel.active_branches
|
12
|
+
|
16
13
|
stale_databases = all_databases.filter do |database|
|
17
|
-
active_branches.none? { |branch| database.
|
14
|
+
active_branches.none? { |branch| database.branch == branch }
|
18
15
|
end
|
19
16
|
stale_databases.each do |database|
|
20
|
-
@
|
17
|
+
@adapter.drop_database(database.name, dry_run: @dry_run)
|
21
18
|
end
|
22
19
|
end
|
23
|
-
|
24
|
-
def active_branches
|
25
|
-
`git for-each-ref 'refs/heads/' --format '%(refname)'`.split("\n").map { |ref| ref.sub('refs/heads/', '') }
|
26
|
-
end
|
27
|
-
|
28
|
-
def all_databases
|
29
|
-
@pg_cluster.list_databases(prefix: @prefix)
|
30
|
-
end
|
31
20
|
end
|
32
21
|
end
|
33
22
|
end
|
data/lib/veksel/commands/fork.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
|
-
require_relative '../pg_cluster'
|
2
|
-
|
3
1
|
module Veksel
|
4
2
|
module Commands
|
5
3
|
class Fork
|
6
|
-
attr_reader :
|
4
|
+
attr_reader :adapter, :source_db, :target_db
|
7
5
|
|
8
6
|
def initialize(db)
|
9
|
-
@
|
10
|
-
@source_db =
|
11
|
-
@target_db =
|
7
|
+
@adapter = Veksel.adapter_for(db.configuration_hash)
|
8
|
+
@source_db = adapter.main_database
|
9
|
+
@target_db = adapter.db_name_for_suffix(Veksel.suffix)
|
12
10
|
raise "Source and target databases cannot be the same" if source_db == target_db
|
13
11
|
end
|
14
12
|
|
15
13
|
def perform
|
16
|
-
return
|
14
|
+
return false unless adapter
|
15
|
+
return false if adapter.target_populated?(target_db)
|
16
|
+
|
17
|
+
adapter.kill_connection(source_db)
|
18
|
+
adapter.create_database(target_db, template: source_db)
|
17
19
|
|
18
|
-
|
19
|
-
pg_cluster.create_database(target_db, template: source_db)
|
20
|
+
true
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Veksel
|
2
|
+
module Commands
|
3
|
+
class List
|
4
|
+
attr_reader :adapter
|
5
|
+
|
6
|
+
def initialize(db)
|
7
|
+
@adapter = Veksel.adapter_for(db.configuration_hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform
|
11
|
+
databases = adapter.forked_databases
|
12
|
+
active_branches = Veksel.active_branches
|
13
|
+
|
14
|
+
if databases.empty?
|
15
|
+
puts "No databases created by Veksel"
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
hash = {}
|
20
|
+
databases.each do |database|
|
21
|
+
branch = database.branch
|
22
|
+
hash[branch] = database.name
|
23
|
+
end
|
24
|
+
|
25
|
+
longest_branch_name = hash.keys.max_by(&:length).length
|
26
|
+
longest_database_name = hash.values.max_by(&:length).length
|
27
|
+
puts "Databases created by Veksel:"
|
28
|
+
puts ""
|
29
|
+
puts "#{'Branch'.ljust(longest_branch_name)} #{'Database'.ljust(longest_database_name)} Active"
|
30
|
+
inactive_count = 0
|
31
|
+
hash.each do |branch, database|
|
32
|
+
# Print a formatted string padded to fit the longest branch name
|
33
|
+
active = active_branches.include?(branch) ? 'Yes' : 'No'
|
34
|
+
inactive_count += 1 if active == 'No'
|
35
|
+
puts "#{branch.ljust(longest_branch_name)} #{database.ljust(longest_database_name)} #{active}"
|
36
|
+
end
|
37
|
+
|
38
|
+
if inactive_count > 0
|
39
|
+
puts ""
|
40
|
+
puts "Clean inactive databases with bin/rails veksel:clean"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/veksel/pg_cluster.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
1
|
module Veksel
|
2
2
|
class PgCluster
|
3
|
+
Database = Struct.new(:name, :branch)
|
4
|
+
|
3
5
|
attr_reader :configuration_hash
|
4
6
|
|
5
7
|
def initialize(configuration_hash)
|
6
8
|
@configuration_hash = configuration_hash
|
7
9
|
end
|
8
10
|
|
11
|
+
def main_database
|
12
|
+
configuration_hash[:veksel_main_database] || configuration_hash[:database]
|
13
|
+
end
|
14
|
+
|
15
|
+
def forked_databases
|
16
|
+
list_databases(prefix: forked_database_prefix).map do |name|
|
17
|
+
Database.new(name, name.sub(forked_database_prefix, ''))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def db_name_for_suffix(suffix)
|
22
|
+
return main_database if suffix.empty?
|
23
|
+
"#{forked_database_prefix}#{suffix}"
|
24
|
+
end
|
25
|
+
|
9
26
|
def target_populated?(dbname)
|
10
27
|
IO.pipe do |r, w|
|
11
28
|
spawn(pg_env, %[psql -t #{pg_connection_args(dbname)} -c "SELECT 'ok' FROM ar_internal_metadata LIMIT 1;"], out: w, err: '/dev/null')
|
@@ -42,6 +59,10 @@ module Veksel
|
|
42
59
|
|
43
60
|
private
|
44
61
|
|
62
|
+
def forked_database_prefix
|
63
|
+
"#{main_database}_"
|
64
|
+
end
|
65
|
+
|
45
66
|
def pg_connection_args(dbname)
|
46
67
|
"-d #{dbname} --no-password"
|
47
68
|
end
|
data/lib/veksel/railtie.rb
CHANGED
@@ -11,14 +11,16 @@ module Veksel
|
|
11
11
|
ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
|
12
12
|
next if url.present?
|
13
13
|
next unless env_name == 'development' || env_name == 'test'
|
14
|
+
veksel_adapter = Veksel.adapter_for(config, exception: false)
|
15
|
+
next unless veksel_adapter
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
database_name = veksel_adapter.db_name_for_suffix(Veksel.suffix)
|
18
|
+
next unless veksel_adapter.target_populated?(database_name)
|
19
|
+
|
20
|
+
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, name, config.merge({
|
21
|
+
database: database_name,
|
22
|
+
veksel_main_database: config[:database],
|
23
|
+
}))
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
data/lib/veksel/suffix.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
module Veksel
|
2
|
-
PROTECTED_BRANCHES = %w[master main HEAD].freeze
|
3
|
-
|
4
2
|
class Suffix
|
3
|
+
PROTECTED_BRANCHES = %w[master main].freeze
|
4
|
+
|
5
5
|
def initialize(branch_name)
|
6
6
|
@branch_name = branch_name
|
7
7
|
end
|
8
8
|
|
9
9
|
def to_s
|
10
10
|
case @branch_name
|
11
|
+
when 'HEAD'
|
11
12
|
when *PROTECTED_BRANCHES
|
12
13
|
""
|
13
14
|
else
|
14
|
-
|
15
|
+
@branch_name
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
data/lib/veksel/version.rb
CHANGED
data/lib/veksel.rb
CHANGED
@@ -1,19 +1,30 @@
|
|
1
|
-
if defined?(Rails::VERSION)
|
2
|
-
if Rails::VERSION::MAJOR < 6
|
3
|
-
raise "Veksel requires Rails 6 or later"
|
4
|
-
end
|
5
|
-
end
|
6
|
-
|
7
1
|
require "veksel/version"
|
8
2
|
require "veksel/railtie" if defined?(Rails::Railtie)
|
9
3
|
require "veksel/suffix"
|
10
4
|
|
11
5
|
module Veksel
|
6
|
+
class AdapterNotSupported < StandardError; end
|
7
|
+
|
12
8
|
class << self
|
9
|
+
def adapter_for(config, exception: true)
|
10
|
+
case config[:adapter]
|
11
|
+
when 'postgresql'
|
12
|
+
require_relative './veksel/pg_cluster'
|
13
|
+
Veksel::PgCluster.new(config)
|
14
|
+
else
|
15
|
+
return unless exception
|
16
|
+
raise AdapterNotSupported, "Veksel does not yet support #{config[:adapter]}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
def current_branch
|
14
21
|
`git rev-parse --abbrev-ref HEAD`.strip
|
15
22
|
end
|
16
23
|
|
24
|
+
def active_branches
|
25
|
+
`git for-each-ref 'refs/heads/' --format '%(refname)'`.split("\n").map { |ref| ref.sub('refs/heads/', '') }
|
26
|
+
end
|
27
|
+
|
17
28
|
def skip_fork?
|
18
29
|
suffix.to_s.strip.empty?
|
19
30
|
end
|
@@ -21,9 +32,5 @@ module Veksel
|
|
21
32
|
def suffix
|
22
33
|
Suffix.new(current_branch).to_s
|
23
34
|
end
|
24
|
-
|
25
|
-
def prefix(dbname)
|
26
|
-
dbname.sub(%r[#{Veksel.suffix}$], '_')
|
27
|
-
end
|
28
35
|
end
|
29
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: veksel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theodor Tonum
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,20 +44,6 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '0'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: pg
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - ">="
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
54
|
-
type: :runtime
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - ">="
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '0'
|
61
47
|
description: Seperate databases for every branch in your development environment
|
62
48
|
email:
|
63
49
|
- theodor@tonum.no
|
@@ -75,6 +61,7 @@ files:
|
|
75
61
|
- lib/veksel/cli.rb
|
76
62
|
- lib/veksel/commands/clean.rb
|
77
63
|
- lib/veksel/commands/fork.rb
|
64
|
+
- lib/veksel/commands/list.rb
|
78
65
|
- lib/veksel/pg_cluster.rb
|
79
66
|
- lib/veksel/railtie.rb
|
80
67
|
- lib/veksel/suffix.rb
|
@@ -102,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
89
|
- !ruby/object:Gem::Version
|
103
90
|
version: '0'
|
104
91
|
requirements: []
|
105
|
-
rubygems_version: 3.
|
92
|
+
rubygems_version: 3.5.16
|
106
93
|
signing_key:
|
107
94
|
specification_version: 4
|
108
95
|
summary: Veksel keeps seperate databases for every branch in your development environment.
|