prick 0.18.0 → 0.20.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 +4 -4
- data/Gemfile +10 -4
- data/README.md +7 -7
- data/Rakefile +3 -1
- data/TODO +13 -11
- data/bin/console +2 -1
- data/doc/build-yml.txt +14 -0
- data/exe/prick +264 -28
- data/lib/builder/batch.rb +147 -0
- data/lib/builder/builder.rb +122 -0
- data/lib/builder/node.rb +189 -0
- data/lib/builder/node_pool.rb +105 -0
- data/lib/builder/parser.rb +120 -0
- data/lib/local/command.rb +193 -0
- data/lib/{prick → local}/git.rb +148 -22
- data/lib/local/timer.rb +98 -0
- data/lib/prick/constants.rb +54 -66
- data/lib/prick/diff.rb +28 -18
- data/lib/prick/prick_version.rb +161 -0
- data/lib/prick/state.rb +80 -165
- data/lib/prick/version.rb +2 -163
- data/lib/prick.rb +43 -27
- data/lib/share/init/.gitignore +10 -0
- data/lib/share/init/.prick-context +2 -0
- data/lib/share/init/.rspec +3 -0
- data/{share/schema/schema/public → lib/share/init/migration}/.keep +0 -0
- data/lib/share/init/prick.yml +6 -0
- data/lib/share/init/schema/.keep +0 -0
- data/lib/share/init/schema/build.yml +2 -0
- data/lib/share/init/schema/prick/.keep +0 -0
- data/lib/share/init/schema/prick/build.yml +5 -0
- data/lib/share/init/schema/prick/data.sql +6 -0
- data/{share/schema → lib/share/init}/schema/prick/tables.sql +2 -3
- data/lib/share/init/schema/public/.keep +0 -0
- data/lib/share/init/spec/prick_helper.rb +1 -0
- data/lib/share/init/spec/prick_spec.rb +6 -0
- data/lib/share/init/spec/spec_helper.rb +50 -0
- data/lib/share/migrate/migration/build.yml +4 -0
- data/lib/share/migrate/migration/diff.after-tables.sql +0 -0
- data/lib/share/migrate/migration/diff.before-tables.sql +0 -0
- data/lib/share/migrate/migration/diff.tables.sql +0 -0
- data/lib/subcommand/prick-build.rb +55 -0
- data/lib/subcommand/prick-create.rb +78 -0
- data/lib/subcommand/prick-drop.rb +25 -0
- data/lib/subcommand/prick-fox.rb +62 -0
- data/lib/subcommand/prick-init.rb +46 -0
- data/lib/subcommand/prick-make.rb +202 -0
- data/lib/subcommand/prick-migrate.rb +37 -0
- data/lib/subcommand/prick-release.rb +23 -0
- data/lib/subcommand/prick-setup.rb +20 -0
- data/lib/subcommand/prick-teardown.rb +18 -0
- data/prick.gemspec +43 -16
- metadata +161 -72
- data/.gitignore +0 -29
- data/.travis.yml +0 -7
- data/doc/create_release.txt +0 -17
- data/doc/flow.txt +0 -98
- data/doc/migra +0 -1
- data/doc/migrations.txt +0 -172
- data/doc/notes.txt +0 -116
- data/doc/prick.txt +0 -114
- data/doc/sh.prick +0 -316
- data/lib/ext/algorithm.rb +0 -14
- data/lib/ext/fileutils.rb +0 -26
- data/lib/ext/forward_method.rb +0 -18
- data/lib/ext/pg.rb +0 -18
- data/lib/ext/shortest_path.rb +0 -44
- data/lib/prick/archive.rb +0 -124
- data/lib/prick/branch.rb +0 -254
- data/lib/prick/builder.rb +0 -205
- data/lib/prick/cache.rb +0 -34
- data/lib/prick/command.rb +0 -102
- data/lib/prick/database.rb +0 -82
- data/lib/prick/dsort.rb +0 -151
- data/lib/prick/ensure.rb +0 -119
- data/lib/prick/exceptions.rb +0 -25
- data/lib/prick/head.rb +0 -183
- data/lib/prick/migration.rb +0 -70
- data/lib/prick/program.rb +0 -506
- data/lib/prick/project.rb +0 -626
- data/lib/prick/rdbms.rb +0 -137
- data/lib/prick/schema.rb +0 -27
- data/lib/prick/share.rb +0 -64
- data/libexec/strip-comments +0 -33
- data/make_releases +0 -72
- data/make_schema +0 -10
- data/share/diff/diff.after-tables.sql +0 -4
- data/share/diff/diff.before-tables.sql +0 -4
- data/share/diff/diff.tables.sql +0 -8
- data/share/features/diff.sql +0 -2
- data/share/features/feature/diff.sql +0 -2
- data/share/features/feature/migrate.sql +0 -2
- data/share/features/features.sql +0 -2
- data/share/features/features.yml +0 -2
- data/share/features/migrations.sql +0 -4
- data/share/gitignore +0 -2
- data/share/migration/diff.tables.sql +0 -8
- data/share/migration/features.yml +0 -6
- data/share/migration/migrate.sql +0 -3
- data/share/migration/migrate.yml +0 -8
- data/share/migration/tables.sql +0 -3
- data/share/schema/build.yml +0 -14
- data/share/schema/schema/build.yml +0 -3
- data/share/schema/schema/prick/build.yml +0 -14
- data/share/schema/schema/prick/data.sql +0 -7
- data/share/schema/schema/prick/schema.sql +0 -3
- data/share/schema/schema/public/build.yml +0 -13
- data/share/schema/schema.sql +0 -3
- data/test_assorted +0 -192
- data/test_feature +0 -112
- data/test_refactor +0 -34
- data/test_single_dev +0 -83
data/lib/prick/branch.rb
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
require "prick/state.rb"
|
|
2
|
-
|
|
3
|
-
module Prick
|
|
4
|
-
class Branch
|
|
5
|
-
# Branch name. It is usually equal to the version but migrations use a
|
|
6
|
-
# <base_version>_<version> format instead
|
|
7
|
-
attr_reader :name
|
|
8
|
-
|
|
9
|
-
# Version of this branch. Note that version and base_version are the same
|
|
10
|
-
# for feature branches
|
|
11
|
-
attr_reader :version
|
|
12
|
-
|
|
13
|
-
# Base version
|
|
14
|
-
attr_reader :base_version
|
|
15
|
-
|
|
16
|
-
# The release directory. It contains the release's .prick-migration file
|
|
17
|
-
attr_reader :directory
|
|
18
|
-
|
|
19
|
-
# The Schema object. This is shared by many all branches
|
|
20
|
-
attr_reader :schema
|
|
21
|
-
|
|
22
|
-
# Migration object. Running the migration object on the base release will
|
|
23
|
-
# migrate it to this release
|
|
24
|
-
attr_reader :migration
|
|
25
|
-
|
|
26
|
-
# Database name
|
|
27
|
-
def database() name end
|
|
28
|
-
|
|
29
|
-
# Classifiers
|
|
30
|
-
def release?() self.is_a?(Release) && !prerelease? end
|
|
31
|
-
def prerelease?() self.is_a?(PreRelease) end
|
|
32
|
-
def feature?() self.is_a?(Feature) end
|
|
33
|
-
def migration?() self.is_a?(MigrationRelease) end
|
|
34
|
-
|
|
35
|
-
# Note that `name` can be nil. It defaults to `version.to_s`
|
|
36
|
-
def initialize(name, version, base_version, directory, migration)
|
|
37
|
-
@name = name || migration.version.to_s
|
|
38
|
-
@version = version
|
|
39
|
-
@base_version = base_version
|
|
40
|
-
@directory = directory
|
|
41
|
-
@schema = Schema.new
|
|
42
|
-
@migration = migration
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def dump
|
|
46
|
-
puts "#{self.class}"
|
|
47
|
-
indent {
|
|
48
|
-
puts "name: #{name}"
|
|
49
|
-
puts "version: #{version}"
|
|
50
|
-
puts "base_version: #{base_version}"
|
|
51
|
-
puts "directory: #{directory}"
|
|
52
|
-
print "migration: "
|
|
53
|
-
migration.dump
|
|
54
|
-
puts "database: #{database}"
|
|
55
|
-
}
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def self.load(name) raise NotThis end
|
|
59
|
-
|
|
60
|
-
# True if the branch exists in git
|
|
61
|
-
def exist?() self.class.exist?(name) end
|
|
62
|
-
|
|
63
|
-
def create()
|
|
64
|
-
!exist? or raise Error, "Can't create branch #{name}, exists already"
|
|
65
|
-
Git.create_branch(name)
|
|
66
|
-
Git.checkout_branch(name)
|
|
67
|
-
prepare if !prepared?
|
|
68
|
-
self
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# True if the branch exists on disk
|
|
72
|
-
def present?() self.class.present?(name) end
|
|
73
|
-
|
|
74
|
-
def prepared?() @migration.exist? end
|
|
75
|
-
def prepare() @migration.create end
|
|
76
|
-
|
|
77
|
-
def include(feature_version) @migration.append_feature(feature_version) end
|
|
78
|
-
|
|
79
|
-
def build(database) schema.build(database) end
|
|
80
|
-
|
|
81
|
-
# Used to checkout migrations. MigrationReleases checks out the
|
|
82
|
-
# corresponding branch while Release and PreRelease checks out a tag
|
|
83
|
-
def checkout_release() Git.checkout_branch(version) end
|
|
84
|
-
|
|
85
|
-
def migrate(database)
|
|
86
|
-
@migration.migrate(database)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def migrate_features(database)
|
|
90
|
-
@migration.migrate_features(database)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def self.directory(name) raise NotThis end
|
|
94
|
-
def self.exist?(name) Git.branch?(name) || Git.tag?(name) end
|
|
95
|
-
def self.present?(name) File.exist?(directory(name)) end
|
|
96
|
-
|
|
97
|
-
def <=>(other)
|
|
98
|
-
if !self.is_a?(MigrationRelease) && other.is_a?(MigrationRelease)
|
|
99
|
-
compare(other.base_version, other.version)
|
|
100
|
-
else
|
|
101
|
-
compare(other.version, other.base_version)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
def compare(other_version, other_base_version)
|
|
107
|
-
r = version <=> other_version
|
|
108
|
-
return r if r != 0
|
|
109
|
-
if base_version.nil?
|
|
110
|
-
other_base_version.nil? ? 0 : -1
|
|
111
|
-
elsif other_base_version.nil?
|
|
112
|
-
1
|
|
113
|
-
else
|
|
114
|
-
base_version <=> other_base_version
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
class AbstractRelease < Branch
|
|
120
|
-
def create(schema_version = self.version)
|
|
121
|
-
super()
|
|
122
|
-
schema.version = schema_version
|
|
123
|
-
Git.add schema.version_file
|
|
124
|
-
self
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def checkout_release() Git.checkout_tag(version) end
|
|
128
|
-
|
|
129
|
-
def tag!() Git.create_tag(version) end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
class Release < AbstractRelease
|
|
133
|
-
def initialize(version, base_version)
|
|
134
|
-
!version.zero? || base_version.nil? or raise Internal, "Version 0.0.0 has no base release"
|
|
135
|
-
directory = self.class.directory(version.to_s)
|
|
136
|
-
migration = ReleaseMigration.new(version, base_version)
|
|
137
|
-
super(nil, version, base_version, directory, migration)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def self.load(name)
|
|
141
|
-
migration = ReleaseMigration.load(directory(name))
|
|
142
|
-
self.new(migration.version, migration.base_version)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def self.directory(name)
|
|
146
|
-
File.join(RELEASES_DIR, name)
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
class PreRelease < AbstractRelease
|
|
151
|
-
attr_reader :prerelease_version
|
|
152
|
-
|
|
153
|
-
def database() version.to_s end
|
|
154
|
-
|
|
155
|
-
def initialize(prerelease_version, base_version)
|
|
156
|
-
@prerelease_version = prerelease_version
|
|
157
|
-
version = prerelease_version.truncate(:pre)
|
|
158
|
-
directory = Release.directory(version.to_s)
|
|
159
|
-
migration = ReleaseMigration.new(version, base_version)
|
|
160
|
-
super(prerelease_version.to_s, version, base_version, directory, migration)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def self.load(name, prerelease_version)
|
|
164
|
-
migration = ReleaseMigration.load(directory(name))
|
|
165
|
-
self.new(prerelease_version, migration.base_version)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def create()
|
|
169
|
-
super(prerelease_version)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def increment
|
|
173
|
-
PreRelease.new(prerelease_version.increment(:pre), base_version)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def self.directory(name)
|
|
177
|
-
version = Version.new(name)
|
|
178
|
-
Release.directory(version.truncate(:pre).to_s)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
class MigrationRelease < Branch
|
|
183
|
-
def database() version.to_s end
|
|
184
|
-
|
|
185
|
-
def initialize(version, base_version)
|
|
186
|
-
migration = MigrationMigration.new(version, base_version)
|
|
187
|
-
super(migration.name, version, base_version, migration.dir, migration)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def self.load(name)
|
|
191
|
-
directory = self.directory(name)
|
|
192
|
-
migration_state = MigrationState.new(directory).read
|
|
193
|
-
self.new(migration_state.version, migration_state.base_version)
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def self.directory(name)
|
|
197
|
-
File.join(MIGRATIONS_DIR, name)
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Feature maintains a migration in the parent release but it is ignored when
|
|
202
|
-
# including the feature
|
|
203
|
-
class Feature < Branch
|
|
204
|
-
attr_reader :feature_name
|
|
205
|
-
|
|
206
|
-
def database() base_version.to_s end
|
|
207
|
-
|
|
208
|
-
def initialize(feature_name, base_version)
|
|
209
|
-
version = Version.new(base_version, feature: feature_name)
|
|
210
|
-
migration = FeatureMigration.new(feature_name, base_version)
|
|
211
|
-
super(version.to_s, version, base_version, migration.features_dir, migration)
|
|
212
|
-
@feature_name = feature_name
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def create
|
|
216
|
-
super()
|
|
217
|
-
schema.version = version
|
|
218
|
-
Git.add schema.version_file
|
|
219
|
-
self
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def include(feature_version) @migration.insert_feature(feature_version) end
|
|
223
|
-
|
|
224
|
-
def rebase(base_version)
|
|
225
|
-
new_version = Version.new(new_base_version, feature: feature_name)
|
|
226
|
-
name = new_version.to_s
|
|
227
|
-
|
|
228
|
-
Git.create_branch(name)
|
|
229
|
-
Git.checkout_branch(name)
|
|
230
|
-
|
|
231
|
-
symlink = self.directory(name)
|
|
232
|
-
FileUtils.ln_sr(directory, symlink, force: true)
|
|
233
|
-
Git.add(symlink)
|
|
234
|
-
|
|
235
|
-
migration.base_version = new_base_version
|
|
236
|
-
migration.save
|
|
237
|
-
|
|
238
|
-
schema.version = new_version
|
|
239
|
-
Git.add ect.schema.version_file
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def self.load(name)
|
|
243
|
-
directory = self.directory(name)
|
|
244
|
-
migration_state = MigrationState.new(directory).read
|
|
245
|
-
self.new(migration_state.version.feature, migration_state.base_version)
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def self.directory(name)
|
|
249
|
-
version = Version.new(name)
|
|
250
|
-
File.join(Release.directory(version.truncate(:feature).to_s), version.feature.to_s)
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
data/lib/prick/builder.rb
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
require "prick/state.rb"
|
|
2
|
-
|
|
3
|
-
module Prick
|
|
4
|
-
# Builder is a procedural object for building schemas and executing
|
|
5
|
-
# migrations. Builder use resources that can be executables, SQL files, YAML
|
|
6
|
-
# files, or directories. A resource is identified by a name (eg. 'my-tables')
|
|
7
|
-
# and is used to match a build file. Build files are looked up in the
|
|
8
|
-
# following order:
|
|
9
|
-
#
|
|
10
|
-
# #{name} executable
|
|
11
|
-
# #{name}.* executable
|
|
12
|
-
# #{name}.yml
|
|
13
|
-
# #{name}.sql
|
|
14
|
-
# #{name}/
|
|
15
|
-
#
|
|
16
|
-
# The output from executable objects is expected to be SQL statements that
|
|
17
|
-
# are fed into postgres
|
|
18
|
-
#
|
|
19
|
-
# When a resource match a directory, the directory can contain a special
|
|
20
|
-
# default resource ('build' or 'migrate') that takes over the rest of the
|
|
21
|
-
# build process for that directory. Typically, you'll use the 'build.yml' or
|
|
22
|
-
# 'migrate.yml' to control the build order of the other resources. If a
|
|
23
|
-
# directory doesn't contain a build resource, the resources in the directory
|
|
24
|
-
# are built in alphabetic order
|
|
25
|
-
#
|
|
26
|
-
# Build (but not migration) SQL scripts and executables are executed with
|
|
27
|
-
# search path set to containing schema
|
|
28
|
-
#
|
|
29
|
-
class Builder
|
|
30
|
-
attr_reader :database
|
|
31
|
-
attr_reader :directory
|
|
32
|
-
attr_reader :default # either "build" or "migrate"
|
|
33
|
-
attr_reader :lines
|
|
34
|
-
|
|
35
|
-
def initialize(database, directory, default)
|
|
36
|
-
@database = database
|
|
37
|
-
@directory = directory
|
|
38
|
-
@default = default
|
|
39
|
-
@execute = true
|
|
40
|
-
@lines = []
|
|
41
|
-
@cwd = Dir.getwd # used in error messages
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def build(subject = default, execute: true)
|
|
45
|
-
@execute = execute
|
|
46
|
-
@lines = []
|
|
47
|
-
# puts "Building #{subject.inspect}"
|
|
48
|
-
Dir.chdir(directory) {
|
|
49
|
-
if subject
|
|
50
|
-
build_subject(subject)
|
|
51
|
-
else
|
|
52
|
-
build_directory(".")
|
|
53
|
-
end
|
|
54
|
-
}
|
|
55
|
-
@lines.reject! { |l| l =~ /^\s*--/ || l =~ /^\s*$/ }
|
|
56
|
-
self
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def self.yml_file(directory) raise NotThis end
|
|
60
|
-
|
|
61
|
-
protected
|
|
62
|
-
def do_dir(path, &block)
|
|
63
|
-
# puts "do_dir(#{path.inspect})"
|
|
64
|
-
dir, file = File.split(path)
|
|
65
|
-
Dir.chdir(dir) { yield(file) }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def do_sql(path, schema: nil)
|
|
69
|
-
# puts "do_sql(#{path})"
|
|
70
|
-
do_dir(path) { |file|
|
|
71
|
-
if @execute
|
|
72
|
-
begin
|
|
73
|
-
Rdbms.exec_file(database.name, file, user: database.user, schema: schema)
|
|
74
|
-
rescue Command::Error => ex
|
|
75
|
-
$stderr.puts ex.stderr
|
|
76
|
-
$stderr.puts "in #{reldir}/#{file}"
|
|
77
|
-
exit 1
|
|
78
|
-
end
|
|
79
|
-
else
|
|
80
|
-
@lines += IO.readlines(file).map(&:chomp)
|
|
81
|
-
end
|
|
82
|
-
}
|
|
83
|
-
true
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def do_exe(path, args = [], schema: nil)
|
|
87
|
-
# puts "do_exe(#{path.inspect}, #{args.inspect})"
|
|
88
|
-
do_dir(path) { |file|
|
|
89
|
-
cmd = (["./#{file}"] + args).join(" ")
|
|
90
|
-
lines = Command.command cmd, stdin: [database.name, database.user]
|
|
91
|
-
if @execute
|
|
92
|
-
begin
|
|
93
|
-
Rdbms.exec_sql(database.name, lines.join("\n"), user: database.user, schema: schema)
|
|
94
|
-
rescue Command::Error => ex
|
|
95
|
-
$stderr.puts ex.stderr
|
|
96
|
-
$stderr.puts "from #{reldir}/#{file}"
|
|
97
|
-
exit 1
|
|
98
|
-
end
|
|
99
|
-
else
|
|
100
|
-
@lines += lines
|
|
101
|
-
end
|
|
102
|
-
}
|
|
103
|
-
true
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def do_yml(path)
|
|
107
|
-
# puts "do_yml(#{path})"
|
|
108
|
-
dir, file = File.split(path)
|
|
109
|
-
YAML.load(File.read(path))&.each { |subject| build_subject(File.join(dir, subject)) }
|
|
110
|
-
true
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# A subject can be both an abstract subject or a concrete file (*.yml, *.sql)
|
|
114
|
-
def build_subject(subject)
|
|
115
|
-
cmd, *args = subject.split(/\s+/)
|
|
116
|
-
if File.file?(cmd) && File.executable?(cmd)
|
|
117
|
-
do_exe(cmd, args)
|
|
118
|
-
elsif File.file?(subject) && subject.end_with?(".yml")
|
|
119
|
-
do_yml(subject)
|
|
120
|
-
elsif File.file?(subject) && subject.end_with?(".sql")
|
|
121
|
-
do_sql(subject)
|
|
122
|
-
elsif File.exist?(yml_file = "#{subject}.yml")
|
|
123
|
-
do_yml(yml_file)
|
|
124
|
-
elsif File.exist?(sql_file = "#{subject}.sql")
|
|
125
|
-
do_sql(sql_file)
|
|
126
|
-
elsif File.directory?(subject)
|
|
127
|
-
build_directory(subject)
|
|
128
|
-
else
|
|
129
|
-
false
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def build_directory(path)
|
|
134
|
-
Dir.chdir(path) {
|
|
135
|
-
build_subject(File.join(path, @default)) || begin
|
|
136
|
-
if Dir["#{path}/#{default}", "#{path}/#{default}.yml", "#{path}/#{default}.sql"]
|
|
137
|
-
subjects = [default]
|
|
138
|
-
else
|
|
139
|
-
exes = candidates.select { |file| File.file?(file) && File.executable?(file) }
|
|
140
|
-
ymls = candidates.select { |file| file.end_with?(".yml") }.map { |f| f.sub(/\.yml$/, "") }
|
|
141
|
-
sqls = candidates.select { |file| file.end_with?(".sql") }.map { |f| f.sub(/\.sql$/, "") }
|
|
142
|
-
dirs = candidates.select { |file| File.directory?(file) }
|
|
143
|
-
subjects = (exes + ymls + sqls + dirs).uniq.sort.reject { |f| f != "diff" }
|
|
144
|
-
end
|
|
145
|
-
subjects.inject(false) { |a, s| build_subject(s) || a }
|
|
146
|
-
end
|
|
147
|
-
}
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
private
|
|
151
|
-
# Return the relative path to the current directory from the directory of
|
|
152
|
-
# the time of the instantiation of the Builder object. Used in error
|
|
153
|
-
# messages
|
|
154
|
-
def reldir
|
|
155
|
-
Dir.getwd.sub(/^#{@cwd}\//, "")
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
class MigrationBuilder < Builder
|
|
160
|
-
def initialize(database, directory)
|
|
161
|
-
super(database, directory, "migrate")
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def self.yml_file(directory) File.join(directory, "migrate") + ".yml" end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
class SchemaBuilder < Builder
|
|
168
|
-
def initialize(database, directory)
|
|
169
|
-
super(database, directory, "build")
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def build(subject = nil, execute: true)
|
|
173
|
-
if subject
|
|
174
|
-
@execute = execute
|
|
175
|
-
@lines = []
|
|
176
|
-
Dir.chdir(directory) {
|
|
177
|
-
if File.executable?(subject)
|
|
178
|
-
do_exe(subject)
|
|
179
|
-
elsif subject.end_with?(".sql")
|
|
180
|
-
do_sql(subject)
|
|
181
|
-
else
|
|
182
|
-
build_subject(subject)
|
|
183
|
-
end
|
|
184
|
-
}
|
|
185
|
-
else
|
|
186
|
-
super(execute: execute)
|
|
187
|
-
end
|
|
188
|
-
self
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def self.yml_file(directory) File.join(directory, "build") + ".yml" end
|
|
192
|
-
|
|
193
|
-
protected
|
|
194
|
-
def do_sql(path)
|
|
195
|
-
schema = Dir.getwd.sub(/^.*schema\/([^\/]+)(?:\/.*)?$/, '\1')
|
|
196
|
-
super(path, schema: schema)
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def do_exe(path, args = [])
|
|
200
|
-
schema = Dir.getwd.sub(/^.*schema\/([^\/]+)(?:\/.*)?$/, '\1')
|
|
201
|
-
super(path, args, schema: schema)
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
data/lib/prick/cache.rb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module Prick
|
|
3
|
-
class Cache
|
|
4
|
-
def initialize
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def exist?(version)
|
|
8
|
-
File.exist?(file(version))
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def list
|
|
12
|
-
Dir.glob(File.join(CACHE_DIR, "*.sql.gz"))
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def load(database, version)
|
|
16
|
-
Rdbms.load(database, file(version))
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def save(database, version = database.version)
|
|
20
|
-
Rdbms.save(database, file(version))
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Returns list of removed files
|
|
24
|
-
def clean
|
|
25
|
-
l = list
|
|
26
|
-
FileUtils.rm(l)
|
|
27
|
-
l
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def file(version)
|
|
31
|
-
File.join(CACHE_DIR, "#{version}.sql.gz")
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
data/lib/prick/command.rb
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
require 'fcntl'
|
|
2
|
-
|
|
3
|
-
module Command
|
|
4
|
-
class Error < RuntimeError
|
|
5
|
-
attr_reader :status
|
|
6
|
-
attr_reader :stdout
|
|
7
|
-
attr_reader :stderr
|
|
8
|
-
|
|
9
|
-
def initialize(message, status, stdout, stderr)
|
|
10
|
-
super(message)
|
|
11
|
-
@status = status
|
|
12
|
-
@stdout = stdout
|
|
13
|
-
@stderr = stderr
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Execute the shell command 'cmd' and return standard output as an array of
|
|
18
|
-
# strings. while stderr is passed through unless stderr: is false. If stderr:
|
|
19
|
-
# is true, it returns a tuple of [stdout, stderr] instead. The shell command
|
|
20
|
-
# is executed with the `errexit` and `pipefail` bash options
|
|
21
|
-
#
|
|
22
|
-
# The :stdin option is a line or an array of lines that'll be fed to the
|
|
23
|
-
# standard input of the command. Default is nil
|
|
24
|
-
#
|
|
25
|
-
# It raises a Command::Error exception if the command fails unless :fail is
|
|
26
|
-
# true. The exit status of the last command is stored in ::status
|
|
27
|
-
#
|
|
28
|
-
def command(cmd, stdin: nil, stderr: false, fail: true)
|
|
29
|
-
cmd = "set -o errexit\nset -o pipefail\n#{cmd}"
|
|
30
|
-
|
|
31
|
-
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
|
32
|
-
pr = IO::pipe
|
|
33
|
-
pe = IO::pipe
|
|
34
|
-
|
|
35
|
-
STDOUT.flush
|
|
36
|
-
|
|
37
|
-
pid = fork {
|
|
38
|
-
pw[1].close
|
|
39
|
-
pr[0].close
|
|
40
|
-
pe[0].close
|
|
41
|
-
|
|
42
|
-
STDIN.reopen(pw[0])
|
|
43
|
-
pw[0].close
|
|
44
|
-
|
|
45
|
-
STDOUT.reopen(pr[1])
|
|
46
|
-
pr[1].close
|
|
47
|
-
|
|
48
|
-
STDERR.reopen(pe[1])
|
|
49
|
-
pe[1].close
|
|
50
|
-
|
|
51
|
-
exec(cmd)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pw[0].close
|
|
55
|
-
pr[1].close
|
|
56
|
-
pe[1].close
|
|
57
|
-
|
|
58
|
-
if stdin
|
|
59
|
-
pw[1].puts(stdin)
|
|
60
|
-
pw[1].flush
|
|
61
|
-
pw[1].close
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
@status = Process.waitpid2(pid)[1].exitstatus
|
|
65
|
-
|
|
66
|
-
out = pr[0].readlines.collect { |line| line.chop }
|
|
67
|
-
err = pe[0].readlines.collect { |line| line.chop }.grep_v(/^NOTICE:/)
|
|
68
|
-
|
|
69
|
-
pw[1].close if !stdin
|
|
70
|
-
pr[0].close
|
|
71
|
-
pe[0].close
|
|
72
|
-
|
|
73
|
-
result =
|
|
74
|
-
case stderr
|
|
75
|
-
when true; [out, err]
|
|
76
|
-
when false; out
|
|
77
|
-
when NilClass; $stderr.puts err
|
|
78
|
-
else
|
|
79
|
-
raise Internal, "Unexpected value for :stderr - #{stderr.inspect}"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if @status == 0 || fail == false
|
|
83
|
-
result
|
|
84
|
-
elsif fail
|
|
85
|
-
raise Command::Error.new("\n" + cmd + "\n" + (out + err).join("\n") + "\n", status, out, err)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Exit status of the last command
|
|
90
|
-
def status() @status end
|
|
91
|
-
|
|
92
|
-
# Like command but returns true if the command exited with the expected status
|
|
93
|
-
def command?(cmd, expect: 0)
|
|
94
|
-
command(cmd, fail: false)
|
|
95
|
-
@status == expect
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
module_function :command
|
|
99
|
-
module_function :status
|
|
100
|
-
module_function :command?
|
|
101
|
-
end
|
|
102
|
-
|
data/lib/prick/database.rb
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module Prick
|
|
3
|
-
class Database
|
|
4
|
-
attr_reader :name
|
|
5
|
-
attr_reader :user
|
|
6
|
-
|
|
7
|
-
def initialize(name, user)
|
|
8
|
-
name != "" or raise "Illegal database name"
|
|
9
|
-
@name = name
|
|
10
|
-
@user = user
|
|
11
|
-
@version = nil
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def version(cache: true)
|
|
15
|
-
if cache && @version
|
|
16
|
-
@version
|
|
17
|
-
else
|
|
18
|
-
@version = begin
|
|
19
|
-
version =
|
|
20
|
-
if exist? && Rdbms.exist_table?(name, "prick", "versions")
|
|
21
|
-
Rdbms.select(name, "select version from prick.versions")&.first&.first
|
|
22
|
-
else
|
|
23
|
-
nil
|
|
24
|
-
end
|
|
25
|
-
version && Version.new(version)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def version=(version)
|
|
31
|
-
@version = version && Version.new(version)
|
|
32
|
-
Rdbms.exec_sql(name, %(
|
|
33
|
-
update prick.versions
|
|
34
|
-
set major = '#{@version.major}',
|
|
35
|
-
minor = '#{@version.minor}',
|
|
36
|
-
patch = '#{@version.patch}',
|
|
37
|
-
pre = '#{@version.pre}',
|
|
38
|
-
version = '#{@version.to_s}'
|
|
39
|
-
)) if @version
|
|
40
|
-
@version
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def exist?() Rdbms.exist_database?(name) end
|
|
44
|
-
def create() Rdbms.create_database(name, owner: user) end
|
|
45
|
-
def recreate() drop if exist?; create; @version = nil end
|
|
46
|
-
def drop() Rdbms.drop_database(name, fail: false); @version = nil end
|
|
47
|
-
|
|
48
|
-
def loaded?() !version.nil? end
|
|
49
|
-
|
|
50
|
-
def load(file)
|
|
51
|
-
!loaded? or raise Internal, "Database #{name} is already loaded"
|
|
52
|
-
Rdbms.load(name, file, user: user)
|
|
53
|
-
version # Provoke load of version
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def save(file)
|
|
57
|
-
loaded? or raise Internal, "Database #{name} is not loaded"
|
|
58
|
-
Rdbms.save(name, file)
|
|
59
|
-
@version = nil
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Hollow-out a database without dropping it. This is useful compared to a
|
|
63
|
-
# simple drop database since it wont block on other sessions wont block
|
|
64
|
-
def clean()
|
|
65
|
-
if exist?
|
|
66
|
-
schemas = Rdbms.select_values(
|
|
67
|
-
"postgres",
|
|
68
|
-
"select nspname from pg_namespace where nspowner != 10")
|
|
69
|
-
for schema in schemas
|
|
70
|
-
Rdbms.exec_sql(name, "drop schema \"#{schema}\" cascade")
|
|
71
|
-
end
|
|
72
|
-
Rdbms.exec_sql(name, "drop schema if exists public cascade")
|
|
73
|
-
Rdbms.exec_sql(name, "create schema public authorization postgres")
|
|
74
|
-
Rdbms.exec_sql(name, "grant usage, create on schema public to public")
|
|
75
|
-
else
|
|
76
|
-
create
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def to_s() name end
|
|
81
|
-
end
|
|
82
|
-
end
|