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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/TODO +2 -0
  3. data/exe/prick +290 -518
  4. data/lib/prick/builder/batch.rb +1 -4
  5. data/lib/prick/builder/builder.rb +15 -9
  6. data/lib/prick/builder/node.rb +3 -2
  7. data/lib/prick/builder/node_pool.rb +3 -1
  8. data/lib/prick/builder/parser.rb +2 -1
  9. data/lib/prick/constants.rb +12 -12
  10. data/lib/prick/diff.rb +2 -2
  11. data/lib/prick/environment.rb +23 -10
  12. data/lib/prick/local/command.rb +8 -2
  13. data/lib/prick/local/fmt.rb +56 -0
  14. data/lib/prick/prick_version.rb +1 -1
  15. data/lib/prick/share/init/{.gitignore → dot.gitignore} +1 -1
  16. data/lib/prick/share/init/prick.environment.yml +16 -0
  17. data/lib/prick/share/init/prick.yml +3 -3
  18. data/lib/prick/share/init/schema/prick/build.yml +11 -2
  19. data/lib/prick/share/init/schema/prick/tables.sql +30 -9
  20. data/lib/prick/share/init/schema/prick/views.sql +6 -0
  21. data/lib/prick/state.rb +297 -95
  22. data/lib/prick/subcommand/prick-build.rb +26 -20
  23. data/lib/prick/subcommand/prick-clean.rb +5 -5
  24. data/lib/prick/subcommand/prick-create.rb +41 -6
  25. data/lib/prick/subcommand/prick-drop.rb +57 -14
  26. data/lib/prick/subcommand/prick-fox.rb +1 -1
  27. data/lib/prick/subcommand/prick-init.rb +25 -18
  28. data/lib/prick/subcommand/prick-list.rb +99 -0
  29. data/lib/prick/subcommand/prick-make.rb +8 -8
  30. data/lib/prick/subcommand/prick-migrate.rb +8 -7
  31. data/lib/prick/subcommand/prick-release.rb +4 -2
  32. data/lib/prick/subcommand/prick-set.rb +52 -0
  33. data/lib/prick/subcommand/prick-setup.rb +3 -13
  34. data/lib/prick/subcommand/prick-teardown.rb +13 -9
  35. data/lib/prick/subcommand/subcommand.rb +12 -0
  36. data/lib/prick/version.rb +1 -1
  37. data/lib/prick.rb +54 -15
  38. metadata +9 -5
  39. data/lib/prick/share/init/schema/prick/data.sql +0 -6
  40. 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
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
- # Prick executable search_path
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,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
- }).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
+ "#{prefix}#{var}=\"#{Array(val).join(' ')}\"\n"
214
+ }.join
88
215
  end
89
216
 
90
- def bash_source
91
- bash_environment(:local).map { |var,val| "export #{var}=\"#{Array(val).join(' ')}\"\n" }.join
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
- 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
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
- 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)
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
- 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
- )
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, :schema_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
- def load_yaml(file, expected_keys, optional_keys)
280
+ # YAML helper methods
281
+
282
+ def read_yaml(file)
147
283
  begin
148
- hash = YAML.load(File.read file)
284
+ YAML.load(File.read file)
149
285
  rescue Errno::ENOENT
150
- raise Prick::Error, "Can't read #{file}"
286
+ Prick.error "Can't read #{file}"
151
287
  end
152
- for key in expected_keys
153
- !hash[key].to_s.empty? or raise Prick::Error, "Can't find '#{key}' in #{file}"
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 - expected_keys - optional_keys).first) and
156
- Prick::Error "Illegal key '#{unknown}' in #{file}"
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 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"]
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 parse_project_file(prick_file)
168
- hash = load_yaml(prick_file, %w(name title), %w(version prick environments))
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
- Environment.load_environments(hash["environments"] || {})
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, 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?, 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 = 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,7 +86,7 @@ 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
91
  drop_all(from_db)
57
92
  drop_all(to_db)