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.
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/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,93 @@ 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. If the --force option is present, the
55
+ database is dropped even if it is not a Prick database
53
56
 
54
- init! -n,name=NAME -t,title=TITLE [DIRECTORY]
55
- Initializes a prick project
57
+ clean! -- [DATABASE] @ Empty database and remove users
58
+ Empty the database and drop related users except the database owner and
59
+ the prick schema. The public schema is recreated automatically
56
60
 
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'
61
+ get.database!
62
+ Print the name of the current database
63
63
 
64
- setup!
65
- Create the database user (if necessary) and an empty database
64
+ get.username!
65
+ Print the name of the current database owner
66
66
 
67
- teardown!
68
- Drop the database and the database user. TODO: Also run teardown scripts
67
+ get.environment!
68
+ Print the current environment
69
69
 
70
- clean!
71
- Empties the database and drops related users except the database user
70
+ set! -- VARIABLE [VALUE [OPT]] @ Switch database/environment
71
+ Switch database and/or environment or assign a value to the corresponding
72
+ database variable Currently only 'database', 'environment', and
73
+ 'duration' can be used as arguments. 'duration' not meant for the
74
+ end-user but for make scripts that enclose the prick command and that
75
+ would like to record the total time used. Prints the value of the
76
+ variable if VALUE is absent
72
77
 
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
78
+ create.database! -- [[OWNER@]DATABASE [ENVIRONMENT]] @ Create database
79
+ Create database and owner if needed. OWNER defaults to the database
80
+ name and DATABASE defaults to the project name. ENVIRONMENT defaults to
81
+ the current environment or to the prick default environment if there is
82
+ no current environment
79
83
 
80
- create.migration! -f,force -o,file=FILE -- VERSION
84
+ It is an error if the database exists but not if the owner exists. Note
85
+ that you can have multiple databases and switch between them using the
86
+ 'set database' subcommand
87
+
88
+ create.migration! -f,force -o,file=FILE -- VERSION @ Create migration
81
89
  Create a migration from VERSION to the current and write it to
82
90
  migration/VERSION. Fails if migration exist unless the --force flag is
83
91
  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
92
+ the migration directory
86
93
 
87
- drop! -- [KIND]
88
- @ Drop objects
94
+ create.users!
95
+ create.schema!
96
+ create.data!
97
+ create.all!
98
+ TODO
99
+
100
+ drop.users! -- [DATABASE]
101
+ @ Drop database users
102
+
103
+ Drops all database users except the database owner
104
+
105
+ drop.owner! -- [DATABASE]
106
+ Drop database owner
89
107
 
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
108
+ drop.database! -- [DATABASE]
109
+ Drop database
110
+
111
+ drop.schema! -- [DATABASE] [SCHEMA...]
112
+ @ Drop schemas
113
+
114
+ Drops the given schemas or all schemas if called without arguments. The
115
+ 'prick' schema is only deleted if named explicity
116
+
117
+ drop.data!
118
+ @ Drop data
119
+
120
+ TODO
93
121
 
94
122
  build! -f,force -t,time --dump=KIND? -- [SCHEMA]
95
123
  Build the project. If SCHEMA is defined, later schemas are excluded.
