prick 0.29.2 → 0.31.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 +294 -519
- data/idea.txt +38 -0
- 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 +30 -12
- 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 +304 -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 +42 -7
- data/lib/prick/subcommand/prick-drop.rb +41 -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 +14 -9
- 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
|
48
84
|
end
|
49
85
|
|
50
|
-
#
|
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
|
+
if @connection.nil?
|
110
|
+
database ||= self.database
|
111
|
+
username ||= self.username
|
112
|
+
environment ||= self.environment
|
113
|
+
!database.nil? or Prick.error "Can't connect to Postgres - no database specified"
|
114
|
+
# exist_database_environment? or Prick.error "Database '#{database}' is not initialized"
|
115
|
+
|
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
|
146
|
+
end
|
147
|
+
|
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,97 @@ 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
|
+
if val.is_a?(Array)
|
214
|
+
if val.first.is_a?(Array)
|
215
|
+
val = val.map { |v| v.join("\n") }.join("\n")
|
216
|
+
else
|
217
|
+
val = val.join(" ")
|
218
|
+
end
|
219
|
+
end
|
220
|
+
"#{prefix}#{var}=\"#{val}\"\n"
|
221
|
+
}.join
|
88
222
|
end
|
89
223
|
|
90
|
-
|
91
|
-
|
224
|
+
# It is an error if the project file exists.
|
225
|
+
def save_project
|
226
|
+
!File.exists?(project_file) or Prick.error "Won't overwrite '#{project_file}'"
|
227
|
+
hash = { name: name, title: title, version: version.to_s, prick: prick_version.to_s }
|
228
|
+
save_yaml(project_file, hash)
|
92
229
|
end
|
93
230
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
state.send(:parse_context_file, context_file || PRICK_CONTEXT_PATH)
|
98
|
-
state
|
231
|
+
# FIXME: Ugly
|
232
|
+
def save_version
|
233
|
+
system("sed -i 's/^version:.*/version: #{version.to_s} #{project_file}/'")
|
99
234
|
end
|
100
235
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
}
|
109
|
-
File.write(PRICK_PROJECT_PATH, h.to_yaml)
|
236
|
+
# Used by 'prick setup'
|
237
|
+
def save_state(database = nil, username = nil, environment = nil)
|
238
|
+
database ||= self.database or raise ArgumentError
|
239
|
+
username ||= self.username or raise ArgumentError
|
240
|
+
environment ||= self.environment
|
241
|
+
save_yaml(state_file, database: database, username: username, environment: environment)
|
242
|
+
end
|
110
243
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
)
|
244
|
+
# Save build-start information to PRICK.BUILDS. It is a nop if PRICK.BUILDS
|
245
|
+
# doesn't exist, this happens on first build in a completely empty database
|
246
|
+
def save_build_begin
|
247
|
+
@build_id = nil # Used by save_build_end
|
248
|
+
if conn.schema.exist_table?("prick", "builds")
|
249
|
+
@build_id = insert_record(
|
250
|
+
"prick.builds",
|
251
|
+
name: name,
|
252
|
+
version: version.to_s, prick: prick_version,
|
253
|
+
branch: branch, rev: rev(kind: :short), clean: clean?,
|
254
|
+
environment: environment)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def save_build_end(success, duration)
|
259
|
+
dt = Time.now - TIME
|
260
|
+
if @build_id
|
261
|
+
update_record("prick.builds", @build_id, success: success, duration: duration, prick_duration: dt)
|
262
|
+
else
|
263
|
+
insert_record(
|
264
|
+
"prick.builds",
|
265
|
+
name: name,
|
266
|
+
version: version.to_s, prick: prick_version,
|
267
|
+
branch: branch, rev: rev(kind: :short), clean: clean?,
|
268
|
+
environment: environment,
|
269
|
+
success: success, duration: duration, prick_duration: dt)
|
129
270
|
end
|
130
271
|
end
|
131
272
|
|
@@ -133,8 +274,8 @@ module Prick
|
|
133
274
|
puts "State"
|
134
275
|
indent {
|
135
276
|
for method in [
|
136
|
-
:name, :title, :prick_version, :project_version,
|
137
|
-
:database_version, :database, :username]
|
277
|
+
:name, :title, :prick_version, :project_version,
|
278
|
+
:database_version, :database_environment, :database, :username]
|
138
279
|
puts "#{method}: #{self.send method}"
|
139
280
|
end
|
140
281
|
puts "environments:"
|
@@ -143,36 +284,104 @@ module Prick
|
|
143
284
|
end
|
144
285
|
|
145
286
|
private
|
146
|
-
|
287
|
+
# YAML helper methods
|
288
|
+
|
289
|
+
def read_yaml(file)
|
147
290
|
begin
|
148
|
-
|
291
|
+
YAML.load(File.read file)
|
149
292
|
rescue Errno::ENOENT
|
150
|
-
|
293
|
+
Prick.error "Can't read #{file}"
|
151
294
|
end
|
152
|
-
|
153
|
-
|
295
|
+
end
|
296
|
+
|
297
|
+
def load_yaml(file, mandatory_keys, optional_keys = [])
|
298
|
+
mandatory_keys = mandatory_keys.map(&:to_s)
|
299
|
+
optional_keys = optional_keys.map(&:to_s)
|
300
|
+
hash = read_yaml(file) or Prick.error "Not a valid YAML file - #{file}"
|
301
|
+
for key in mandatory_keys
|
302
|
+
!hash[key].to_s.empty? or Prick.error "Can't find '#{key}' in #{file}"
|
154
303
|
end
|
155
|
-
(unknown = (hash.keys -
|
156
|
-
Prick
|
304
|
+
(unknown = (hash.keys - mandatory_keys - optional_keys).first) and
|
305
|
+
Prick.error "Illegal key '#{unknown}' in #{file}"
|
157
306
|
hash
|
158
307
|
end
|
159
308
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
309
|
+
def save_yaml(file, hash)
|
310
|
+
self.class.save_yaml(file, hash)
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.save_yaml(file, hash)
|
314
|
+
IO.write(file, hash.map { |k,v| [k.to_s, v] }.to_h.to_yaml)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Database helper methods. TODO: Make generally available
|
318
|
+
|
319
|
+
# FIXME FIXME FIXME Yt. Replace with PgConn methods
|
320
|
+
def select_record(table, id, columns = [])
|
321
|
+
sql_columns = columns.join(", ")
|
322
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
323
|
+
connection.tuple "select #{sql_columns} from #{table} where #{sql_id_cond}"
|
165
324
|
end
|
166
325
|
|
167
|
-
def
|
168
|
-
|
326
|
+
def select_record?(table, id, columns = [])
|
327
|
+
sql_columns = columns.join(", ")
|
328
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
329
|
+
connection.tuple? "select #{sql_columns} from #{table} where #{sql_id_cond}"
|
330
|
+
end
|
331
|
+
|
332
|
+
def insert_record(table, attrs = {})
|
333
|
+
sql_fields = attrs.keys.join(", ")
|
334
|
+
sql_values = attrs.values.map { conn.quote_literal(_1) }.join(", ")
|
335
|
+
conn.value "insert into #{table} (#{sql_fields}) values (#{sql_values}) returning id"
|
336
|
+
end
|
337
|
+
|
338
|
+
def update_record(table, id, attrs = {})
|
339
|
+
sql_assign = attrs.map { |k,v| "#{conn.quote_identifier(k)} = #{conn.quote_literal(v)}" }.join(", ")
|
340
|
+
sql_id_cond = (id.nil? ? "true" : "id = #{id}")
|
341
|
+
conn.exec "update #{table} set #{sql_assign} where #{sql_id_cond}"
|
342
|
+
end
|
343
|
+
|
344
|
+
# State helper methods
|
345
|
+
|
346
|
+
def load_project_file
|
347
|
+
hash = load_yaml(project_file, %w(name title), %w(version prick))
|
169
348
|
@name = hash["name"]
|
170
349
|
@title = hash["title"]
|
171
350
|
@version = hash["version"] && PrickVersion.new(hash["version"])
|
172
351
|
@prick_version = hash["prick"] && PrickVersion.new(hash["prick"])
|
173
|
-
|
352
|
+
@project_loaded = true
|
353
|
+
end
|
354
|
+
|
355
|
+
def load_environment_file
|
356
|
+
hash = environment_file ? read_yaml(environment_file) : {}
|
357
|
+
Environment.load_environments(hash)
|
358
|
+
@environment_loaded = true
|
359
|
+
end
|
360
|
+
|
361
|
+
def load_state_file
|
362
|
+
hash = load_yaml(state_file, %w(database username), %w(environment))
|
363
|
+
@database = hash["database"]
|
364
|
+
@username = hash["username"]
|
365
|
+
@environment = hash["environment"]
|
366
|
+
@state_loaded = true
|
367
|
+
end
|
368
|
+
|
369
|
+
# Database helper methods
|
370
|
+
|
371
|
+
# Return true if the database environment exists
|
372
|
+
def exist_database_environment?
|
373
|
+
connection.schema.exist_relation?("prick", "versions")
|
374
|
+
end
|
375
|
+
|
376
|
+
# Loads database environment. It is an error if the prick schema is absent
|
377
|
+
def load_database_environment
|
378
|
+
version, environment, prick_version = connection.tuple %(
|
379
|
+
select version, environment, prick from prick.versions
|
380
|
+
)
|
381
|
+
@database_version = PrickVersion.new(version)
|
382
|
+
@database_environment = environment
|
383
|
+
@database_prick_version = PrickVersion.new(prick_version)
|
174
384
|
end
|
175
385
|
end
|
176
386
|
end
|
177
387
|
|
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?(database), true # FIXME Same problem as below
|
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,9 +86,9 @@ 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
|
-
drop_all(from_db)
|
91
|
+
drop_all(from_db) # FIXME This will fail, maybe use teardown instead?
|
57
92
|
drop_all(to_db)
|
58
93
|
end
|
59
94
|
end
|