prick 0.29.2 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/TODO +2 -0
  3. data/exe/prick +294 -519
  4. data/idea.txt +38 -0
  5. data/lib/prick/builder/batch.rb +1 -4
  6. data/lib/prick/builder/builder.rb +15 -9
  7. data/lib/prick/builder/node.rb +3 -2
  8. data/lib/prick/builder/node_pool.rb +3 -1
  9. data/lib/prick/builder/parser.rb +2 -1
  10. data/lib/prick/constants.rb +12 -12
  11. data/lib/prick/diff.rb +2 -2
  12. data/lib/prick/environment.rb +30 -12
  13. data/lib/prick/local/command.rb +8 -2
  14. data/lib/prick/local/fmt.rb +56 -0
  15. data/lib/prick/prick_version.rb +1 -1
  16. data/lib/prick/share/init/{.gitignore → dot.gitignore} +1 -1
  17. data/lib/prick/share/init/prick.environment.yml +16 -0
  18. data/lib/prick/share/init/prick.yml +3 -3
  19. data/lib/prick/share/init/schema/prick/build.yml +11 -2
  20. data/lib/prick/share/init/schema/prick/tables.sql +30 -9
  21. data/lib/prick/share/init/schema/prick/views.sql +6 -0
  22. data/lib/prick/state.rb +304 -95
  23. data/lib/prick/subcommand/prick-build.rb +26 -20
  24. data/lib/prick/subcommand/prick-clean.rb +5 -5
  25. data/lib/prick/subcommand/prick-create.rb +42 -7
  26. data/lib/prick/subcommand/prick-drop.rb +41 -14
  27. data/lib/prick/subcommand/prick-fox.rb +1 -1
  28. data/lib/prick/subcommand/prick-init.rb +25 -18
  29. data/lib/prick/subcommand/prick-list.rb +99 -0
  30. data/lib/prick/subcommand/prick-make.rb +8 -8
  31. data/lib/prick/subcommand/prick-migrate.rb +8 -7
  32. data/lib/prick/subcommand/prick-release.rb +4 -2
  33. data/lib/prick/subcommand/prick-set.rb +52 -0
  34. data/lib/prick/subcommand/prick-setup.rb +3 -13
  35. data/lib/prick/subcommand/prick-teardown.rb +13 -9
  36. data/lib/prick/subcommand/subcommand.rb +12 -0
  37. data/lib/prick/version.rb +1 -1
  38. data/lib/prick.rb +54 -15
  39. metadata +14 -9
  40. data/lib/prick/share/init/schema/prick/data.sql +0 -6
  41. data/prick.environments.yml +0 -14
data/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
- )