@@ -112,6 +140,9 @@ SPEC = %(
112
140
 
113
141
  # TODO: A --clean option that resets data
114
142
 
143
+ version!
144
+ Print project version
145
+
115
146
  release! -- KIND
116
147
  Create a release of the given kind. KIND can be 'major', 'minor', or
117
148
  'patch'. Release checks that the current repo is clean and up to date
@@ -120,146 +151,279 @@ SPEC = %(
120
151
  migrate! -f,file=FILE
121
152
  Execute a migration
122
153
 
123
- dump.type!
124
- dump.data!
125
- dump.schema!
126
- dump.database!
127
- dump.environment!
128
- TODO
154
+ list.environments! -l,long
155
+ List available environments (environments with a comment field)
156
+
157
+ list.databases! -l,long
158
+ List Prick databases. The --long option creates a table-like output
159
+
160
+ list.users!
161
+ List database users in the current environment
162
+
163
+ list.owners! -l,long
164
+ List database owners
165
+
166
+ list.variables! -l,long -a,all
167
+ List variables for the current environment
129
168
 
130
169
  dump.migration! --force -- VERSION
170
+
171
+ dump.type!
172
+ Dumps a PgGraph object
173
+
174
+ dump.variable! -x,export -l,local -- [VARIABLE...]
175
+ Dumps a bash source snippets that sets and optionally exports values from
176
+ the configuration, environment, and state. Environment is only dumped if
177
+ the current environment is defined in the state file. The state file may
178
+ be absent in which case the related variables are also left undefined
179
+
180
+ dump.value! -- VARIABLE...
181
+ Dumps values of the given variables. Multiple variables can be read into bash
182
+ variables using read:
183
+
184
+ read name title <<< $(prick dump value PRICK_NAME PRICK_TITLE)
185
+
186
+ dump.node!
187
+ Dump build nodes
188
+
189
+ dump.build!
190
+ dump.data! @ TODO
191
+ dump.schema! @ TODO
192
+ dump.database! @ TODO
193
+ TODO
131
194
  )
132
195
 
133
- opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
196
+ def require_db(database = Prick.state&.database, exist: true)
197
+ database or raise ArgumentError
198
+ dba = State.connection
199
+ if exist
200
+ dba.rdbms.exist?(database) or Prick.error "Can't find database '#{database}'"
201
+ if Prick.state&.connection&.name == database
202
+ close_conn = false
203
+ conn = Prick.state.conn
204
+ else
205
+ close_conn = true
206
+ conn = PgConn.new(database)
207
+ end
208
+ conn.schema.exist?("prick") or Prick.error "Database '#{database}' is not a Prick database"
209
+ conn.close if close_conn # Close session explicitly so we can drop database if required
210
+ else
211
+ !dba.rdbms.exist?(database) or Prick.error "Database '#{database}' exists"
212
+ end
213
+ end
214
+
215
+ def parse_database_args(state, args)
216
+ arg, environment = args.expect(0..2)
217
+ if arg
218
+ if arg =~ /^(.*)@(.*)$/
219
+ username, database = $1, $2
220
+ else
221
+ database = arg
222
+ username = database
223
+ end
224
+ else
225
+ database = state.name
226
+ username = database
227
+ end
228
+ database or Prick.error "Database is undefined"
229
+ [username, database, environment]
230
+ end
231
+
232
+ begin
233
+ # Parse command line
234
+ opts, args = ShellOpts.process(SPEC, ARGV, exception: true)
235
+
236
+ # Honor -C option
237
+ begin
238
+ Dir.chdir(opts.directory) if opts.directory?
239
+ rescue Errno::ENOENT
240
+ raise ShellOpts::Error, "Can't cd to '#{opts.directory}'"
241
+ end
242
+ rescue ShellOpts::Error => ex
243
+ ShellOpts.error(ex.message)
244
+ end
245
+
246
+ # Require prick only after -C directory option because some constants depends
247
+ # on the current directory
248
+ require_relative '../lib/prick.rb'
249
+ include Prick
134
250
 
135
251
  begin
136
252
  # Handle verbose and quiet
137
253
  $verbose = opts.verbose?
138
254
  $quiet = opts.quiet?
139
255
 
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
256
+ # Expect a sub-command. TODO: Make this a built-in in ShellOpts#subcommand!
257
+ cmd = opts.subcommand! or Prick.error "Subcomand expected"
152
258
 
153
- # Get subcommand
154
- cmd = opts.subcommand!
259
+ # Handle -p, -e, and -c. TODO: Take -C into account for relative paths. Low-hanging fruit
260
+ project_file = opts.project_file || PRICK_PROJECT_PATH
261
+ environment_file = opts.environment_file || PRICK_ENVIRONMENT_PATH
262
+ state_file = opts.state_file || PRICK_STATE_PATH
155
263
 
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
264
+ # Process init command and exit
265
+ if opts.subcommand == :init!
266
+ dir, name = Prick::SubCommand.init(project_file, args.expect(0..1), cmd.name, cmd.title)
267
+ Prick.mesg "Initialized Prick project '#{name}' in #{File.absolute_path(dir)}/"
164
268
  exit
165
269
  end
166
270
 
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
271
+ # Check for prick project
272
+ if ![:teardown!, :clean!].include?(opts.subcommand) || args.size == 0
273
+ # We test for the presense of the schema directory to verify we're in a
274
+ # prick directory. Checking for a configuration file doesn't work because
275
+ # they can be placed elsewhere. TODO: This require us to check for project file manually
276
+ # File.directory? "schema" or Prick.error "Not in a prick project directory"
277
+ File.directory?(SCHEMA_DIR) or Prick.error "Not in a prick project directory"
278
+ File.exist?(project_file) or Prick.error "Can't find project file '#{project_file}'"
279
+ end
173
280
 
174
281
  # 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
282
+ state = Prick.state = State.new(project_file, environment_file, state_file)
181
283
 
182
- # Expect a sub-command
183
- cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected"
284
+ # Lazyness
285
+ database = state.database
286
+ username = state.username
287
+ environment = state.environment
184
288
 
185
289
  # Process subcommands
186
290
  case opts.subcommand
187
291
  when :version!
188
292
  puts "#{Prick.state.name}-#{Prick.state.version}"
189
293
 
190
- when :env!
191
- env = args.expect(1)
192
- Prick.state.environment = env
193
- Prick.state.save
194
-
195
294
  when :setup!
196
- Prick::SubCommand.setup(database, username)
295
+ state.project_loaded? or Prick.error "No #{project_file} found"
296
+ username, database, environment = parse_database_args(state, args)
297
+ require_db(database, exist: false)
298
+ Prick::SubCommand.setup(database, username, environment)
197
299
 
300
+ # FIXME Ensure that we never drop the current user
198
301
  when :teardown!
199
- Prick::SubCommand.teardown(database, username)
302
+ if args.empty?
303
+ Prick::SubCommand.teardown(database, remove_state_file: true)
304
+ else
305
+ Prick::SubCommand.teardown(args)
306
+ end
307
+
308
+ when :get!
309
+ case cmd.subcommand
310
+ when :database!; puts database
311
+ when :username!; puts username
312
+ when :environment!; puts environment
313
+ else
314
+ raise ArgumentError
315
+ end
316
+
317
+ when :set!
318
+ variable, *args = args.expect(1..3)
319
+ require_db(database) if variable == "duration"
320
+ Prick::SubCommand.set(variable, *args) or error "Illegal variable name '#{variable}'"
200
321
 
201
322
  when :clean!
323
+ database = args.expect(0..1) || database
324
+ require_db(database)
202
325
  Prick::SubCommand.clean(database)
203
326
 
204
327
  when :create!
205
- create_command = opts.create!
328
+ create_command = opts.create! # Because we need easy access to subcommand options
206
329
  case create_command.subcommand
207
- when :migration
330
+ when :database!
331
+ database, username, environment = parse_database_args(state, args)
332
+ require_db(database, exist: false)
333
+ Prick::SubCommand.create_database(database, username, environment)
334
+ when :migration!
335
+ require_db
208
336
  arg = args.expect(1)
209
- version = PrickVersion.try(arg) or raise Prick::Error, "Illegal version: #{arg}"
337
+ version = PrickVersion.try(arg) or Prick.error "Illegal version: #{arg}"
210
338
  Prick::SubCommand.create_migration(
211
339
  username, version,
212
340
  force: create_command.subcommand!.force?,
213
341
  file: create_command.subcommand!.file)
342
+ when :users, :schema, :data, :all
343
+ raise NotImplementedError
214
344
  else
215
- raise NotImplementedError
345
+ raise ArgumentError
216
346
  end
217
347
 
218
348
  when :build!
349
+ require_db
219
350
  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
220
351
  # exclude = cmd.exclude? ? cmd.exclude.split(",") : []
221
352
  Prick::SubCommand.build(
222
353
  database, username, args.expect(0..1), force: cmd.force?, timer: cmd.time?, dump: dump)
223
354
 
224
355
  when :make!
356
+ require_db
225
357
  dump = cmd.dump? ? cmd.dump("batches")&.to_sym || :batches : nil
226
358
  Prick::SubCommand.make(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
227
359
 
228
360
  when :fox!
361
+ require_db
229
362
  Prick::SubCommand.fox(database, username, args)
230
363
 
231
364
  when :drop!
232
- case subject = args.expect(1).to_sym
233
- when :all
234
- Prick::SubCommand.drop_all(database)
235
- when :users
365
+ case cmd.subcommand
366
+ when :users!
367
+ # Should set state.username to owner of database
368
+ database = state.database = args.expect(0..1) || database
369
+ username = state.username = State.connection.rdbms.owner(database)
370
+ require_db(database)
236
371
  Prick::SubCommand.drop_users(database)
237
- when :user
238
- Prick::SubCommand.drop_user(username)
239
- when :database
372
+ when :owner!
373
+ owner = args.expect(0..1) || username
374
+ Prick::SubCommand.drop_owner(owner)
375
+ when :database!
376
+ database = args.expect(0..1) || database
240
377
  Prick::SubCommand.drop_database(database)
241
- when :data, :schema
378
+ when :data!
242
379
  raise NotImplementedError
380
+ when :schema!
381
+ require_db
382
+ schemas = args.to_a
383
+ Prick::SubCommand.drop_schema(database, schemas)
243
384
  else
244
- raise Prick::Error, "Unknown subject: #{subject}"
385
+ Prick.error "Unknown subject: #{subject}"
245
386
  end
246
387
 
247
388
  when :release!
248
389
  kind = args.expect(1).to_sym
249
390
  constrain? kind, :major, :minor, :patch or
250
- raise Prick::Fail, "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
391
+ Prick.failure "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
251
392
  Prick::SubCommand.release(kind)
252
393
 
253
394
  when :migrate!
395
+ require_db
254
396
  args.expect(0)
255
397
  Prick::SubCommand.migrate(database, username, file: cmd.file)
256
398
 
399
+ when :list!
400
+ format = (cmd.subcommand!.long? ? :long : :short) if cmd.subcommand && cmd.subcommand != :users!
401
+ case cmd.subcommand
402
+ when :environments!; Prick::SubCommand.list_environments(format: format)
403
+ when :databases!; Prick::SubCommand.list_databases(format: format)
404
+ when :variables!;
405
+ all = cmd.subcommand!.all?
406
+ Prick::SubCommand.list_variables(format: format, all: all)
407
+ when :users!; Prick::SubCommand.list_users
408
+ when :owners!; Prick::SubCommand.list_owners(format: format)
409
+ else
410
+ Prick.error "Unknown subcommand '#{cmd.subcommand || args.first }'"
411
+ end
412
+
257
413
  when :dump!
258
414
  subject = cmd.subcommand!
259
415
  case cmd.subcommand
416
+ when :node!
417
+ conn = PgConn.new(database, username)
418
+ builder = Prick::Build::Builder.new(conn, Prick::SCHEMA_DIR)
419
+ builder.root.dump
420
+ when :build!
421
+ conn = PgConn.new(database, username)
422
+ builder = Prick::Build::Builder.new(conn, Prick::SCHEMA_DIR)
423
+ # FIXME Nothing happens here
260
424
  when :migration!
261
425
  arg = args.expect(1)
262
- version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}"
426
+ version = PrickVersion.try(arg) or Prick.error "Illegal version number: #{arg}"
263
427
  Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout")
264
428
  when :type!
265
429
  conn = PgConn.new(database, username)
@@ -269,15 +433,24 @@ begin
269
433
  graph.dump
270
434
  when :data!, :schema!, :database!
271
435
  raise NotImplementedError
272
- when :environment!
273
- puts state.bash_source
436
+ when :variable!
437
+ scope =
438
+ case [subject.export?, subject.local?]
439
+ in [true, false]; :global
440
+ in [false, true]; :local
441
+ in [false, false]; nil
442
+ else
443
+ Prick.error "Conflicting options - --export and --local"
444
+ end
445
+ puts state.bash_source(args.first ? args : nil, scope: scope)
446
+ when :value!
447
+ puts args.expect(1..).map { |var| Array(state.bash_environment[var]).join(' ') }
274
448
  else
275
- raise Prick::Error, "Unknown dump object: #{cmd.subcommand!.__name__}"
449
+ object = args.extract(1) # Fails if object is absent
450
+ Prick.error "Unknown dump object: #{object}"
276
451
  end
277
-
278
-
279
- else
280
- raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}"
452
+ else
453
+ Prick.failure "Internal error: Unhandled command - #{opts.subcommand.inspect}"
281
454
  end
282
455
 
283
456
  rescue Prick::Build::PostgresError => ex
@@ -295,409 +468,11 @@ rescue Prick::Build::PostgresError => ex
295
468
  end
296
469
  exit 1
297
470
 
298
- rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Fail, Prick::Build::PostgresError => ex
299
- ShellOpts.failure(ex.message)
300
-
301
471
  rescue ShellOpts::Error, Prick::Error => ex
472
+ raise
302
473
  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
474
 
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)
475
+ rescue RuntimeError, IOError, ShellOpts::Failure, Prick::Failure, Prick::Build::PostgresError => ex
476
+ ShellOpts.failure(ex.message)
649
477
  end
650
478
 
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
- )