prick 0.29.2 → 0.30.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/TODO +2 -0
- data/exe/prick +290 -518
- data/lib/prick/builder/batch.rb +1 -4
- data/lib/prick/builder/builder.rb +15 -9
- data/lib/prick/builder/node.rb +3 -2
- data/lib/prick/builder/node_pool.rb +3 -1
- data/lib/prick/builder/parser.rb +2 -1
- data/lib/prick/constants.rb +12 -12
- data/lib/prick/diff.rb +2 -2
- data/lib/prick/environment.rb +23 -10
- data/lib/prick/local/command.rb +8 -2
- data/lib/prick/local/fmt.rb +56 -0
- data/lib/prick/prick_version.rb +1 -1
- data/lib/prick/share/init/{.gitignore → dot.gitignore} +1 -1
- data/lib/prick/share/init/prick.environment.yml +16 -0
- data/lib/prick/share/init/prick.yml +3 -3
- data/lib/prick/share/init/schema/prick/build.yml +11 -2
- data/lib/prick/share/init/schema/prick/tables.sql +30 -9
- data/lib/prick/share/init/schema/prick/views.sql +6 -0
- data/lib/prick/state.rb +297 -95
- data/lib/prick/subcommand/prick-build.rb +26 -20
- data/lib/prick/subcommand/prick-clean.rb +5 -5
- data/lib/prick/subcommand/prick-create.rb +41 -6
- data/lib/prick/subcommand/prick-drop.rb +57 -14
- data/lib/prick/subcommand/prick-fox.rb +1 -1
- data/lib/prick/subcommand/prick-init.rb +25 -18
- data/lib/prick/subcommand/prick-list.rb +99 -0
- data/lib/prick/subcommand/prick-make.rb +8 -8
- data/lib/prick/subcommand/prick-migrate.rb +8 -7
- data/lib/prick/subcommand/prick-release.rb +4 -2
- data/lib/prick/subcommand/prick-set.rb +52 -0
- data/lib/prick/subcommand/prick-setup.rb +3 -13
- data/lib/prick/subcommand/prick-teardown.rb +13 -9
- data/lib/prick/subcommand/subcommand.rb +12 -0
- data/lib/prick/version.rb +1 -1
- data/lib/prick.rb +54 -15
- metadata +9 -5
- data/lib/prick/share/init/schema/prick/data.sql +0 -6
- data/prick.environments.yml +0 -14
data/lib/prick/state.rb
CHANGED
@@ -2,76 +2,172 @@
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
module Prick
|
5
|
+
# There is only one State object: Prick.state
|
6
|
+
#
|
7
|
+
# FIXME Not how it is done
|
8
|
+
# The prick.state file contains the current database, username, and
|
9
|
+
# environment. It is controlled by prick(1) but you can set its values by
|
10
|
+
# using 'prick database=asdf
|
11
|
+
#
|
5
12
|
class State
|
6
|
-
#
|
7
|
-
|
13
|
+
# Prick project dir. This is not a constant because prick can change
|
14
|
+
# directory through the -C option or the 'init' command
|
15
|
+
def prick_dir() @prick_dir ||= Dir.getwd end
|
8
16
|
|
9
|
-
#
|
17
|
+
# Project file. Default 'prick.yml'
|
18
|
+
attr_reader :project_file
|
19
|
+
|
20
|
+
# Environment file. Default 'prick.environment'. Note that the file can be
|
21
|
+
# absent if the project doesn't use environments
|
22
|
+
attr_reader :environment_file
|
23
|
+
|
24
|
+
# State file. Default '.prick-state.yml'
|
25
|
+
attr_reader :state_file
|
26
|
+
|
27
|
+
# Schema data file. FIXME What is this?
|
10
28
|
def schema_file() SCHEMA_VERSION_PATH end
|
11
29
|
|
30
|
+
# True if the configuration files has been loaded
|
31
|
+
def project_loaded? = @project_loaded
|
32
|
+
def state_loaded? = @state_loaded
|
33
|
+
def environment_loaded? = @environment_loaded
|
34
|
+
|
12
35
|
# Used as an identifier and the default database and username
|
13
36
|
attr_accessor :name
|
14
37
|
|
15
38
|
# Capitalized name of project
|
16
39
|
attr_accessor :title
|
17
40
|
|
18
|
-
#
|
41
|
+
# Project version in prick.yml. Can be nil FIXME Can it?
|
19
42
|
attr_accessor :version
|
20
43
|
|
21
|
-
# Version in
|
22
|
-
|
23
|
-
|
24
|
-
# Version from prick.versions.version. Retrieved dynamically. TODO FIXME: Where is conn?
|
25
|
-
attr_reader :database_version
|
26
|
-
|
27
|
-
# Version of prick(1)
|
44
|
+
# Version of prick in prick.yml. Note that this can be different than the current
|
45
|
+
# version of prick
|
28
46
|
attr_accessor :prick_version
|
29
47
|
|
30
|
-
#
|
48
|
+
# Database name. nil if state file is absent
|
31
49
|
attr_accessor :database
|
32
50
|
|
33
|
-
#
|
51
|
+
# Database owner name. Typically the same as the database name. nil if
|
52
|
+
# database is absent
|
34
53
|
attr_accessor :username
|
35
54
|
|
36
|
-
#
|
55
|
+
# Environment name. If not set in the state file, the enviroment is read
|
56
|
+
# from the database when the first connection is established by the
|
57
|
+
# #connection method. Use 'Environment[environment]' to get the
|
58
|
+
# corresponding Environment object
|
37
59
|
def environment() @environment end
|
38
60
|
def environment=(env)
|
39
|
-
constrain env, String
|
40
|
-
Environment.environment?(env) or raise "Illegal environment: '#{env}'"
|
61
|
+
constrain env, String, nil
|
62
|
+
env.nil? || Environment.environment?(env) or raise "Illegal environment: '#{env}'"
|
41
63
|
@environment = env
|
42
64
|
end
|
43
65
|
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
|
66
|
+
# Project version from PRICK.VERSIONS. Initialized by #connection
|
67
|
+
attr_accessor :database_version
|
68
|
+
|
69
|
+
# Prick version from PRICK.VERSIONS. Initialized by #connection
|
70
|
+
attr_accessor :database_prick_version
|
71
|
+
|
72
|
+
# Environment from PRICK.VERSIONS. Initialized by #connection
|
73
|
+
attr_accessor :database_environment
|
74
|
+
|
75
|
+
# Git branch. Lazy-evaluated
|
76
|
+
def branch() @branch ||= Git.branch.current end
|
77
|
+
|
78
|
+
# Git revision (commit ID). Lazy-evaluated
|
79
|
+
def rev(kind: :long)
|
80
|
+
case kind
|
81
|
+
when :short; @rev_short ||= rev()[0...8]
|
82
|
+
when :long; @rev_long ||= Git.id
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# True if the git repository is clean (not modified). Lazy-evaluated
|
87
|
+
def clean?()
|
88
|
+
return @clean if defined?(@clean)
|
89
|
+
@clean = Git.clean?
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(project_file, environment_file, state_file)
|
93
|
+
@project_file, @environment_file, @state_file = project_file, environment_file, state_file
|
94
|
+
@project_loaded = @state_loaded = @environment_loaded = false
|
95
|
+
|
96
|
+
if @project_file && File.exist?(@project_file)
|
97
|
+
load_project_file
|
98
|
+
load_state_file if @state_file && File.exist?(@state_file)
|
99
|
+
end
|
100
|
+
|
101
|
+
# FIXME The environment file should be loaded on-demand but it is hard to
|
102
|
+
# do when the environments are accessed through a class-interface
|
103
|
+
load_environment_file if @environment_file && File.exist?(@environment_file)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Project user (owner) connection. Memoized to connect only once.
|
107
|
+
# TODO Rename. Also rename self.connection
|
108
|
+
def connection(database: nil, username: nil, environment: nil, &block)
|
109
|
+
database ||= self.database
|
110
|
+
username ||= self.username
|
111
|
+
environment ||= self.environment
|
112
|
+
!database.nil? or Prick.error "Can't connect to Postgres - no database specified"
|
113
|
+
# exist_database_environment? or Prick.error "Database '#{database}' is not initialized"
|
114
|
+
|
115
|
+
if @connection.nil?
|
116
|
+
@connection = PgConn.new(database, username)
|
117
|
+
|
118
|
+
# Set database_version/environment/prick members
|
119
|
+
load_database_environment
|
120
|
+
|
121
|
+
# Set environment if undefined and not overridden by :environment
|
122
|
+
self.environment =
|
123
|
+
environment ||
|
124
|
+
Prick.state.environment ||
|
125
|
+
Environment.environment?(database_environment) && database_environment ||
|
126
|
+
DEFAULT_ENVIRONMENT
|
127
|
+
end
|
128
|
+
if block_given?
|
129
|
+
yield @connection
|
130
|
+
else
|
131
|
+
@connection
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Superuser connection. This is a connection to Postgres using the current
|
136
|
+
# user's credentials. It is assumed that the current user has a postgres
|
137
|
+
# superuser account with the same name as the user's. Memoized to connect
|
138
|
+
# only once
|
139
|
+
def self.connection(&block)
|
140
|
+
@@connection ||= PgConn.new("postgres")
|
141
|
+
if block_given?
|
142
|
+
yield @@connection
|
143
|
+
else
|
144
|
+
@@connection
|
145
|
+
end
|
48
146
|
end
|
49
147
|
|
50
|
-
#
|
148
|
+
# Short-hand for #connection
|
149
|
+
alias_method :conn, :connection
|
150
|
+
|
151
|
+
# Prick executable search_path. This includes the bin and libexec directories
|
51
152
|
def executable_search_path
|
52
153
|
@executable_search_path ||= "#{ENV['PATH']}:#{prick_dir}/#{BIN_DIR}:#{prick_dir}/#{LIBEXEC_DIR}"
|
53
154
|
end
|
54
155
|
|
55
156
|
# Create a bash(1) environment (Hash). It is used for in-prick expansion of
|
56
|
-
# variables and also injected into the enviroment of subprocesses
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
when :local; # nop
|
71
|
-
hash.merge Environment[Prick.state.environment].bash_env
|
72
|
-
when :global;
|
73
|
-
ENV.to_h.merge(hash).merge({
|
74
|
-
"PATH" => Prick.state.executable_search_path,
|
157
|
+
# variables and is also injected into the enviroment of subprocesses
|
158
|
+
#
|
159
|
+
# FIXME: Problems with BUNDLE_* variables FIXME Still a problem?
|
160
|
+
#
|
161
|
+
# TODO: Explain handling of PRICK_<STANDARD-DIRECTORY>
|
162
|
+
def bash_environment(all: true)
|
163
|
+
@bash_environment ||= begin
|
164
|
+
hash = {
|
165
|
+
"PATH" => Prick.state.executable_search_path
|
166
|
+
}
|
167
|
+
|
168
|
+
if all
|
169
|
+
hash.merge!({
|
170
|
+
"PRICK_DIR" => Prick.state.prick_dir,
|
75
171
|
"PRICK_SCHEMADIR" => File.join(Prick.state.prick_dir, SCHEMA_DIR),
|
76
172
|
"PRICK_BINDIR" => File.join(Prick.state.prick_dir, BIN_DIR),
|
77
173
|
"PRICK_LIBEXECDIR" => File.join(Prick.state.prick_dir, LIBEXEC_DIR),
|
@@ -80,52 +176,90 @@ module Prick
|
|
80
176
|
"PRICK_SPOOLDIR" => File.join(Prick.state.prick_dir, SPOOL_DIR),
|
81
177
|
"PRICK_TMPDIR" => File.join(Prick.state.prick_dir, TMP_DIR),
|
82
178
|
"PRICK_CLONEDIR" => File.join(Prick.state.prick_dir, CLONE_DIR),
|
83
|
-
"PRICK_SPECDIR" => File.join(Prick.state.prick_dir, BIN_DIR)
|
84
|
-
})
|
179
|
+
"PRICK_SPECDIR" => File.join(Prick.state.prick_dir, BIN_DIR),
|
180
|
+
})
|
181
|
+
end
|
182
|
+
|
183
|
+
hash.merge!({
|
184
|
+
"DATABASE" => Prick.state.database, # FIXME: Yt
|
185
|
+
"USERNAME" => Prick.state.username, # FIXME: Yt
|
186
|
+
"ENVIRONMENT" => Prick.state.environment.to_s, # FIXME: Yt
|
187
|
+
"PRICK_NAME" => Prick.state.name,
|
188
|
+
"PRICK_TITLE" => Prick.state.title,
|
189
|
+
"PRICK_VERSION" => Prick.state.version,
|
190
|
+
"PRICK_DATABASE" => Prick.state.database,
|
191
|
+
"PRICK_USERNAME" => Prick.state.username,
|
192
|
+
"PRICK_ENVIRONMENT" => Prick.state.environment&.to_s, # may be the empty string
|
193
|
+
})
|
194
|
+
|
195
|
+
# PRICK_ENVIRONMENT_* variables
|
196
|
+
hash.merge! (Prick.state.environment.nil? ? {} : Environment[Prick.state.environment].bash_env)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# @scope can be :global (variables are exported), :local (variables are
|
201
|
+
# declared local), or nil (variables are global but not exported)
|
202
|
+
def bash_source(vars = nil, scope: nil)
|
203
|
+
case scope
|
204
|
+
when :global; prefix="export "
|
205
|
+
when :local; prefix="local "
|
206
|
+
when nil; prefix=""
|
85
207
|
else
|
86
|
-
raise ArgumentError
|
208
|
+
raise ArgumentError, "Illegal value for scope: #{scope.inspect}"
|
87
209
|
end
|
210
|
+
|
211
|
+
(vars || bash_environment.keys).map { |var|
|
212
|
+
val = bash_environment[var]
|
213
|
+
"#{prefix}#{var}=\"#{Array(val).join(' ')}\"\n"
|
214
|
+
}.join
|
88
215
|
end
|
89
216
|
|
90
|
-
|
91
|
-
|
217
|
+
# It is an error if the project file exists.
|
218
|
+
def save_project
|
219
|
+
!File.exists?(project_file) or Prick.error "Won't overwrite '#{project_file}'"
|
220
|
+
hash = { name: name, title: title, version: version.to_s, prick: prick_version.to_s }
|
221
|
+
save_yaml(project_file, hash)
|
92
222
|
end
|
93
223
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
state.send(:parse_context_file, context_file || PRICK_CONTEXT_PATH)
|
98
|
-
state
|
224
|
+
# FIXME: Ugly
|
225
|
+
def save_version
|
226
|
+
system("sed -i 's/^version:.*/version: #{version.to_s} #{project_file}/'")
|
99
227
|
end
|
100
228
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
}
|
109
|
-
File.write(PRICK_PROJECT_PATH, h.to_yaml)
|
229
|
+
# Used by 'prick setup'
|
230
|
+
def save_state(database = nil, username = nil, environment = nil)
|
231
|
+
database ||= self.database or raise ArgumentError
|
232
|
+
username ||= self.username or raise ArgumentError
|
233
|
+
environment ||= self.environment
|
234
|
+
save_yaml(state_file, database: database, username: username, environment: environment)
|
235
|
+
end
|
110
236
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
)
|
237
|
+
# Save build-start information to PRICK.BUILDS. It is a nop if PRICK.BUILDS
|
238
|
+
# doesn't exist, this happens on first build in a completely empty database
|
239
|
+
def save_build_begin
|
240
|
+
@build_id = nil # Used by save_build_end
|
241
|
+
if conn.schema.exist_table?("prick", "builds")
|
242
|
+
@build_id = insert_record(
|
243
|
+
"prick.builds",
|
244
|
+
name: name,
|
245
|
+
version: version.to_s, prick: prick_version,
|
246
|
+
branch: branch, rev: rev(:short), clean: clean?,
|
247
|
+
environment: environment)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def save_build_end(success, duration)
|
252
|
+
dt = Time.now - TIME
|
253
|
+
if @build_id
|
254
|
+
update_record("prick.builds", @build_id, success: success, duration: duration, prick_duration: dt)
|
255
|
+
else
|
256
|
+
insert_record(
|
257
|
+
"prick.builds",
|
258
|
+
name: name,
|
259
|
+
version: version.to_s, prick: prick_version,
|
260
|
+
branch: branch, rev: rev(:short), clean: clean?,
|
261
|
+
environment: environment,
|
262
|
+
success: success, duration: duration, prick_duration: dt)
|
129
263
|
end
|
130
264
|
end
|
131
265
|
|
@@ -133,8 +267,8 @@ module Prick
|
|
133
267
|
puts "State"
|
134
268
|
indent {
|
135
269
|
for method in [
|
136
|
-
:name, :title, :prick_version, :project_version,
|
137
|
-
:database_version, :database, :username]
|
270
|
+
:name, :title, :prick_version, :project_version,
|
271
|
+
:database_version, :database_environment, :database, :username]
|
138
272
|
puts "#{method}: #{self.send method}"
|
139
273
|
end
|
140
274
|
puts "environments:"
|
@@ -143,36 +277,104 @@ module Prick
|
|
143
277
|
end
|
144
278
|
|
145
279
|
private
|
146
|
-
|
280
|
+
# YAML helper methods
|
281
|
+
|
282
|
+
def read_yaml(file)
|
147
283
|
begin
|
148
|
-
|
284
|
+
YAML.load(File.read file)
|
149
285
|
rescue Errno::ENOENT
|
150
|
-
|
286
|
+
Prick.error "Can't read #{file}"
|
151
287
|
end
|
152
|
-
|
153
|
-
|
288
|
+
end
|
289
|
+
|
290
|
+
def load_yaml(file, mandatory_keys, optional_keys = [])
|
291
|
+
mandatory_keys = mandatory_keys.map(&:to_s)
|
292
|
+
optional_keys = optional_keys.map(&:to_s)
|
293
|
+
hash = read_yaml(file)
|
294
|
+
for key in mandatory_keys
|
295
|
+
!hash[key].to_s.empty? or Prick.error "Can't find '#{key}' in #{file}"
|
154
296
|
end
|
155
|
-
(unknown = (hash.keys -
|
156
|
-
Prick
|
297
|
+
(unknown = (hash.keys - mandatory_keys - optional_keys).first) and
|
298
|
+
Prick.error "Illegal key '#{unknown}' in #{file}"
|
157
299
|
hash
|
158
300
|
end
|
159
301
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
302
|
+
def save_yaml(file, hash)
|
303
|
+
self.class.save_yaml(file, hash)
|
304
|
+
end
|
305
|
+
|
306
|
+
def self.save_yaml(file, hash)
|
307
|
+
IO.write(file, hash.map { |k,v| [k.to_s, v] }.to_h.to_yaml)
|
308
|
+
end
|
309
|
+
|
310
|
+
# Database helper methods. TODO: Make generally available
|
311
|
+
|
312
|
+
# FIXME FIXME FIXME Yt. Replace with PgConn methods
|
313
|
+
def select_record(table, id, columns = [])
|
314
|
+
sql_columns = columns.join(", ")
|
315
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
316
|
+
connection.tuple "select #{sql_columns} from #{table} where #{sql_id_cond}"
|
165
317
|
end
|
166
318
|
|
167
|
-
def
|
168
|
-
|
319
|
+
def select_record?(table, id, columns = [])
|
320
|
+
sql_columns = columns.join(", ")
|
321
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
322
|
+
connection.tuple? "select #{sql_columns} from #{table} where #{sql_id_cond}"
|
323
|
+
end
|
324
|
+
|
325
|
+
def insert_record(table, attrs = {})
|
326
|
+
sql_fields = attrs.keys.join(", ")
|
327
|
+
sql_values = attrs.values.map { conn.quote_literal(_1) }.join(", ")
|
328
|
+
conn.value "insert into #{table} (#{sql_fields}) values (#{sql_values}) returning id"
|
329
|
+
end
|
330
|
+
|
331
|
+
def update_record(table, id, attrs = {})
|
332
|
+
sql_assign = attrs.map { |k,v| "#{conn.quote_identifier(k)} = #{conn.quote_literal(v)}" }.join(", ")
|
333
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
334
|
+
conn.exec "update #{table} set #{sql_assign} where #{sql_id_cond}"
|
335
|
+
end
|
336
|
+
|
337
|
+
# State helper methods
|
338
|
+
|
339
|
+
def load_project_file
|
340
|
+
hash = load_yaml(project_file, %w(name title), %w(version prick))
|
169
341
|
@name = hash["name"]
|
170
342
|
@title = hash["title"]
|
171
343
|
@version = hash["version"] && PrickVersion.new(hash["version"])
|
172
344
|
@prick_version = hash["prick"] && PrickVersion.new(hash["prick"])
|
173
|
-
|
345
|
+
@project_loaded = true
|
346
|
+
end
|
347
|
+
|
348
|
+
def load_environment_file
|
349
|
+
hash = environment_file ? read_yaml(environment_file) : {}
|
350
|
+
Environment.load_environments(hash)
|
351
|
+
@environment_loaded = true
|
352
|
+
end
|
353
|
+
|
354
|
+
def load_state_file
|
355
|
+
hash = load_yaml(state_file, %w(database username), %w(environment))
|
356
|
+
@database = hash["database"]
|
357
|
+
@username = hash["username"]
|
358
|
+
@environment = hash["environment"]
|
359
|
+
@state_loaded = true
|
360
|
+
end
|
361
|
+
|
362
|
+
# Database helper methods
|
363
|
+
|
364
|
+
# Return true if the database environment exists
|
365
|
+
def exist_database_environment?
|
366
|
+
connection.schema.exist_relation?("prick", "versions")
|
367
|
+
end
|
368
|
+
|
369
|
+
# Loads database environment. It is an error if the prick schema is absent
|
370
|
+
def load_database_environment
|
371
|
+
version, environment, prick_version = connection.tuple %(
|
372
|
+
select version, environment, prick from prick.versions
|
373
|
+
)
|
374
|
+
@database_version = PrickVersion.new(version)
|
375
|
+
@database_environment = environment
|
376
|
+
@database_prick_version = PrickVersion.new(prick_version)
|
174
377
|
end
|
175
378
|
end
|
176
379
|
end
|
177
380
|
|
178
|
-
|
@@ -5,36 +5,40 @@ require_relative '../builder/builder.rb'
|
|
5
5
|
module Prick::SubCommand
|
6
6
|
def self.build(
|
7
7
|
database, username, schema,
|
8
|
-
builddir: "schema", force: false,
|
8
|
+
builddir: "schema", force: false,
|
9
|
+
timer: nil, dump: nil)
|
9
10
|
|
10
11
|
Timer.on! if timer
|
11
|
-
time "Prick::Command#build" do
|
12
|
-
begin
|
13
|
-
super_conn = PgConn.new # Used to create new databases (doesn't make a
|
14
|
-
# difference right now as the mikras user is
|
15
|
-
# still a superuser)
|
16
12
|
|
13
|
+
Timer.time "Prick::Command#build" do
|
14
|
+
begin
|
15
|
+
super_conn = State.connection # Used to create new databases (doesn't make a
|
16
|
+
# difference right now as the database user is
|
17
|
+
# a superuser anyway
|
17
18
|
conn = nil
|
18
19
|
builder = nil
|
19
|
-
|
20
|
-
|
20
|
+
|
21
|
+
constrain super_conn.rdbms.exist?, true
|
22
|
+
|
23
|
+
Timer.time "Load build object" do
|
24
|
+
if super_conn.rdbms.exist? database # FIXME Why create database? Setup should have done this
|
21
25
|
exist = true
|
22
26
|
else
|
23
27
|
super_conn.rdbms.create database, owner: username
|
24
28
|
exist = false
|
25
29
|
end
|
26
|
-
conn =
|
30
|
+
conn = Prick.state.connection
|
27
31
|
|
28
32
|
builder = Prick::Build::Builder.new(conn, builddir)
|
29
33
|
|
30
|
-
if exist
|
34
|
+
if exist # Empty (part of) the database
|
31
35
|
if force
|
32
|
-
# Drop all schemas
|
33
|
-
|
34
|
-
|
36
|
+
# Drop all schemas except the prick schema and re-creates the
|
37
|
+
# public schema
|
38
|
+
build.pool.delete_schema("prick") if super_conn.schema.exist?("prick")
|
39
|
+
super_conn.rdbms.empty!(database, public: false, exclude: ["prick"])
|
35
40
|
else
|
36
|
-
# Find
|
37
|
-
# database
|
41
|
+
# Find all schemas
|
38
42
|
refresh_schemas = conn.schema.list
|
39
43
|
|
40
44
|
# Find existing keep-schemas (but exclude target schema)
|
@@ -45,15 +49,17 @@ module Prick::SubCommand
|
|
45
49
|
# Remove keep-schemas from list of schemas
|
46
50
|
refresh_schemas -= keep_schemas
|
47
51
|
|
48
|
-
#
|
52
|
+
# Also remove keep-schemas from the build pool. Why don't we use
|
53
|
+
# the pool tracker's list of keep schemas?
|
49
54
|
builder.pool.delete_schema(keep_schemas)
|
50
55
|
|
51
56
|
# Drop refresh schemes
|
52
|
-
refresh_schemas.each { |
|
57
|
+
refresh_schemas.each { |s| conn.schema.drop(s, cascade: true) }
|
53
58
|
end
|
54
59
|
end
|
55
60
|
|
56
|
-
# Delete schemas after target
|
61
|
+
# Delete schemas that comes after the target in the build sequence if
|
62
|
+
# 'prick build' was given a schema
|
57
63
|
builder.pool.delete_schema(builder.pool.after_schema(schema)) if schema
|
58
64
|
end
|
59
65
|
|
@@ -63,10 +69,10 @@ module Prick::SubCommand
|
|
63
69
|
when :batches; builder.dump
|
64
70
|
when nil;
|
65
71
|
else
|
66
|
-
|
72
|
+
Prick.error "Illegal dump type: #{dump.inspect}"
|
67
73
|
end && exit
|
68
74
|
|
69
|
-
time "Execute build object" do
|
75
|
+
Timer.time "Execute build object" do
|
70
76
|
builder.execute conn
|
71
77
|
end
|
72
78
|
|
@@ -3,13 +3,13 @@
|
|
3
3
|
require_relative '../builder/builder.rb'
|
4
4
|
|
5
5
|
module Prick::SubCommand
|
6
|
-
# Drop users and
|
7
|
-
# teardown+setup but without recreating the
|
8
|
-
#
|
6
|
+
# Drop users and hollow-out database. This has the same as effect as
|
7
|
+
# teardown+setup but without recreating the database owner and the database
|
8
|
+
# which would otherwise terminate all user sessions
|
9
9
|
def self.clean(database)
|
10
10
|
drop_users(database)
|
11
|
-
|
12
|
-
conn.rdbms.empty!(database) if conn.rdbms.exist?(database)
|
11
|
+
State.connection { |conn|
|
12
|
+
conn.rdbms.empty!(database, exclude: ["prick"]) if conn.rdbms.exist?(database)
|
13
13
|
}
|
14
14
|
end
|
15
15
|
end
|
@@ -1,16 +1,51 @@
|
|
1
|
-
|
2
1
|
module Prick
|
3
2
|
module SubCommand
|
3
|
+
PRICK_BUILD_FILES = %w(tables.sql views.sql) # HARDCODED
|
4
|
+
|
5
|
+
def self.create_database(database, username, environment)
|
6
|
+
super_conn = State.connection
|
7
|
+
|
8
|
+
# Create user if absent
|
9
|
+
if !super_conn.role.exist? username
|
10
|
+
# FIXME Should not be created as superuser but we can't do that before we
|
11
|
+
# have a super: option in build so that superuser-requiring function
|
12
|
+
# definitions can be built
|
13
|
+
super_conn.role.create username, superuser: true, can_login: true, create_role: true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create database
|
17
|
+
super_conn.rdbms.create database, owner: username
|
18
|
+
|
19
|
+
# Setup Prick schema. Note that this is hardcoded, the prick build file
|
20
|
+
# is ignored
|
21
|
+
conn = PgConn.new(database, username) # Can't use Prick.state.connection on a new database
|
22
|
+
conn.schema.create("prick")
|
23
|
+
PRICK_BUILD_FILES.each { |file| conn.exec(IO.read("#{SCHEMA_PRICK_DIR}/#{file}")) }
|
24
|
+
|
25
|
+
# Add initial build record
|
26
|
+
state = Prick.state # shorthand
|
27
|
+
conn.insert "prick.builds",
|
28
|
+
name: state.title,
|
29
|
+
version: state.version,
|
30
|
+
prick: state.prick_version,
|
31
|
+
branch: state.branch,
|
32
|
+
rev: state.rev(kind: :short),
|
33
|
+
clean: state.clean?,
|
34
|
+
environment: environment || Prick::DEFAULT_ENVIRONMENT,
|
35
|
+
built_at: nil,
|
36
|
+
success: nil
|
37
|
+
end
|
38
|
+
|
4
39
|
def self.create_migration(username, from_version, force: false, file: nil)
|
5
40
|
constrain from_version, PrickVersion
|
6
41
|
Git.clean? or raise "Won't migrate: Repository is dirty"
|
7
42
|
Git.synchronized? or raise "Won't migrate: Repository is not synchronized"
|
8
43
|
|
9
|
-
from_version != PrickVersion.zero or
|
44
|
+
from_version != PrickVersion.zero or Prick.error "Can't migrate from release 0.0.0"
|
10
45
|
to_version = Prick.state.version
|
11
46
|
migration_dir = "#{MIGRATION_DIR}/#{from_version}"
|
12
47
|
migration_exist = File.directory? migration_dir
|
13
|
-
force || file || !migration_exist or
|
48
|
+
force || file || !migration_exist or Prick.failure "Migration #{from_version} exists"
|
14
49
|
|
15
50
|
$quiet = true if file
|
16
51
|
|
@@ -21,8 +56,8 @@ module Prick
|
|
21
56
|
end
|
22
57
|
}
|
23
58
|
else
|
24
|
-
from_version != to_version or
|
25
|
-
from_version < to_version or
|
59
|
+
from_version != to_version or Prick.failure "Can't migrate to same release"
|
60
|
+
from_version < to_version or Prick.failure "Can't migrate backwards (why not?)"
|
26
61
|
|
27
62
|
diff = nil
|
28
63
|
if force || !migration_exist
|
@@ -51,7 +86,7 @@ module Prick
|
|
51
86
|
mesg " Creating diff"
|
52
87
|
Diff.new(from_db, to_db)
|
53
88
|
}
|
54
|
-
!diff.same? or
|
89
|
+
!diff.same? or Prick.failure "No changes"
|
55
90
|
ensure
|
56
91
|
drop_all(from_db)
|
57
92
|
drop_all(to_db)
|