prick 0.29.2 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/TODO +2 -0
  3. data/exe/prick +294 -519
  4. data/idea.txt +38 -0
  5. data/lib/prick/builder/batch.rb +1 -4
  6. data/lib/prick/builder/builder.rb +15 -9
  7. data/lib/prick/builder/node.rb +3 -2
  8. data/lib/prick/builder/node_pool.rb +3 -1
  9. data/lib/prick/builder/parser.rb +2 -1
  10. data/lib/prick/constants.rb +12 -12
  11. data/lib/prick/diff.rb +2 -2
  12. data/lib/prick/environment.rb +30 -12
  13. data/lib/prick/local/command.rb +8 -2
  14. data/lib/prick/local/fmt.rb +56 -0
  15. data/lib/prick/prick_version.rb +1 -1
  16. data/lib/prick/share/init/{.gitignore → dot.gitignore} +1 -1
  17. data/lib/prick/share/init/prick.environment.yml +16 -0
  18. data/lib/prick/share/init/prick.yml +3 -3
  19. data/lib/prick/share/init/schema/prick/build.yml +11 -2
  20. data/lib/prick/share/init/schema/prick/tables.sql +30 -9
  21. data/lib/prick/share/init/schema/prick/views.sql +6 -0
  22. data/lib/prick/state.rb +304 -95
  23. data/lib/prick/subcommand/prick-build.rb +26 -20
  24. data/lib/prick/subcommand/prick-clean.rb +5 -5
  25. data/lib/prick/subcommand/prick-create.rb +42 -7
  26. data/lib/prick/subcommand/prick-drop.rb +41 -14
  27. data/lib/prick/subcommand/prick-fox.rb +1 -1
  28. data/lib/prick/subcommand/prick-init.rb +25 -18
  29. data/lib/prick/subcommand/prick-list.rb +99 -0
  30. data/lib/prick/subcommand/prick-make.rb +8 -8
  31. data/lib/prick/subcommand/prick-migrate.rb +8 -7
  32. data/lib/prick/subcommand/prick-release.rb +4 -2
  33. data/lib/prick/subcommand/prick-set.rb +52 -0
  34. data/lib/prick/subcommand/prick-setup.rb +3 -13
  35. data/lib/prick/subcommand/prick-teardown.rb +13 -9
  36. data/lib/prick/subcommand/subcommand.rb +12 -0
  37. data/lib/prick/version.rb +1 -1
  38. data/lib/prick.rb +54 -15
  39. metadata +14 -9
  40. data/lib/prick/share/init/schema/prick/data.sql +0 -6
  41. 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
- # State file
7
- def file() PRICK_PROJECT_PATH end
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
- # Schema data file
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
- # Version in prick.yml. Can be nil
41
+ # Project version in prick.yml. Can be nil FIXME Can it?
19
42
  attr_accessor :version
20
43
 
21
- # Version in schema/prick/data.sql. Retrieved dynamically. TODO
22
- attr_reader :schema_version
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
- # Name of database
48
+ # Database name. nil if state file is absent
31
49
  attr_accessor :database
32
50
 
33
- # Name of database user
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
- # Run-time environment name
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
- # Prick project dir. This is not a constant because exe/prick can change
45
- # directory (FIXME it can?)
46
- def prick_dir
47
- @prick_dir ||= Dir.getwd
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
- # Prick executable search_path
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
- def bash_environment(scope = :global)
58
- hash = {
59
- "DATABASE" => Prick.state.database, # FIXME: Yt
60
- "USERNAME" => Prick.state.username, # FIXME: Yt
61
- "ENVIRONMENT" => Prick.state.environment.to_s, # FIXME: Yt
62
- "PRICK_NAME" => Prick.state.name,
63
- "PRICK_TITLE" => Prick.state.title,
64
- "PRICK_VERSION" => Prick.state.version,
65
- "PRICK_DATABASE" => Prick.state.database,
66
- "PRICK_USERNAME" => Prick.state.username,
67
- "PRICK_ENVIRONMENT" => Prick.state.environment.to_s,
68
- }
69
- case scope
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
- }).merge(Environment[Prick.state.environment].bash_env)
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
- def bash_source
91
- bash_environment(:local).map { |var,val| "export #{var}=\"#{Array(val).join(' ')}\"\n" }.join
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
- def self.load(project_file, context_file)
95
- state = State.new
96
- state.send(:parse_project_file, project_file || PRICK_PROJECT_PATH)
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
- def save
102
- raise NotImplementedError
103
- h = {
104
- "name" => name,
105
- "title" => title,
106
- "version" => version.to_s,
107
- "prick" => prick_version.to_s
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
- h = {
112
- "environment" => environment.to_s,
113
- "database" => database,
114
- "username" => username
115
- }
116
- File.write(PRICK_CONTEXT_PATH, h.to_yaml)
117
-
118
- if version
119
- File.write(SCHEMA_VERSION_PATH,
120
- <<~EOS
121
- --
122
- -- This file is auto-generated by prick(1). Please don't touch
123
- --
124
- delete from prick.versions;
125
- insert into prick.versions (fork, major, minor, patch, pre, feature, version)
126
- values (null, #{version.major}, #{version.minor}, #{version.patch}, null, null, '#{version}');
127
- EOS
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, :schema_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
- def load_yaml(file, expected_keys, optional_keys)
287
+ # YAML helper methods
288
+
289
+ def read_yaml(file)
147
290
  begin
148
- hash = YAML.load(File.read file)
291
+ YAML.load(File.read file)
149
292
  rescue Errno::ENOENT
150
- raise Prick::Error, "Can't read #{file}"
293
+ Prick.error "Can't read #{file}"
151
294
  end
152
- for key in expected_keys
153
- !hash[key].to_s.empty? or raise Prick::Error, "Can't find '#{key}' in #{file}"
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 - expected_keys - optional_keys).first) and
156
- Prick::Error "Illegal key '#{unknown}' in #{file}"
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 parse_context_file(context_file)
161
- hash = load_yaml(context_file, %w(environment database username), [])
162
- @database = hash["database"]
163
- @username = hash["username"]
164
- @environment = hash["environment"]
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 parse_project_file(prick_file)
168
- hash = load_yaml(prick_file, %w(name title), %w(version prick environments))
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
- Environment.load_environments(hash["environments"] || {})
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, timer: nil, dump: nil)
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
- time "Load build object" do
20
- if super_conn.rdbms.exist? database
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 = PgConn.new(database, username)
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 but re-creates the public schema
33
- super_conn.rdbms.empty!(database, public: false)
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 schemas to refresh. This includes all schemas in the
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
- # Eliminate keep schemas from build pool
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 { |schema| conn.schema.drop(schema, cascade: true) }
57
+ refresh_schemas.each { |s| conn.schema.drop(s, cascade: true) }
53
58
  end
54
59
  end
55
60
 
56
- # Delete schemas after target scheme if present
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
- raise Prick::Error, "Illegal dump type: #{dump.inspect}"
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 empty database. This has the same as effect as
7
- # teardown+setup but without recreating the user and the database that
8
- # annoyingly terminates all user sessions
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
- PgConn.new("postgres") { |conn|
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 raise Prick::Error, "Can't migrate from release 0.0.0"
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 raise Prick::Fail, "Migration #{from_version} exists"
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 raise Prick::Fail, "Can't migrate to same release"
25
- from_version < to_version or raise Prick::Fail, "Can't migrate backwards (why not?)"
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 raise Prick::Fail, "No changes"
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