prick 0.29.2 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|