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/exe/prick CHANGED
@@ -10,16 +10,14 @@ begin
10
10
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
11
11
  compile_cache_yaml: true # Compile YAML into a cache
12
12
  )
13
- # Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
13
+ # Bootsnap.instrumentation = ->(event, path) { puts "#{event} #{path}" }
14
14
  end
15
15
 
16
16
  require 'shellopts'
17
- require_relative '../lib/prick.rb'
18
-
19
- include ShellOpts
20
- include Prick
21
17
 
22
- TIME = false
18
+ # Start time of prick. Used in make and build to compute duration of the whole
19
+ # build process
20
+ TIME = Time.now
23
21
 
24
22
  SPEC = %(
25
23
  @ Prick project command
@@ -33,63 +31,91 @@ SPEC = %(
33
31
  -C,directory=EDIR
34
32
  Change to directory DIR before doing anything else
35
33
 
36
- -d,database=DATABASE
37
- Override database name. Default is read from .prick.context
34
+ -p,project-file=PRICK-FILE
35
+ Override project file. Default is 'prick.yml'
38
36
 
39
- -U,username=USERNAME
40
- Override username. Default is read from .prick.context
37
+ -e,environment-file=ENVIRONMENT-FILE
38
+ Override the environment file. Default is 'prick.environment.yml'
41
39
 
42
- -e,environment=ENVIRONMENT
43
- Override environment. Default is read from from .prick.context
40
+ -s,state-file=STATE-FILE
41
+ Override state file. Default is '.prick-state.yml'
44
42
 
45
- -p,project=PRICK-FILE
46
- Override default environment file ('prick.yml')
43
+ init! -n,name=NAME -t,title=TITLE DIRECTORY #@ Initialize new Prick project directory
44
+ Initializes a prick project and checks it into git. If a directory is
45
+ given the directory will be created and the project initialized in it.
46
+ Default name and title is derived from the directory name
47
47
 
48
- -c,context=CONTEXT-FILE
49
- Override default context file ('.prick.context')
48
+ setup! -- [OWNER@]DATABASE [ENVIRONMENT] @ Create and set current database
49
+ Create database and owner and set the current database
50
50
 
51
- version!
52
- Print project version
51
+ teardown! -- [DATABASE...] @ Remove database and owner
52
+ Drop database, database users, and database owner if possible and not the
53
+ current user. DATABASE defaults to the current database in which case the
54
+ state file is also removed
53
55
 
54
- init! -n,name=NAME -t,title=TITLE [DIRECTORY]
55
- Initializes a prick project
56
+ clean! -- [DATABASE] @ Empty database and remove users
57
+ Empty the database and drop related users except the database owner and
58
+ the prick schema. The public schema is recreated automatically
56
59
 
57
- env! ENVIRONMENT
58
- Set build environment in the prick context file. The environment is
59
- accessible as $ENVIRONMENT in build.yml files and can be used to do
60
- conditional builds. There are no built-in values for the environment but
61
- 'production' and 'development' will be typical choices. Defaults to
62
- 'development'
60
+ get.database!
61
+ Print the name of the current database
63
62
 
64
- setup!
65
- Create the database user (if necessary) and an empty database
63
+ get.username!
64
+ Print the name of the current database owner
66
65
 
67
- teardown!
68
- Drop the database and the database user. TODO: Also run teardown scripts
66
+ get.environment!
67
+ Print the current environment
69
68
 
70
- clean!
71
- Empties the database and drops related users except the database user
69
+ set! -- VARIABLE [VALUE [OPT]] @ Switch database/environment
70
+ Switch database and/or environment or assign a value to the corresponding
71
+ database variable Currently only 'database', 'environment', and
72
+ 'duration' can be used as arguments. 'duration' not meant for the
73
+ end-user but for make scripts that enclose the prick command and that
74
+ would like to record the total time used. Prints the value of the
75
+ variable if VALUE is absent
72
76
 
73
- create.data!
74
- create.schema!
75
- create.database!
76
- create.users!
77
- create.all!
78
- Create an object. Fails if migration exist unless the --force flag is given
77
+ create.database! -- [[OWNER@]DATABASE [ENVIRONMENT]] @ Create database
78
+ Create database and owner if needed. OWNER defaults to the database
79
+ name and DATABASE defaults to the project name. ENVIRONMENT defaults to
80
+ the current environment or to the prick default environment if there is
81
+ no current environment
79
82
 
80
- create.migration! -f,force -o,file=FILE -- VERSION
83
+ It is an error if the database exists but not if the owner exists. Note
84
+ that you can have multiple databases and switch between them using the
85
+ 'set database' subcommand
86
+
87
+ create.migration! -f,force -o,file=FILE -- VERSION @ Create migration
81
88
  Create a migration from VERSION to the current and write it to
82
89
  migration/VERSION. Fails if migration exist unless the --force flag is
83
90
  given. If --file is given, the migration is written to FILE instead of
84
- the migration directory. This doesn't require you to be on a release
85
- branch and can be used to create ad-hoc migration scripts
91
+ the migration directory
92
+
93
+ create.users!
94
+ create.schema!
95
+ create.data!
96
+ create.all!
97
+ TODO
98
+
99
+ drop.schema! -- [SCHEMA...]
100
+ @ Drop schemas
101
+
102
+ Drops the given schemas or all schemas if called without arguments
103
+
104
+ drop.users!
105
+ @ Drop database users
106
+
107
+ Drops all database users except the database owner
108
+
109
+ drop.data!
110
+ @ Drop data
111
+
112
+ TODO
86
113
 
87
- drop! -- [KIND]
88
- @ Drop objects
114
+ drop.owner!
115
+ Drop database owner
89
116
 
90
- Kind can be 'users', 'data', 'schema', 'user', 'database' (the default),
91
- or 'all'. It is not an error if the object doesn't exist. TODO 'data' and
92
- 'schema' is not implemented
117
+ drop.database!
118
+ Drop database
93
119
 
94
120
  build! -f,force -t,time --dump=KIND? -- [SCHEMA]
95
121
  Build the project. If SCHEMA is defined, later schemas are excluded.
@@ -112,6 +138,9 @@ SPEC = %(
112
138
 
113
139
  # TODO: A --clean option that resets data
114
140
 
141
+ version!
142
+ Print project version
143
+
115
144
  release! -- KIND
116
145
  Create a release of the given kind. KIND can be 'major', 'minor', or
117
146
  'patch'. Release checks that the current repo is clean and up to date
@@ -120,146 +149,277 @@ SPEC = %(
120
149
  migrate! -f,file=FILE
121
150
  Execute a migration
122
151
 
123
- dump.type!
124
- dump.data!
125
- dump.schema!
126
- dump.database!
127
- dump.environment!
128
- TODO
152
+ list.environments! -l,long
153
+ List available environments (environments with a comment field)
154
+
155
+ list.databases! -l,long
156
+ List Prick databases. The --long option creates a table-like output
157
+
158
+ list.users!
159
+ List database users in the current environment
160
+
161
+ list.owners! -l,long
162
+ List database owners
163
+
164
+ list.variables! -l,long -a,all
165
+ List variables for the current environment
129
166
 
130
167
  dump.migration! --force -- VERSION
168
+
169
+ dump.type!
170
+ Dumps a PgGraph object
171
+
172
+ dump.variable! -x,export -l,local -- [VARIABLE...]
173
+ Dumps a bash source snippets that sets and optionally exports values from
174
+ the configuration, environment, and state. Environment is only dumped if
175
+ the current environment is defined in the state file. The state file may
176
+ be absent in which case the related variables are also left undefined
177
+
178
+ dump.value! -- VARIABLE...
179
+ Dumps values of the given variables. Multiple variables can be read into bash
180
+ variables using read:
181
+
182
+ read name title <<< $(prick dump value PRICK_NAME PRICK_TITLE)
183
+
184
+ dump.node!
185
+ dump.build!
186
+ dump.data! @ TODO
187
+ dump.schema! @ TODO
188
+ dump.database! @ TODO
189
+ TODO
131
190
  )
132
191
 
133
- opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
192
+ def require_db(database = Prick.state&.database, exist: true)
193
+ database or raise ArgumentError
194
+ dba = State.connection
195
+ if exist
196
+ dba.rdbms.exist?(database) or Prick.error "Can't find database '#{database}'"
197
+ if Prick.state&.conn&.name == database
198
+ close_conn = false
199
+ conn = Prick.state.conn
200
+ else
201
+ close_conn = true
202
+ conn = PgConn.new(database)
203
+ end
204
+ conn.schema.exist?("prick") or Prick.error "Database '#{database}' is not a Prick database"
205
+ conn.close if close_conn # Close session explicitly so we can drop database if required
206
+ else
207
+ !dba.rdbms.exist?(database) or Prick.error "Database '#{database}' exists"
208
+ end
209
+ end
210
+
211
+ def parse_database_args(state, args)
212
+ arg, environment = args.expect(0..2)
213
+ if arg
214
+ if arg =~ /^(.*)@(.*)$/
215
+ username, database = $1, $2
216
+ else
217
+ database = arg
218
+ username = database
219
+ end
220
+ else
221
+ database = state.name
222
+ username = database
223
+ end
224
+ database or Prick.error "Database is undefined"
225
+ [username, database, environment]
226
+ end
227
+
228
+ begin
229
+ # Parse command line
230
+ opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
231
+
232
+ # Honor -C option
233
+ begin
234
+ Dir.chdir(opts.directory) if opts.directory?
235
+ rescue Errno::ENOENT
236
+ raise ShellOpts::Error, "Can't cd to '#{opts.directory}'"
237
+ end
238
+ rescue ShellOpts::Error => ex
239
+ ShellOpts.error(ex.message)
240
+ end
241
+
242
+ # Require prick only after -C directory option because some constants depends
243
+ # on the current directory
244
+ require_relative '../lib/prick.rb'
245
+ include Prick
134
246
 
135
247
  begin
136
248
  # Handle verbose and quiet
137
249
  $verbose = opts.verbose?
138
250
  $quiet = opts.quiet?
139
251
 
140
- # Honor -C option
141
- if opts.directory?
142
- if File.exist?(opts.directory)
143
- begin
144
- Dir.chdir(opts.directory)
145
- rescue Errno::ENOENT
146
- raise Prick::Error, "Can't cd to '#{opts.directory}'"
147
- end
148
- else
149
- raise Prick::Error, "Can't find directory: #{opts.directory}"
150
- end
151
- end
252
+ # Expect a sub-command. TODO: Make this a built-in in ShellOpts#subcommand!
253
+ cmd = opts.subcommand! or Prick.error "Subcomand expected"
152
254
 
153
- # Get subcommand
154
- cmd = opts.subcommand!
255
+ # Handle -p, -e, and -c. TODO: Take -C into account for relative paths. Low-hanging fruit
256
+ project_file = opts.project_file || PRICK_PROJECT_PATH
257
+ environment_file = opts.environment_file || PRICK_ENVIRONMENT_PATH
258
+ state_file = opts.state_file || PRICK_STATE_PATH
155
259
 
156
- # Process init command
157
- if opts.subcommand == :init
158
- dir, state = Prick::SubCommand.init(args.expect(0..1), cmd.name, cmd.title, opts.database, opts.username)
159
- puts "Initialized prick project '#{state.name}' in #{dir}"
160
- if opts.database.nil? || opts.username.nil?
161
- puts
162
- puts "Please check database/username in #{PRICK_CONTEXT_FILE}"
163
- end
260
+ # Process init command and exit
261
+ if opts.subcommand == :init!
262
+ dir, name = Prick::SubCommand.init(project_file, args.expect(0..1), cmd.name, cmd.title)
263
+ Prick.mesg "Initialized Prick project '#{name}' in #{File.absolute_path(dir)}/"
164
264
  exit
165
265
  end
166
266
 
167
- # Check state
168
- File.exist?(PRICK_PROJECT_FILE) or raise Prick::Error, "Not in a prick project directory"
169
-
170
- # Handle -p and -c
171
- project_file = opts.project || PRICK_PROJECT_PATH
172
- context_file = opts.context || PRICK_CONTEXT_PATH
267
+ # Check for prick project
268
+ if ![:teardown!, :clean!].include?(opts.subcommand) || args.size == 0
269
+ # We test for the presense of the schema directory to verify we're in a
270
+ # prick directory. Checking for a configuration file doesn't work because
271
+ # they can be placed elsewhere. TODO: This require us to check for project file manually
272
+ # File.directory? "schema" or Prick.error "Not in a prick project directory"
273
+ File.directory?(SCHEMA_DIR) or Prick.error "Not in a prick project directory"
274
+ File.exist?(project_file) or Prick.error "Can't find project file '#{project_file}'"
275
+ end
173
276
 
174
277
  # Load state
175
- state = Prick.state = State.load(project_file, context_file)
176
-
177
- # Set database, username and environment
178
- database = state.database = opts.database || state.database
179
- username = state.username = opts.username || state.username
180
- environment = state.environment = opts.environment || state.environment
278
+ state = Prick.state = State.new(project_file, environment_file, state_file)
181
279
 
182
- # Expect a sub-command
183
- cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected"
280
+ # Lazyness
281
+ database = state.database
282
+ username = state.username
283
+ environment = state.environment
184
284
 
185
285
  # Process subcommands
186
286
  case opts.subcommand
187
287
  when :version!
188
288
  puts "#{Prick.state.name}-#{Prick.state.version}"
189
289
 
190
- when :env!
191
- env = args.expect(1)
192
- Prick.state.environment = env
193
- Prick.state.save
194
-
195
290
  when :setup!
196
- Prick::SubCommand.setup(database, username)
291
+ state.project_loaded? or Prick.error "No #{project_file} found"
292
+ username, database, environment = parse_database_args(state, args)
293
+ require_db(database, exist: false)
294
+ Prick::SubCommand.setup(database, username, environment)
197
295
 
296
+ # FIXME Ensure that we never drop the current user
198
297
  when :teardown!
199
- Prick::SubCommand.teardown(database, username)
298
+ if args.empty?
299
+ Prick::SubCommand.teardown(database, remove_state_file: true)
300
+ else
301
+ Prick::SubCommand.teardown(args)
302
+ end
303
+
304
+ when :get!
305
+ case cmd.subcommand
306
+ when :database!; puts database
307
+ when :username!; puts username
308
+ when :environment!; puts environment
309
+ else
310
+ raise ArgumentError
311
+ end
312
+
313
+ when :set!
314
+ variable, *args = args.expect(1..3)
315
+ require_db(database) if variable == "duration"
316
+ Prick::SubCommand.set(variable, *args) or error "Illegal variable name '#{variable}'"
200
317
 
201
318
  when :clean!
319
+ database = args.expect(0..1) || database
320
+ require_db(database)
202
321
  Prick::SubCommand.clean(database)
203
322
 
204
323
  when :create!
205
- create_command = opts.create!
324
+ create_command = opts.create! # Because we need easy access to subcommand options
206
325
  case create_command.subcommand
207
- when :migration
326
+ when :database!
327
+ database, username, environment = parse_database_args(state, args)
328
+ require_db(database, exist: false)
329
+ Prick::SubCommand.create_database(database, username, environment)
330
+ when :migration!
331
+ require_db
208
332
  arg = args.expect(1)
209
- version = PrickVersion.try(arg) or raise Prick::Error, "Illegal version: #{arg}"
333
+ version = PrickVersion.try(arg) or Prick.error "Illegal version: #{arg}"
210
334
  Prick::SubCommand.create_migration(
211
335
  username, version,
212
336
  force: create_command.subcommand!.force?,
213
337
  file: create_command.subcommand!.file)
338
+ when :users, :schema, :data, :all
339
+ raise NotImplementedError
214
340
  else
215
- raise NotImplementedError
341
+ raise ArgumentError
216
342
  end
217
343
 
218
344
  when :build!
345
+ require_db
219
346
  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
220
347
  # exclude = cmd.exclude? ? cmd.exclude.split(",") : []
221
348
  Prick::SubCommand.build(
222
349
  database, username, args.expect(0..1), force: cmd.force?, timer: cmd.time?, dump: dump)
223
350
 
224
351
  when :make!
352
+ require_db
225
353
  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
226
354
  Prick::SubCommand.make(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
227
355
 
228
356
  when :fox!
357
+ require_db
229
358
  Prick::SubCommand.fox(database, username, args)
230
359
 
231
360
  when :drop!
232
- case subject = args.expect(1).to_sym
233
- when :all
234
- Prick::SubCommand.drop_all(database)
235
- when :users
361
+ require_db if cmd.subcommand != :owner!
362
+ if cmd.subcommand == :schema!
363
+ schemas = args.to_a
364
+ else
365
+ args.expect(0)
366
+ end
367
+ case cmd.subcommand
368
+ when :all!
369
+ Prick::SubCommand.drop_all(database, username)
370
+ when :users!
236
371
  Prick::SubCommand.drop_users(database)
237
- when :user
238
- Prick::SubCommand.drop_user(username)
239
- when :database
372
+ when :owner!
373
+ Prick::SubCommand.drop_owner(username)
374
+ when :database!
240
375
  Prick::SubCommand.drop_database(database)
241
- when :data, :schema
376
+ when :data!
242
377
  raise NotImplementedError
378
+ when :schema!
379
+ Prick::SubCommand.drop_schema(database, schemas)
243
380
  else
244
- raise Prick::Error, "Unknown subject: #{subject}"
381
+ Prick.error "Unknown subject: #{subject}"
245
382
  end
246
383
 
247
384
  when :release!
248
385
  kind = args.expect(1).to_sym
249
386
  constrain? kind, :major, :minor, :patch or
250
- raise Prick::Fail, "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
387
+ Prick.failure "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
251
388
  Prick::SubCommand.release(kind)
252
389
 
253
390
  when :migrate!
391
+ require_db
254
392
  args.expect(0)
255
393
  Prick::SubCommand.migrate(database, username, file: cmd.file)
256
394
 
395
+ when :list!
396
+ format = (cmd.subcommand!.long? ? :long : :short) if cmd.subcommand != :users!
397
+ case cmd.subcommand
398
+ when :environments!; Prick::SubCommand.list_environments(format: format)
399
+ when :databases!; Prick::SubCommand.list_databases(format: format)
400
+ when :variables!;
401
+ all = cmd.subcommand!.all?
402
+ Prick::SubCommand.list_variables(format: format, all: all)
403
+ when :users!; Prick::SubCommand.list_users
404
+ when :owners!; Prick::SubCommand.list_owners(format: format)
405
+ else
406
+ Prick.error "Unknown subcommand: #{cmd.subcommand}"
407
+ end
408
+
257
409
  when :dump!
258
410
  subject = cmd.subcommand!
259
411
  case cmd.subcommand
412
+ when :node!
413
+ conn = PgConn.new(database, username)
414
+ builder = Prick::Build::Builder.new(conn, Prick::SCHEMA_DIR)
415
+ builder.root.dump
416
+ when :build!
417
+ conn = PgConn.new(database, username)
418
+ builder = Prick::Build::Builder.new(conn, Prick::SCHEMA_DIR)
419
+ # FIXME Nothing happens here
260
420
  when :migration!
261
421
  arg = args.expect(1)
262
- version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}"
422
+ version = PrickVersion.try(arg) or Prick.error "Illegal version number: #{arg}"
263
423
  Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout")
264
424
  when :type!
265
425
  conn = PgConn.new(database, username)
@@ -269,15 +429,25 @@ begin
269
429
  graph.dump
270
430
  when :data!, :schema!, :database!
271
431
  raise NotImplementedError
272
- when :environment!
273
- puts state.bash_source
432
+ when :variable!
433
+ scope =
434
+ case [subject.export?, subject.local?]
435
+ in [true, false]; :global
436
+ in [false, true]; :local
437
+ in [false, false]; nil
438
+ else
439
+ Prick.error "Conflicting options - --export and --local"
440
+ end
441
+ puts state.bash_source(args.first ? args : nil, scope: scope)
442
+ exit
443
+ when :value!
444
+ puts args.expect(1..).map { |var| Array(state.bash_environment[var]).join(' ') }
274
445
  else
275
- raise Prick::Error, "Unknown dump object: #{cmd.subcommand!.__name__}"
446
+ object = args.extract(1) # Fails if object is absent
447
+ Prick.error "Unknown dump object: #{object}"
276
448
  end
277
-
278
-
279
449
  else
280
- raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}"
450
+ Prick.failure "Internal error: Unhandled command - #{opts.subcommand.inspect}"
281
451
  end
282
452
 
283
453
  rescue Prick::Build::PostgresError => ex
@@ -295,409 +465,11 @@ rescue Prick::Build::PostgresError => ex
295
465
  end
296
466
  exit 1
297
467
 
298
- rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Fail, Prick::Build::PostgresError => ex
299
- ShellOpts.failure(ex.message)
300
-
301
468
  rescue ShellOpts::Error, Prick::Error => ex
469
+ raise
302
470
  ShellOpts.error(ex.message)
303
- end
304
-
305
-
306
- __END__
307
-
308
- -n,name=NAME
309
- Name of project. Defauls to the environment variable `PRICK_PROJECT` if
310
- set and else the name of the current directory
311
-
312
- init! -u,user=USER [NAME]
313
- Initialize a project in the given directory. The USER is the postgres
314
- user and defaults to the project name
315
-
316
- info!
317
- Print project information
318
-
319
- list.releases! -m,migrations -c,cancelled
320
- List releases. Include migration releases if the --migration option is
321
- present and also include cancelled releases if the --cancelled option is
322
- present
323
-
324
- list.migrations!
325
- List migrations
326
-
327
- list.upgrades! [FROM [TO]]
328
- List available upgrades
329
-
330
- list.cache!
331
- List cache files
332
-
333
- build! -d,database=DATABASE -s,state=FILE -C,no-cache [TAG]
334
- Drop all users associated with the database before building the current
335
- database from the content in the schemas/ directory. With a tag the
336
- version is built into the associated versioned database and the result is
337
- saved to cache unless the -C option is given. The -d option overrides the
338
- default database and the -s option overrides the default state file
339
- (fox.state)
340
-
341
- make! -d,database=DATABASE -C,no-cache [TAG]
342
- Build the current database from the content in the schemas/ directory.
343
- With a tag the associated versioned database is loaded from cache if
344
- present. The -C option ignores the cache and the -d option overrides
345
- the default database
346
-
347
- make.clean! -a,all
348
- Drop versioned databases and remove cached and other temporary files.
349
- Also drop the project database if the -a option is given
350
-
351
- load! -d,database=DATABASE VERSION|FILE
352
- Load the cached version or the file into the associated versioned
353
- database. It is an error if the version hasn't been cached. The --database
354
- argument overrides the database
355
-
356
- save! VERSION [FILE]
357
- Save the versioned database associated with version to the cache or the
358
- given file
359
-
360
- drop! -a,all [DATABASE]
361
- Drop the given database or all versioned databases. Users with a username
362
- on the form <database>__<username> are also dropped. The --all option
363
- also drops the project database
364
-
365
- drop.users! [DATABASE]
366
- Drop users with a username on the form <database>__<username>
367
-
368
- diff! -m,mark -t,tables -T,notables
369
- diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
370
- Create a schema diff between the given databases or versions. Default
371
- to-version is the current schema and default from-version is the base
372
- version of this branch/tag
373
-
374
- migrate!
375
- Not yet implemented
376
-
377
- prepare!
378
- Prepare a release. Just a shorthand for 'prick prepare release'
379
-
380
- prepare.release! [FORK]
381
- Populate the current migration directory with migration files
382
-
383
- prepare.feature! NAME
384
- Create and populate a feature as a subdirectory of the current directory.
385
- Also prepares the current release directory
386
471
 
387
- prepare.migration! [FROM]
388
- Create and populate a migration directory
389
-
390
- prepare.schema! NAME
391
- Create and populate a new schema directory. Existing files and
392
- directories are kept
393
-
394
- prepare.diff! [VERSION]
395
- Not yet implemented
396
-
397
- include.feature! FEATURE
398
- Include the given feature in the current pre-release
399
-
400
- check!
401
- Check that the current migration applied to the base version yields the
402
- same result as loading the current schema
403
-
404
- create.release! [RELEASE]
405
- Prepare a release and create release directory and migration file before
406
- tagging and branching to a release branch. The RELEASE argument can be
407
- left out if the current branch is a prerelease branch
408
-
409
- create.prerelease! RELEASE
410
- Prepare a release and create release directory and migration file before
411
- branching to a prerelease branch
412
-
413
- create.feature! NAME
414
- Prepare a feature before branching to a feature branch
415
-
416
- cancel!
417
- Cancel a release. Just a shorthand for 'prick cancel release'
418
-
419
- cancel.release!
420
- Cancel a release. Since tags are immutable, the release is cancelled by
421
- added a special cancel-tag to the release that makes prick ignore it
422
-
423
- generate.migration!
424
- Create a script to migrate the database
425
-
426
- generate.schema!
427
- Create a script to create the database
428
-
429
- upgrade!
430
- Migrate the database to match the current schema
431
-
432
- backup! [FILE]
433
- Saves a backup of the database to the given file or to the var/spool
434
- directory
435
-
436
- restore! [FILE]
437
- Restore the database from the given backup file or from the latest backup
438
- in the var/spool directory
439
- )
440
-
441
- __END__
442
-
443
-
444
-
445
-
446
-
447
-
448
- DEFAULT_STATE_FILE = "fox.state"
449
-
450
- opts, args = ShellOpts.process(SPEC, ARGV)
451
-
452
- # Handle --help
453
- if opts.help?
454
- ShellOpts.help
455
- exit
456
- end
457
-
458
- # Handle --version
459
- if opts.version?
460
- puts "prick-#{VERSION}"
461
- exit
462
- end
463
-
464
- begin
465
- # Honor -C option
466
- if opts.directory?
467
- if File.exist?(opts.directory)
468
- begin
469
- Dir.chdir(opts.directory)
470
- rescue Errno::ENOENT
471
- raise Prick::Error, "Can't cd to '#{opts.directory}'"
472
- end
473
- else
474
- raise Prick::Error, "Can't find directory: #{opts.directory}"
475
- end
476
- end
477
-
478
- # Create program object
479
- program = Program.new(quiet: opts.quiet?, verbose: opts.verbose?)
480
- $verbose = opts.verbose? ? opts.verbose : nil
481
-
482
- # Handle init command
483
- if opts.subcommand == :init
484
- directory = args.expect(0..1)
485
- name = opts.name || (directory && File.basename(directory)) || File.basename(Dir.getwd)
486
- user = opts.init!.user || name
487
- program.init(name, user, directory || ".")
488
- exit 0
489
- end
490
-
491
- # Change to parent directory containing the Prick version file if not found
492
- # in the current directory
493
- program.current_directory = Dir.getwd
494
- while Dir.getwd != "/" && !File.exist?(PRICK_VERSION_FILE)
495
- Dir.chdir("..")
496
- end
497
-
498
- # Check prick version
499
- file = PrickVersion.new
500
- file.exist? or raise Prick::Error, "Can't find prick version file '#{file.path}'"
501
- VERSION == file.read.to_s or
502
- raise Prick::Fail, ".prick-version required prick-#{file.read} but you're using prick-#{VERSION}"
503
-
504
- # TODO: Check for dirty detached head
505
-
506
- # Expect a sub-command
507
- opts.subcommand or raise Prick::Error, "Subcomand expected"
508
-
509
- case opts.subcommand
510
- when :info
511
- args.expect(0)
512
- program.info
513
-
514
- when :list
515
- command = opts.list!
516
- case command.subcommand
517
- when :releases;
518
- obj = command.releases!
519
- program.list_releases(migrations: obj.migrations?, cancelled: obj.cancelled?)
520
- when :migrations; program.list_migrations
521
- when :upgrades; program.list_upgrades(*args.expect(0..2).compact)
522
- when :cache;
523
- args.expect(0)
524
- program.list_cache
525
- when NilClass; raise Prick::Error, "list requires a releases|migrations|upgrades sub-command"
526
- else
527
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{command.subcommand} is not matched"
528
- end
529
-
530
- when :build
531
- version = args.expect(0..1)
532
- state_file = File.expand_path(opts.build!.state || DEFAULT_STATE_FILE)
533
- FileUtils.rm_f(state_file)
534
- program.build(opts.build!.database, version, state_file, opts.build!.no_cache?)
535
-
536
- when :make
537
- command = opts.make!
538
- case command.subcommand
539
- when :clean
540
- args.expect(0)
541
- program.make_clean(command.clean!.all?)
542
- else
543
- version = args.expect(0..1)
544
- program.make(opts.make!.database, version, opts.make!.no_cache?)
545
- end
546
-
547
- when :load
548
- version_or_file = args.expect(1)
549
- program.load(opts.load!.database, version_or_file)
550
-
551
- when :save
552
- version, file = args.expect(1..2)
553
- program.save(version, file)
554
-
555
- when :drop
556
- command = opts.drop!
557
- case command.subcommand
558
- when :users
559
- database = args.extract(0..1) || program.project.database.name
560
- args.expect(0)
561
- program.drop_users(database)
562
- else
563
- program.drop(args.expect(0..1), opts.drop!.all?)
564
- end
565
-
566
- when :diff
567
- mark = opts.diff!.mark
568
- tables = opts.diff!.tables
569
- no_tables = opts.diff!.notables
570
- tables.nil? && no_tables.nil? || tables ^ no_tables or
571
- raise Error, "--tables and --no-tables options are exclusive"
572
- select = tables ? :tables : (no_tables ? :no_tables : :all)
573
- from, to = args.expect(0..2)
574
- program.diff(from, to, mark, select)
575
-
576
- when :migrate
577
- raise NotYet
578
-
579
- when :prepare
580
- cmd = opts.prepare!.subcommand || :release
581
- case cmd
582
- when :release; program.prepare_release(args.expect(0..1))
583
- when :feature; program.prepare_feature(args.expect(1))
584
- when :migration; program.prepare_migration(args.expect(0..1))
585
- when :schema; program.prepare_schema(args.expect(1))
586
- when :diff; program.prepare_diff(args.expect(0..1))
587
- else
588
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
589
- end
590
-
591
- when :include
592
- cmd = opts.include!.subcommand || :feature
593
- case cmd
594
- when :feature; program.include_feature(args.expect(1))
595
- else
596
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
597
- end
598
-
599
- when :check
600
- args.expect(0)
601
- program.check
602
-
603
- when :create
604
- cmd = opts.create!.subcommand || :release
605
- case cmd
606
- when :release; program.create_release(args.expect(0..1))
607
- when :prerelease; program.create_prerelease(args.expect(0..1))
608
- when :feature; program.create_feature(args.expect(1))
609
- else
610
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
611
- end
612
-
613
- when :cancel
614
- cmd = opts.cancel!.subcommand
615
- case cmd
616
- when :release; program.cancel_release(args.expect(1))
617
- when nil; raise Prick::Error, "'cancel' subcommand requires a release argument"
618
- else
619
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
620
- end
621
-
622
- when :generate
623
- cmd = opts.generate!.subcommand
624
- case cmd
625
- when :schema; program.generate_schema
626
- when :migration; program.generate_migration
627
- when nil; raise Prick::Error, "'generate' subcommand requires a 'schema' or 'migration' argument"
628
- else
629
- raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
630
- end
631
-
632
- when :upgrade
633
- args.expect(0)
634
- program.upgrade
635
-
636
- when :backup
637
- program.backup(args.expect(0..1))
638
-
639
- when :restore
640
- program.restore(args.expect(0..1))
641
- else
642
- raise Prick::Internal, "Subcommand #{opts.subcommand} is not matched"
643
- end
644
-
645
- rescue Prick::Fail => ex # Handling of Fail has to come first because Fail < Error
646
- ShellOpts.fail(ex.message)
647
- rescue Prick::Error => ex
648
- ShellOpts.error(ex.message)
472
+ rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Failure, Prick::Build::PostgresError => ex
473
+ ShellOpts.failure(ex.message)
649
474
  end
650
475
 
651
- __END__
652
-
653
- # Awaits support for sections in ShellOpts
654
- HELP = %(
655
- OPTIONS
656
- -n, --name=NAME
657
- -C, --directory=DIR
658
- -h, --help
659
- -v, --verbose
660
- --version
661
-
662
- COMMANDS
663
- INITIALIZATION
664
- init --user=USER [DIR]
665
-
666
- INFO COMMANDS
667
- info
668
- list releases --migrations --cancelled
669
- list migrations
670
- list upgrades --all
671
-
672
- BUILDING
673
- build -d DATABASE -C --nocache [TAG]
674
- make -d DATABASE -C --nocache [TAG]
675
- make clean -a
676
- load -d DATABASE VERSION|FILE
677
- save VERSION [FILE]
678
- drop --all [DATABASE]
679
- diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
680
- migrate
681
-
682
- PREPARING RELEASES
683
- prepare release [FORK]
684
- prepare feature NAME
685
- prepare migration FROM
686
- prepare schema NAME
687
- prepare diff [VERSION]
688
- include feature FEATURE
689
- check
690
-
691
- CREATING RELEASES
692
- create release [RELEASE]
693
- create prerelease RELEASE
694
- create feature NAME
695
- cancel release RELEASE
696
-
697
- DEPLOYING RELEASES
698
- generate migration
699
- generate schema
700
- upgrade
701
- backup [FILE]
702
- restore [FILE]
703
- )